Leerpad
We kunnen een applicatie in gevaar brengen door problematische code te mergen, bijvoorbeeld doordat we per ongeluk onvoltooide werkzaamheden in de main-branch integreren of een kritieke bug over het hoofd zien die door geautomatiseerde tests is geglipt.
In dit artikel laat ik je zien hoe je git revert gebruikt om een merge veilig ongedaan te maken, zodat de commitgeschiedenis intact blijft en de integriteit van het project behouden blijft.
Hoe git revert werkt
Je kunt git revert zien als Git’s variant van een undo-commando. Het git revert-commando verwijdert echter geen commits en springt ook niet terug naar een vorige staat van de branch. In plaats daarvan maakt het een nieuwe commit die de wijzigingen van een specifieke commit terugdraait.

De syntaxis om een commit met hash <commit_hash> terug te draaien is:
git revert <commit_hash>
We kunnen de commits met hun hash-identificaties opvragen met het commando git log. De output van git log toont de commits van recent naar oud, zoals hieronder:

Om bijvoorbeeld de commit die de subtract-functie implementeert terug te draaien, gebruiken we het commando:
git revert 7ba24a3e62d4d37182428ccfaa070baa222b1151
Met git revert kunnen we de wijzigingen van een specifieke commit ongedaan maken zonder de commitgeschiedenis te beïnvloeden.
Let op dat git revert geen tovermiddel is; afhankelijk van de commitgeschiedenis kan er een conflict ontstaan dat je handmatig moet oplossen.
Voordelen van git revert boven handmatige wijzigingen
Waarom is git revert nuttig als we mogelijk toch handmatig een conflict moeten oplossen? Zou het niet makkelijker zijn om de wijzigingen gewoon met de hand terug te draaien? Dit zijn de voordelen:
- Behoudt geschiedenis:
git revertmaakt een nieuwe commit die de wijzigingen van een opgegeven commit terugdraait, terwijl de volledige commitgeschiedenis behouden blijft. Dit helpt een transparant overzicht van wijzigingen en terugdraaiacties te bewaren. - Atomair terugdraaien: Het zorgt ervoor dat terugdraaiacties atomair en consistent zijn. Bij handmatig verwijderen en committen is er risico op menselijke fouten.
- Conflict-bewust: Het zorgt dat we via conflicten een waarschuwing krijgen als er integraties of wijzigingen afhankelijk zijn van de oorspronkelijke commit. Dat lijkt misschien onhandig, maar het beschermt tegen onbedoelde neveneffecten.
- Metadata: De nieuwe commit die door git revert wordt aangemaakt, bevat metadata en een commitbericht dat context geeft over wat er is teruggedraaid, wat later begrip bevordert. Zonder
git revertgaat die context mogelijk verloren.
Een merge terugdraaien in verschillende scenario's
In deze sectie leer je hoe je een merge ongedaan maakt. Ter illustratie gaan we ervan uit dat we een branch genaamd feature mergen in de main-branch en het commando uitvoeren vanuit de main-branch:
git merge feature
Wat we hier leren, kun je toepassen op willekeurige twee branches door de namen passend te vervangen.
Een merge terugdraaien zonder bijbehorende commit
git merge maakt niet altijd een nieuwe commit aan. Er wordt alleen een commit aangemaakt als de main-branch is afgetakt ten opzichte van de feature-branch. Omdat git revert een commit nodig heeft om op te werken, kunnen we het in dit geval niet gebruiken.
De branches main en feature lopen uiteen wanneer er nieuwe commits op main zijn die geen voorouders zijn van de feature-branch. Met andere woorden: er zijn nieuwe commits op main gemaakt nadat feature is aangemaakt.
Als de branches niet zijn uiteengelopen, zal Git een fast-forward-merge doen wanneer we op de main-branch het commando git merge feature draaien. Dat betekent dat de HEAD van de main-branch wordt verplaatst naar de HEAD van de feature-branch.

We kunnen dit herkennen aan de output van git merge:

Om zo'n merge ongedaan te maken, hoeven we alleen de HEAD van de main-branch terug te zetten naar waar die stond. Hiervoor doen we het volgende:
- Identificeer de vorige
HEADmetgit reflog - Reset de
HEADnaar de vorige metgit reset --hard <previous_head>, waarbij je<previous_head>vervangt door de vorigeHEAD.
De output van git reflog ziet er ongeveer zo uit:

