Flux Git îmbunătățit explicat
Publicat: 2022-03-11A provoca daune din greșeală cu Git poate fi prea ușor. Cu toate acestea, cel mai bun mod de a folosi Git va fi întotdeauna controversat.
Acest lucru se datorează faptului că Git în sine detaliază doar operațiunile de bază de ramificare, ceea ce lasă modelele sale de utilizare - adică modelele de ramificare - o chestiune de părere a utilizatorului. Modelele de ramificare Git promit să ușureze durerea prin organizarea haosului care apare în mod inevitabil pe măsură ce dezvoltatorii de software își modifică bazele de cod.
La fel ca mulți dezvoltatori, v-ați dorit ceva care să „funcționeze”, astfel încât să puteți continua cu dezvoltarea software-ului real. Deci ați luat Git flow , un model de ramificare adesea recomandat utilizatorilor Git. Poate că ați fost de acord cu logica Git flow la început, până când ați găsit niște probleme cu ea în practică. Sau poate Git flow nu pare a fi suficient de potrivit pentru a-l adopta. La urma urmei, există nenumărate variabile în joc și niciun model de ramificare nu va funcționa bine în orice situație.
Vești bune! O variantă a modelului clasic de flux Git, fluxul Git îmbunătățit simplifică manevrele mai frecvente ale fluxului de lucru Git, păstrând în același timp principalele avantaje.
Splendoarea și mizeria modelului clasic Git Flow
Am fost un susținător puternic al fluxului Git de când am descoperit cum excelează atunci când dezvoltă un produs care evoluează în incremente semnificative de valoare (cu alte cuvinte, lansări ).
O creștere semnificativă a valorii necesită o perioadă semnificativă de timp pentru a fi finalizată, cum ar fi sprinturile de peste două săptămâni utilizate de obicei în dezvoltarea bazată pe Scrum. Dacă o echipă de dezvoltare a fost deja implementată în producție, pot apărea probleme dacă domeniul de aplicare al următoarei ediții se acumulează în același loc în care se află codul de producție, de exemplu, în ramura principală a depozitului Git pe care o utilizează.
În timp ce produsul se află încă în faza inițială de dezvoltare, adică nu există producție și nu există utilizatori reali ai produsului, este în regulă ca echipa să păstreze pur și simplu totul în ramura principală. De fapt, este mai mult decât în regulă: această strategie permite cel mai rapid ritm de dezvoltare fără prea multă ceremonie. Dar lucrurile se schimbă într-un mediu de producție; apoi, oamenii adevărați încep să se bazeze pe produs pentru a fi stabil.
De exemplu, dacă există o eroare critică în producție care trebuie remediată imediat, ar fi un dezastru major pentru echipa de dezvoltare să fie nevoită să anuleze toată munca acumulată până acum în ramura principală doar pentru a implementa remedierea. Iar implementarea codului fără testarea adecvată – indiferent dacă codul este considerat pe jumătate pregătit sau bine dezvoltat – în mod clar nu este o opțiune.
Acolo strălucesc modelele de ramificare, inclusiv fluxul Git. Orice model de ramificare sofisticat ar trebui să răspundă la întrebări referitoare la cum să izolați următoarea ediție de versiunea sistemului utilizată în prezent de oameni, cum să actualizați acea versiune cu următoarea ediție și cum să introduceți remedieri rapide ale oricăror erori critice în versiunea curentă.
Procesul de flux Git abordează aceste scenarii fundamentale, separând „principal” (ramura de producție sau „versiunea curentă”) și „dezvoltare” (ramura de dezvoltare sau „următoarea lansare”) și oferind toate regulile despre utilizarea ramurilor de caracteristici/lansări/remedieri rapide. . Rezolvă eficient multe dureri de cap din fluxurile de lucru de dezvoltare a produselor bazate pe lansare.
Dar chiar și cu proiecte potrivite modelului clasic de flux Git, am suferit problemele tipice pe care le poate aduce:
- Fluxul Git este complex, cu două ramuri de lungă durată, trei tipuri de ramuri temporare și reguli stricte privind modul în care ramurile se ocupă între ele. O astfel de complexitate face greșelile mai probabile și crește efortul necesar pentru a le remedia.
- Ramurile de lansare și remediere rapidă necesită „dubla îmbinare” - o dată în principal, apoi în dezvoltare. Uneori poți uita să le faci pe amândouă. Puteți face ramificarea fluxului Git mai ușoară cu scripturi sau pluginuri pentru client GUI VCS, dar trebuie să le configurați mai întâi pentru fiecare mașină a fiecărui dezvoltator implicat într-un anumit proiect.
- În fluxurile de lucru CI/CD, de obicei ajungeți cu două versiuni finale pentru o ediție – una de la cea mai recentă comitere a ramului de lansare în sine și alta de la comiterea de îmbinare la principal. Strict vorbind, ar trebui să îl folosiți pe cel din principal, dar cele două sunt de obicei identice, creând potențialul de confuzie.
Introduceți „Flux Git îmbunătățit”
Prima dată când am folosit fluxul Git îmbunătățit a fost într-un proiect cu sursă închisă. Lucram cu un alt dezvoltator și lucram la proiect prin angajarea directă în filiala principală.
Notă: Până la prima lansare publică a unui produs, este absolut logic să efectuați toate modificările direct în ramura principală – chiar dacă sunteți un susținător al fluxului Git – de dragul vitezei și simplității fluxului de lucru de dezvoltare. Deoarece nu există încă producție, nu există posibilitatea ca echipa să o remedieze cât mai curând posibil. A face toată magia de ramificare pe care o implică fluxul Git clasic este, prin urmare, exagerat în această etapă.
Apoi ne-am apropiat de lansarea inițială și am convenit că, dincolo de acel punct, nu ne vom mai simți confortabil să ne angajăm direct în ramura principală. Ne-am mutat destul de rapid, iar prioritățile de afaceri nu au lăsat prea mult loc pentru a stabili un proces de dezvoltare solid, adică unul cu suficientă testare automată pentru a ne oferi încrederea că filiala noastră principală era într-o stare pregătită pentru lansare.
Părea a fi un caz valid pentru modelul clasic de flux Git. Cu ramuri principale și de dezvoltare separate și suficient timp între creșterile semnificative ale valorii, a existat încredere că, în cea mai mare parte, QA manuală va produce rezultate suficient de bune. Când am pledat pentru fluxul Git, colegul meu a sugerat ceva similar, dar cu unele diferențe cheie.
La început, m-am împins înapoi. Mi s-a părut că unele dintre „patch-urile” propuse pentru fluxul Git clasic erau puțin prea revoluționare. M-am gândit că s-ar putea rupe ideea principală, iar întreaga abordare va fi scurtă. Dar, gândindu-mă mai mult, mi-am dat seama că aceste ajustări nu întrerup de fapt fluxul Git. Între timp, îl fac un model de ramificare Git mai bun, rezolvând toate punctele dureroase menționate mai sus.
După succesul cu abordarea modificată în acel proiect, l-am folosit într-un alt proiect cu sursă închisă cu o echipă mică în spate, unde eram proprietarul permanent al bazei de cod și un dezvoltator externalizat sau doi ajutat din când în când. În acest proiect, am intrat în producție în șase luni și, de atunci, am folosit teste CI și E2E de peste un an, cu lansări în fiecare lună.
Experiența mea generală cu această nouă abordare de ramificare a fost atât de pozitivă încât am vrut să o împărtășesc cu colegii mei dezvoltatori pentru a-i ajuta să ocolească dezavantajele fluxului Git clasic.
Asemănări cu Classic Git Flow: Development Isolation
Pentru izolarea muncii în flux Git îmbunătățit, există încă două ramuri cu viață lungă, principală și dezvoltată. (Utilizatorii au încă capabilități de remediere rapidă și de lansare, cu accent pe „capacități”, deoarece acestea nu mai sunt ramuri. Vom intra în detalii în secțiunea diferențe.)
Nu există nicio schemă oficială de denumire pentru ramurile caracteristice clasice de flux Git. Pur și simplu vă ramificați de la dezvoltare și vă îmbinați din nou pentru a dezvolta când caracteristica este gata. Echipele pot folosi orice convenție de denumire pe care doresc sau pur și simplu speră că dezvoltatorii vor folosi nume mai descriptive decât „filiala mea”. Același lucru este valabil și pentru fluxul Git îmbunătățit.
Toate caracteristicile acumulate în ramura de dezvoltare până la un anumit punct limită vor modela noua lansare.
Squash Merges
Am recomandat cu tărie utilizarea squash merge pentru ramurile caracteristice pentru a menține istoria plăcută și liniară de cele mai multe ori. Fără el, graficele de comitere (din instrumentele GUI sau git log --graph
) încep să arate neglijent atunci când o echipă jonglează chiar și cu câteva ramuri caracteristice:
Dar chiar dacă sunteți de acord cu imaginile din acest scenariu, există un alt motiv pentru a squash. Fără a zdrobi, comite vizualizări ale istoricului - printre ele atât git log
simplu (fără --graph
) cât și GitHub - spun povești destul de incoerente chiar și cu cele mai simple scenarii de îmbinare:
Avertismentul la utilizarea îmbinării squash este că istoricul original al ramurilor caracteristicilor se pierde. Dar această avertizare nici măcar nu se aplică dacă utilizați GitHub, de exemplu, care expune istoricul complet original al unei ramuri de caracteristică prin cererea de extragere care a fost fuzionată squash, chiar și după ce ramura caracteristică în sine este ștearsă.
Diferențele față de Classic Git Flow: versiuni și remedieri rapide
Să trecem prin ciclul de lansare, deoarece (sperăm) că acesta este principalul lucru pe care îl vei face. Când ajungem în punctul în care am dori să eliberăm ceea ce s-a acumulat în dezvoltare, este strict un supraset de main. După aceea, încep cele mai mari diferențe între fluxul Git clasic și îmbunătățit.

