Git-workflows vergelijken: wat je moet weten
Git is tegenwoordig het meest gebruikte versiebeheersysteem. Een Git-workflow is een recept of aanbeveling voor het gebruik van Git om werk op een consistente en productieve manier uit te voeren. Git-workflows moedigen ontwikkelaars en DevOps-teams aan om Git effectief en consistent te gebruiken. Git biedt veel flexibiliteit in de manier waarop gebruikers met veranderingen omgaan. Gezien de focus van Git op flexibiliteit is er geen gestandaardiseerd proces voor de interactie met Git. Wanneer je met een team aan een door Git beheerd project werkt, is het belangrijk om ervoor te zorgen dat het team het helemaal eens is over hoe de stroom van veranderingen zal worden toegepast. Om ervoor te zorgen dat het team op één lijn zit, moet er een overeengekomen Git-workflow worden ontwikkeld of geselecteerd. Er zijn verschillende gepubliceerde Git-workflows die misschien goed bij je team passen. Hier bespreken we enkele van deze Git-workflowopties.
Door de vele mogelijke workflows kan het lastig zijn om te weten waar je moet beginnen als je Git op de werkvloer implementeert. Deze pagina biedt een startpunt door de meest voorkomende Git-workflows voor softwareteams te onderzoeken.
Onthoud bij het lezen dat deze workflows zijn ontworpen als richtlijnen en niet als concrete regels. We willen je laten zien wat er mogelijk is, zodat je aspecten uit verschillende workflows kunt combineren en matchen om aan je individuele behoeften te voldoen.
Wat is een succesvolle Git-workflow?
Bij het evalueren van een workflow voor je team is het van het grootste belang dat je rekening houdt met de cultuur van je team. Je wilt dat de workflow de effectiviteit van je team vergroot en niet een last vormt die de productiviteit beperkt. Bij het evalueren van een Git-workflow moet je rekening houden met het volgende:
- Schaalt deze workflow mee met de grootte van het team?
- Is het gemakkelijk om vergissingen en fouten ongedaan te maken met deze workflow?
- Brengt deze workflow nieuwe, onnodige cognitieve overheadkosten met zich mee voor het team?
Gecentraliseerde workflow
De gecentraliseerde workflow is een geweldige Git-workflow voor teams die overstappen van SVN. Net als Subversion maakt de gecentraliseerde workflow gebruik van een centrale repository die dient als het enige toegangspunt voor alle wijzigingen in het project. In plaats van trunk
heet de standaardontwikkelingsbranch main
en worden alle wijzigingen doorgevoerd in deze branch. Voor deze workflow zijn geen andere branches nodig naast main
.
De overgang naar een gedistribueerd versiebeheersysteem lijkt misschien een hele opgave, maar je hoeft je bestaande workflow niet te veranderen om te kunnen profiteren van Git. Je team kan projecten op precies dezelfde manier ontwikkelen als met Subversion.
Het gebruik van Git om je ontwikkelingsworkflow te stimuleren heeft echter een aantal voordelen boven SVN. Ten eerste geeft het elke ontwikkelaar een eigen lokale kopie van het hele project. In deze geïsoleerde omgeving kan elke ontwikkelaar onafhankelijk werken van alle andere wijzigingen in een project - ze kunnen commits toevoegen aan hun lokale repository en upstream-ontwikkelingen volledig vergeten totdat ze deze nodig hebben.
Ten tweede geeft het je toegang tot het robuuste Git-model voor branching en samenvoeging. In tegenstelling tot SVN zijn Git-branches ontworpen als een storingsveilig mechanisme voor het integreren van code en het delen van wijzigingen tussen repository's. De gecentraliseerde workflow is vergelijkbaar met andere workflows wat betreft het gebruik van een op een externe server gehoste repository, die ontwikkelaars kunnen pushen en pullen. In vergelijking met andere workflows kent de gecentraliseerde workflow geen gedefinieerde pull-aanvragen of vertakkingspatronen. Een gecentraliseerde workflow is over het algemeen beter geschikt voor teams die migreren van SVN naar Git en voor kleinere teams.
gerelateerd materiaal
Uitgebreid Git log
Oplossing bekijken
Git leren met Bitbucket Cloud
Hoe het werkt
Ontwikkelaars beginnen met het klonen van de centrale repository. In hun eigen lokale kopieën van het project bewerken ze bestanden en committen ze wijzigingen door zoals ze zouden doen met SVN; deze nieuwe commits worden echter lokaal opgeslagen - ze zijn volledig geïsoleerd van de centrale repository. Hierdoor kunnen ontwikkelaars het synchroniseren upstream uitstellen tot ze op een geschikt breekpunt zijn.
Om wijzigingen in het officiële project te publiceren, 'pushen' ontwikkelaars hun lokale main
-branch naar de centrale repository. Dit is het equivalent van svn commit
, met dien verstande dat alle lokale commits worden opgeteld die nog niet in de centrale main
-branch staan.
De centrale repository initialiseren
Eerst moet iemand de centrale repository op een server aanmaken. Als het een nieuw project is, kun je een lege repository initialiseren. Anders moet je een bestaande Git- of SVN-repository importeren.
Centrale repository's moeten altijd bare repository's zijn (ze mogen geen werkmap hebben), die als volgt kunnen worden aangemaakt:
ssh user@host git init --bare /path/to/repo.git
Zorg ervoor dat je een geldige SSH-gebruikersnaam gebruikt voor de gebruiker
, het domein of het IP-adres van je server voor de host
en de locatie waar je je repo wilt opslaan voor /path/to/repo.git
. Merk op dat de extensie .git
gewoonlijk wordt toegevoegd aan de naam van de repository om aan te geven dat het een bare repository is.
Gehoste centrale repository's
Centrale repository's worden vaak aangemaakt via Git-hostingdiensten van derden, zoals Bitbucket Cloud. Het hierboven besproken proces van initialisatie van een bare repository wordt voor je afgehandeld door de hostingdienst. De hostingservice zal dan een adres verstrekken dat de centrale repository kan openen vanuit je lokale repository.
De centrale repository klonen
Vervolgens maakt elke ontwikkelaar een lokale kopie aan van het hele project. Dit wordt bereikt met de opdracht git clone:
git clone ssh://user@host/path/to/repo.git
Wanneer je een repository kloont, voegt Git automatisch een sneltoets toe, genaamd origin
, die terugverwijst naar de 'bovenliggende' repository, in de veronderstelling dat je daar later in het traject mee wilt communiceren.
Wijzigingen aanbrengen en committen
Zodra de repository lokaal is gekloond, kan een ontwikkelaar wijzigingen aanbrengen met behulp van het standaard Git-commit-proces: bewerken, stagen en committen. Als je niet bekend bent met het staginggebied, is dat een manier om een commit voor te bereiden zonder dat je elke wijziging in de werkmap hoeft op te nemen. Zo kun je zeer gerichte commits creëren, zelfs als je veel lokale veranderingen hebt aangebracht.
git status # View the state of the repo
git add <some-file> # Stage a file
git commit # Commit a file</some-file>
Onthoud dat, aangezien deze opdrachten lokale commits genereren, John dit proces net zo vaak kan herhalen als hij wil zonder zich zorgen te hoeven maken over wat er in de centrale repository gebeurt. Dit kan erg handig zijn voor grote functies die opgesplitst moeten worden in eenvoudigere, meer atomische brokken.
Nieuwe commits naar de centrale repository pushen
Zodra de lokale repository nieuwe wijzigingen heeft doorgevoerd. Deze wijzigingen moeten worden doorgevoerd om ze te delen met andere ontwikkelaars die aan het project werken.
git push origin main
Deze opdracht pusht de nieuwe gecommitteerde wijzigingen naar de centrale repository. Bij het pushen van wijzigingen naar de centrale repository is het mogelijk dat er al eerder updates van een andere ontwikkelaar zijn gepusht die code bevatten die in strijd is met de beoogde push-updates. Git zal een bericht sturen waarin dit conflict wordt aangegeven. In deze situatie moet eerst git pull
worden uitgevoerd. Dit conflictscenario wordt nader toegelicht in het volgende gedeelte.
Conflicten beheren
De centrale repository vertegenwoordigt het officiële project, dus de commitgeschiedenis moet als heilig en onveranderlijk worden behandeld. Als de lokale commits van een ontwikkelaar afwijken van de centrale repository, weigert Git de wijzigingen te pushen omdat dit de officiële commits zou overschrijven.
Voordat de ontwikkelaar een functie kan publiceren, moet deze de bijgewerkte centrale commits ophalen en zijn of haar wijzigingen daarop rebaseren. Dit is hetzelfde als zeggen: „Ik wil mijn wijzigingen toevoegen aan wat John al heeft gedaan”. Het resultaat is een perfect lineaire geschiedenis, net als bij traditionele SVN-workflows.
Als lokale wijzigingen rechtstreeks in strijd zijn met upstream-commits, onderbreekt Git het rebaseringsproces en krijg je de kans om de conflicten handmatig oplossen. Het leuke van Git is dat het dezelfde opdrachten git status
en git add
gebruikt voor zowel het genereren van commits als het oplossen van samenvoegingsconflicten. Dit maakt het eenvoudig voor nieuwe ontwikkelaars om hun eigen samenvoegingen te beheren. En als ze zelf in de problemen komen, maakt Git het heel eenvoudig om de volledige rebase af te breken en het opnieuw te proberen (of hulp te zoeken).
Voorbeeld
Laten we een algemeen voorbeeld nemen van hoe een typisch klein team zou samenwerken volgens deze workflow. We gaan zien hoe twee ontwikkelaars, John en Mary, aan verschillende functies kunnen werken en hun bijdragen kunnen delen via een gecentraliseerde repository.
John werkt aan zijn functie
In zijn lokale repository kan John functies ontwikkelen met behulp van het standaard Git-commit-proces: bewerken, stagen en committen.
Onthoud dat, aangezien deze opdrachten lokale commits genereren, John dit proces net zo vaak kan herhalen als hij wil zonder zich zorgen te hoeven maken over wat er in de centrale repository gebeurt.
Mary werkt aan haar functie
Ondertussen werkt Mary aan haar eigen functie in haar eigen lokale repository, waarbij ze gebruik maakt van hetzelfde proces voor bewerken/stagen/committen. Net zoals voor John geldt, het maakt voor haar niet uit wat er in de centrale repository gebeurt, en het maakt haar echt niet uit wat John doet in zijn lokale repository, aangezien alle lokale repository's privé zijn.
John publiceert zijn functie
Zodra John klaar is met zijn functie, moet hij zijn lokale commits publiceren in de centrale repository zodat andere teamleden er toegang tot hebben. Hij kan dit doen met de opdracht git push, bijvoorbeeld:
git push origin main
Onthoud dat origin
de verbinding op afstand is met de centrale repository die Git heeft aangemaakt toen John die kloonde. Het main
argument geeft bij Git aan dat de main
-branch van de oorsprong
eruit moet zien als zijn lokale main
-branch. Aangezien de centrale repository niet is bijgewerkt sinds John deze heeft gekloond, zal dit geen conflicten veroorzaken en werkt de push zoals verwacht.
Mary probeert haar functie te publiceren
Laten we eens kijken wat er gebeurt als Mary haar functie probeert te pushen nadat John zijn wijzigingen in de centrale repository succesvol heeft gepubliceerd. Ze kan exact dezelfde push-opdracht gebruiken:
git push origin main
Maar aangezien haar lokale geschiedenis afwijkt van de centrale repository, weigert Git de aanvraag met een nogal uitgebreide foutmelding:
error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Dit voorkomt dat Mary officiële commits overschrijft. Ze moet de updates van John in haar repository opnemen, ze integreren met haar lokale wijzigingen en het dan opnieuw proberen.
Mary rebaseert bovenop de commit(s) van John
Mary kan git pull gebruiken om upstream veranderingen in haar repository aan te brengen. Deze opdracht lijkt een beetje op svn-update
: hij haalt de volledige geschiedenis van de upstream commit binnen in Mary's lokale repository en probeert die te integreren in haar lokale commits:
git pull --rebase origin main
De optie --rebase
geeft Git de opdracht om alle commits van Mary naar de tip van de main
-branch te verplaatsen nadat deze zijn gesynchroniseerd met de wijzigingen uit de centrale repository, zoals hieronder weergegeven:
De pull zou nog steeds werken als je deze optie zou vergeten, maar je zou dan eindigen met een overbodige 'samenvoegingscommit' elke keer dat iemand moest synchroniseren met de centrale repository. Voor deze workflow is het altijd beter om te rebaseren in plaats van een merge commit te genereren.
Mary lost een samenvoegingsconflict op
Rebasing werkt door elke lokale commit één voor één over te dragen naar de vernieuwde main
-branch. Dit betekent dat je samenvoegingsconflicten vastlegt op basis van commit-by-commit in plaats van ze allemaal op te lossen in één grote samenvoegingscommit. Dit zorgt ervoor dat je commits zo gefocust mogelijk blijven en zorgt voor een schone projectgeschiedenis. Dit maakt het op zijn beurt veel gemakkelijker om uit te zoeken waar bugs zijn geïntroduceerd en, indien nodig, om wijzigingen terug te draaien met minimale gevolgen voor het project.
Als Mary en John aan functies werken die niets met elkaar te maken hebben, is het onwaarschijnlijk dat het rebaseringsproces tot conflicten zal leiden. Maar als dat wel gebeurt, pauzeert Git de rebase bij de huidige commit en wordt het volgende bericht weergegeven, samen met enkele relevante instructies:
CONFLICT (content): Merge conflict in <some-file>
Het mooie van Git is dat iedereen zijn eigen samenvoegingsconflicten kan oplossen. In ons voorbeeld zou Mary gewoon een git-status uitvoeren om te zien waar het probleem zit. Conflicterende bestanden worden weergegeven in het gedeelte Niet-samengevoegde paden:
# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
#
# both modified: <some-file>
Daarna kan ze het bestand of de bestanden naar eigen inzicht bewerken. Zodra ze tevreden is met het resultaat, kan ze de bestanden op de gebruikelijke manier stagen en git-tebase de rest laten doen:
git add <some-file>
git rebase --continue
En dat is alles wat nodig is. Git gaat verder met de volgende commit en herhaalt het proces voor alle andere commits die tot conflicten leiden.
Als je zover komt en je beseft dat je geen idee hebt wat er aan de hand is, raak dan niet in paniek. Voer gewoon de volgende opdracht uit en je bent meteen terug waar je begon:
git rebase --abort
Mary publiceert haar functie met succes
Wanneer ze klaar is met de synchronisatie met de centrale repository, kan Mary haar wijzigingen publiceren:
git push origin main
Hoe nu verder?
Zoals je ziet, is het mogelijk om een traditionele Subversion-ontwikkelomgeving te repliceren met slechts een handvol Git-opdrachten. Dit is geweldig om teams van SVN af te krijgen, maar het maakt geen gebruik van het gedistribueerde karakter van Git.
De gecentraliseerde workflow is geweldig voor kleine teams. Het hierboven beschreven proces voor het oplossen van conflicten kan een knelpunt vormen naarmate je team groter wordt. Als je team vertrouwd is met de gecentraliseerde workflow, maar de samenwerkingsinspanningen wil stroomlijnen, is het zeker de moeite waard om de voordelen van de Workflow feature-branches te verkennen. Door aan elke functie een afzonderlijke branch te wijden, is het mogelijk om diepgaande discussies op gang te brengen over nieuwe toevoegingen voordat ze worden geïntegreerd in het officiële project.
Andere veel voorkomende workflows
De gecentraliseerde workflow is in wezen een bouwsteen voor andere Git-workflows. De meeste populaire Git-workflows hebben een soort gecentraliseerde repo die individuele ontwikkelaars kunnen pushen en pullen. Hieronder bespreken we kort een paar andere populaire Git-workflows. Deze uitgebreide workflows bieden meer gespecialiseerde patronen om branches te beheren voor de ontwikkeling van functies, hotfixes en de uiteindelijke release.
Functiebranches
Feature-branches zijn een logische uitbreiding van een gecentraliseerde workflow. Het kernidee achter de Workflow feature-branches is dat alle ontwikkeling van functies moet plaatsvinden in een speciale branch in plaats van in de main
-branch. Deze inkapseling maakt het eenvoudig om meerdere ontwikkelaars aan een bepaalde functie te laten werken zonder de hoofdcodebase te verstoren. Het betekent ook dat de main
-branch nooit beschadigde code mag bevatten, wat een groot voordeel is voor omgevingen met continue integratie.
Gitflow-workflow
De Workflow Gitflow is voor het eerst gepubliceerd in een zeer gewaardeerde blogpost uit 2010 van Vincent Driessen op nvie. De workflow Gitflow definieert een strikt branchingmodel dat is ontworpen rond de projectrelease. Deze workflow voegt geen nieuwe concepten of opdrachten toe die verder gaan dan wat nodig is voor de Workflow feature-branches. In plaats daarvan kent de workflow zeer specifieke rollen toe aan verschillende branches en definieert hij hoe en wanneer deze moeten communiceren.
Workflow voor vertakking
De Workflow Vertakking is fundamenteel anders dan de andere workflows die in deze tutorial worden besproken. Forking (vertakken) geeft elke ontwikkelaar een repository aan de serverkant in plaats van dat één repository aan de serverkant wordt gebruikt als de 'centrale' codebase. Elke medewerker heeft dus niet één, maar twee Git-repository's: een lokale (privé) en een aan een openbare serverkant.
Richtlijnen
Er is niet één Git-workflow die voor alles en iedereen geschikt is. Zoals eerder vermeld, is het belangrijk om een Git-workflow te ontwikkelen die de productiviteit van je team verhoogt. Naast de teamcultuur moet een workflow ook een aanvulling zijn op de bedrijfscultuur. Git-functies zoals branches en tags zouden een aanvulling moeten zijn op het releaseschema van je bedrijf. Als je team software voor projectmanagement gebruikt om taken bij te houden, wil je misschien branches gebruiken die overeenkomen met lopende taken. Daarnaast zijn er enkele richtlijnen waarmee je rekening moet houden bij het kiezen van een workflow:
Branches met een korte levensduur
Hoe langer een branch gescheiden van de productiesector bestaat, hoe groter het risico op samenvoegingsconflicten en problemen bij de implementatie. Branches met een korte levensduur leiden schonere samenvoegingen en implementaties.
Terugdraaien tot een minimum beperken en vereenvoudigen
Het is belangrijk om een workflow te hebben die proactief helpt om samenvoegingen te voorkomen die moeten worden teruggedraaid. Een voorbeeld hiervan is een workflow waarbij een branch wordt getest voordat deze kan worden samengevoegd met de main
-branch. Ongelukken gebeuren echter wel degelijk. Dat gezegd hebbende, is het nuttig om een workflow te hebben die het mogelijk maakt om dingen terug te draaien zonder de flow voor andere teamleden te verstoren.
Een releaseschema afstemmen
Een workflow moet een aanvulling zijn op de releasecyclus voor softwareontwikkeling van je bedrijf. Als je van plan bent om meerdere keren per dag een release uit te brengen, dan wil je dat je main
-branch stabiel blijft. Als je releaseschema minder frequent is, kun je overwegen om Git-tags te gebruiken om een branch naar een versie te taggen.
Samenvatting
In dit document hebben we de Git-workflows besproken. We hebben uitgebreid gekeken naar een gecentraliseerde workflow met praktische voorbeelden. Voortbouwend op de gecentraliseerde workflow bespraken we aanvullende gespecialiseerde workflows. Enkele belangrijke opmerkingen uit dit document zijn:
- Er is niet één Git-workflow die voor alles en iedereen geschikt is
- Een workflow moet eenvoudig zijn en de productiviteit van je team vergroten
- De vereisten van je bedrijf moeten je Git-workflow helpen vormgeven
Bekijk ons uitgebreide overzicht van de Workflow feature-branche voor meer informatie over de volgende Git-workflow.
Deel dit artikel
Volgend onderwerp
Aanbevolen artikelen
Bookmark deze resources voor meer informatie over soorten DevOps-teams of voor voortdurende updates over DevOps bij Atlassian.