We kunnen de vorige HEAD herkennen aan de regel die zegt: “checkout: moving from feature to main” (er staat feature en main omdat dat de namen van onze branches zijn).
In dit geval is de vorige head fe59838. Om de HEAD van de main-branch hiernaar terug te zetten en de merge ongedaan te maken, gebruiken we het commando:
git reset --hard fe59838

Een merge terugdraaien met een bijbehorende commit
Als de main- en feature-branch uiteen zijn gelopen, wordt er bij het mergen van twee branches een nieuwe commit aangemaakt: een merge-commit.

De merge-commit past de wijzigingen van de ene branch toe op de andere. In dit geval worden de wijzigingen in feature toegepast op de main-branch.
Om de wijzigingen op de main-branch terug te draaien, gebruiken we git revert op de merge-commit. Dit maakt een nieuwe commit die de wijzigingen die met de merge in de main-branch zijn gekomen ongedaan maakt, waardoor de staat van de main-branch effectief wordt hersteld naar hoe die was vóór de merge.
Eerst moeten we de hash van de merge-commit identificeren. Dat kan met het commando git log:

Omdat een merge-commit twee ouders heeft, is de syntaxis van git revert net iets anders. We moeten de optie -m 1 gebruiken om aan te geven dat we de wijzigingen relatief ten opzichte van de main-branch willen terugdraaien:
git revert -m 1 b8dab2c8611e324ed0d273133987415350e6d10d

Conflicten oplossen bij het terugdraaien van een commit
Soms kunnen er conflicten ontstaan bij het terugdraaien van een commit, vooral als de terug te draaien commit botst met latere wijzigingen in de codebase. In zulke gevallen:
- Git pauzeert de revert: je moet conflicten handmatig oplossen. Git markeert de conflicterende bestanden en vereist ingrijpen.
- Los de conflicten op: open elk conflicterend bestand, los de door Git gemarkeerde conflicten op en sla de wijzigingen op.
- Stage de opgeloste bestanden:
git add <file-path> - Ga verder met de revert:
git revert --continue
Conclusie
Een merge-commit ongedaan maken met git revert zorgt ervoor dat elke wijziging en correctie in de commitgeschiedenis wordt vastgelegd.
Bovendien helpt inzicht in wanneer je git reset versus git revert toepast om betere keuzes te maken, zeker in collaboratieve workflows of bij lokale wijzigingen.
Meer hierover lees je in de veelgestelde vragen hieronder. Wil je meer leren over Git? Dan raad ik deze bronnen aan:
Veelgestelde vragen
Kan ik een al gepushte merge terugdraaien met git revert?
Ja, dat is het voornaamste gebruik van git revert bij merges. Als de wijzigingen lokaal zijn, is git reset een eenvoudigere manier om de merge ongedaan te maken.
Opdat anderen de wijzigingen zien, moeten we ze pushen naar de remote repository. Dus om een al gepushte merge ongedaan te maken, doen we:
git revert -m 1 <merge_commit_hash>
git push origin <branch_name>
Vervang <merge_commit_hash> door de hash van de merge-commit en <brach_name> door de naam van de branch waarop de merge is uitgevoerd.
Wordt de merge-commit verwijderd na het gebruik van git revert?
Nee. De merge-commit blijft in de geschiedenis staan, maar de wijzigingen worden ongedaan gemaakt door de nieuwe commit. Het hele punt van een merge ongedaan maken met git revert is juist het behouden van de commitgeschiedenis.
Zijn er andere manieren dan git revert om een merge ongedaan te maken?
Een alternatieve manier om een merge ongedaan te maken is git reset gebruiken. Deze methode zet de branch terug naar een staat vóór de merge-commit. Deze aanpak is ingrijpender en kan problemen veroorzaken als andere collega's hun werk al hebben gebaseerd op de commits die we willen terugzetten. Daarom wordt dit alleen aangeraden als we zeker weten dat het anderen niet beïnvloedt.
git reset --hard <commit_hash>
git push --force
Vervang <commit_hash> door de hash van de commit waarnaar je wilt teruggaan.
Wanneer moet ik git reset gebruiken om een merge ongedaan te maken?
Ik raad aan git reset alleen te gebruiken om een merge ongedaan te maken wanneer de merge uitsluitend lokaal heeft plaatsgevonden en nog niet is gepusht.