Lansări în Enhanced Git Flow
Fiecare pas al realizării unei ediții cu flux Git îmbunătățit diferă de procesul clasic de flux Git:
- Lansările se bazează pe principal, mai degrabă decât pe dezvoltare. Etichetează vârful actual al ramurii principale cu ceva semnificativ. Am adoptat etichete bazate pe data curentă în format ISO 8601 prefixate cu un „v” — de exemplu, v2020-09-09 .
- Dacă s-a întâmplat să existe mai multe versiuni într-o zi - de exemplu, remedieri rapide - formatul ar putea avea un număr secvenţial sau o literă lipită după cum este necesar.
- Rețineți că etichetele nu corespund, în general, cu datele de lansare. Ei trebuie doar să forțeze Git să păstreze o referință la modul în care arăta ramura principală la momentul în care a început următorul proces de lansare.
- Împingeți eticheta folosind
git push origin <the new tag name>
. - După aceea, o mică surpriză: ștergeți filiala principală locală . Nu vă faceți griji, pentru că îl vom restaura în curând.
- Toate commit-urile către main sunt încă în siguranță – le-am protejat de colectarea gunoiului etichetând main la pasul anterior. Fiecare dintre aceste comiteri – chiar și remedierile rapide, după cum vom trata în curând – face parte, de asemenea, din dezvoltare.
- Asigurați-vă că doar o singură persoană dintr-o echipă face acest lucru pentru orice versiune dată; acesta este așa-numitul rol de „manager de lansare”. Un manager de lansare este de obicei cel mai experimentat și/sau cel mai înalt membru al echipei, dar o echipă ar fi înțelept să evite ca un anumit membru al echipei să preia permanent acest rol. Este mai logic să răspândești cunoștințele în echipă pentru a crește factorul infam de autobuz.
- Creați o nouă sucursală principală locală la vârful sucursalei dvs. de dezvoltare .
- Împingeți această nouă structură folosind
git push --force
, deoarece depozitul de la distanță nu va accepta atât de ușor o astfel de „schimbare drastică”. Din nou, acest lucru nu este atât de nesigur pe cât ar putea părea în acest context, deoarece:- Pur și simplu mutăm indicatorul principal al ramurilor de la o comitere la alta.
- Doar un singur membru al echipei face această schimbare la un moment dat.
- Munca de dezvoltare de zi cu zi are loc pe ramura de dezvoltare, așa că nu veți perturba munca nimănui prin mutarea principală în acest fel.
- Ai noua ta lansare! Implementați-l în mediul de pregătire și testați-l. (Vom discuta modelele CI/CD convenabile mai jos.) Orice remediere se duce direct la ramura principală și va începe să diverge de la ramura de dezvoltare din această cauză.
- În același timp, puteți începe să lucrați la o nouă versiune în ramura de dezvoltare, același avantaj văzut în fluxul Git clasic.
- În cazul nefericit în care este nevoie de o remediere rapidă pentru ceea ce este în prezent în producție (nu pentru lansarea viitoare în staging), în acest moment, există mai multe detalii despre acest scenariu în „Rezolvarea remedierilor rapide în timpul unei lansări active...” de mai jos.
- Când noua dvs. versiune este considerată suficient de stabilă, implementați versiunea finală în mediul de producție și faceți o singură îmbinare squash a mainului pentru a dezvolta pentru a prelua toate remediile.
Remedieri rapide în Git Flow îmbunătățit
Cazurile de remediere rapidă sunt duble. Dacă faceți o remediere rapidă când nu există nicio versiune activă - adică, echipa pregătește o nouă versiune în ramura de dezvoltare - este ușor: Angajați-vă la principal, implementați modificările și testați-le în staging până când sunt gata, apoi implementați în producție.
Ca ultim pas, alegeți-vă angajamentul din principal pentru a dezvolta pentru a vă asigura că următoarea versiune va conține toate remediile. În cazul în care ajungeți cu mai multe comiteri de remediere rapidă, economisiți efort - mai ales dacă IDE-ul dvs. sau alt instrument Git vă poate facilita - prin crearea și aplicarea unui patch în loc să alegeți de mai multe ori. Încercarea de a squash merge main pentru a dezvolta după lansarea inițială este probabil să se încheie cu conflicte cu progresul independent realizat în ramura de dezvoltare, așa că nu recomand asta.
Abordarea remedierilor rapide în timpul unei lansări active - adică, atunci când doar forțați apăsarea principală și încă pregătiți noua versiune - este cea mai slabă parte a fluxului Git îmbunătățit. În funcție de durata ciclului de lansare și de severitatea problemei pe care trebuie să o rezolvi, urmărește întotdeauna să includă remedieri în noua ediție în sine — aceasta este cea mai ușoară cale de a parcurge și nu va perturba deloc fluxul de lucru general.
În cazul în care nu se poate, trebuie să introduceți rapid o remediere și abia așteptați ca noua versiune să fie pregătită, atunci pregătiți-vă pentru o procedură Git oarecum complicată:
- Creați o ramură - o vom numi „nouă lansare”, dar echipa dvs. poate adopta orice convenție de denumire aici - la același commit ca și vârful actual al main. Apăsați noua lansare.
- Ștergeți și recreați ramura principală locală la comiterea etichetei pe care ați creat-o anterior pentru versiunea activă curentă. Împingeți forțat principal.
- Introduceți corecțiile necesare pentru principal, implementați în mediul de pregătire și testați. Ori de câte ori este gata, implementați în producție.
- Propagați modificările de la versiunea principală curentă la cea nouă, fie prin alegerea cireșei, fie printr-un patch.
- După aceea, repetați procedura de eliberare: etichetați vârful principalului curent și împingeți eticheta, ștergeți și recreați principalul local la vârful ramurii noii lansări și forțați push main.
- Probabil că nu veți avea nevoie de eticheta anterioară, așa că o puteți elimina.
- Ramura de lansare nouă este acum redundantă, așa că o puteți elimina și pe aceasta.
- Acum ar trebui să fiți bine să mergeți ca de obicei cu o nouă lansare. Încheiați prin propagarea remedierilor rapide de urgență de la principal la dezvoltarea prin alegere de cireșe sau un patch.
Cu o planificare adecvată, o calitate suficient de înaltă a codului și o dezvoltare sănătoasă și o cultură QA, este puțin probabil ca echipa ta să fie nevoită să folosească această metodă. A fost prudent să dezvolt și să testez un astfel de plan de dezastru pentru un flux Git îmbunătățit, pentru orice eventualitate, dar nu am avut niciodată nevoie să-l folosesc în practică.
Configurare CI/CD pe partea de sus a fluxului Git îmbunătățit
Nu orice proiect necesită un mediu de dezvoltare dedicat. Poate fi destul de ușor să configurați un mediu de dezvoltare local sofisticat pe fiecare mașină de dezvoltator.
Dar un mediu de dezvoltare dedicat poate contribui la o cultură de dezvoltare mai sănătoasă. Executarea testelor, măsurarea acoperirii testelor și calcularea valorilor de complexitate pe ramura de dezvoltare scade adesea costul greșelilor prin surprinderea lor cu mult înainte de a ajunge în punere în scenă.
Am găsit unele modele CI/CD a fi deosebit de utile atunci când sunt combinate cu flux Git îmbunătățit:
- Dacă aveți nevoie de un mediu de dezvoltare, configurați CI pentru a construi, testa și implementa în el la fiecare angajare în ramura de dezvoltare. Încadrați-vă în testarea E2E și aici, dacă o aveți și dacă are sens în cazul dvs.
- Configurați CI pentru a construi, testa și implementa în mediul de pregătire la fiecare comitere în ramura principală. Testarea E2E este destul de benefică și în acest moment.
- Poate părea redundant să folosiți testarea E2E în ambele locuri, dar amintiți-vă că remedierile rapide nu se vor produce în curs de dezvoltare. Declanșarea E2E la comiterea în principal va testa remedierile rapide și modificările de zi cu zi înainte de a fi oprite, dar și declanșarea la comiterea pentru dezvoltare va detecta erori mai devreme.
- Configurați CI într-un mod care să permită echipei dvs. să implementeze versiuni din mediul principal în mediul de producție la o solicitare manuală.
Astfel de modele sunt relativ simple, dar oferă mașini puternice pentru a sprijini operațiunile de dezvoltare de zi cu zi.
Modelul de flux Git îmbunătățit: îmbunătățiri și posibile limitări
Fluxul Git îmbunătățit nu este pentru toată lumea. Ea folosește tactica controversată a forței care împinge ramura principală, așa că puriștii ar putea să-i fie supărați. Din punct de vedere practic, totuși, nu este nimic în neregulă.
După cum am menționat, remedierile rapide sunt mai dificile în timpul unei lansări, dar încă sunt posibile. Cu o atenție adecvată pentru QA, acoperirea testelor etc., asta nu ar trebui să se întâmple prea des, așa că din perspectiva mea, este un compromis valid pentru beneficiile generale ale fluxului Git îmbunătățit în comparație cu fluxul Git clasic. Aș fi foarte interesat să aud cum se face fluxul Git îmbunătățit în echipe mai mari și cu proiecte mai complexe, unde remedierea rapidă poate fi o apariție mai frecventă.
Experiența mea pozitivă cu modelul îmbunătățit de flux Git se învârte, de asemenea, în principal în jurul proiectelor comerciale cu sursă închisă. Poate fi problematic pentru un proiect cu sursă deschisă în care cererile de extragere se bazează adesea pe o veche derivare a versiunii arborelui sursă. Nu există obstacole tehnice pentru a rezolva asta - doar ar putea necesita mai mult efort decât se aștepta. Salut feedback-ul cititorilor cu multă experiență în spațiul open source cu privire la aplicabilitatea fluxului Git îmbunătățit în astfel de cazuri.
Mulțumiri speciale colegului Toptal, Antoine Pham, pentru rolul său cheie în dezvoltarea ideii din spatele fluxului Git îmbunătățit.
Citiți suplimentare pe blogul Toptal Engineering:
- Dezvoltare bazată pe trunchi vs. Git Flow
- Fluxuri de lucru Git pentru profesioniști: un ghid Git bun
În calitate de partener de aur Microsoft, Toptal este rețeaua dvs. de elită de experți Microsoft. Construiți echipe de înaltă performanță cu experții de care aveți nevoie - oriunde și exact când aveți nevoie de ei!