Ga naar hoofdinhoud

Git pull forceren: zo overschrijf je een lokale branch met de remote

Leer waarom git pull --force niet de beste manier is om een lokale branch met de remoteversie te overschrijven, en ontdek de juiste methode met git fetch en git reset.

Verken met AI

In sommige situaties willen we de status van een lokale Git-branch resetten zodat deze overeenkomt met de remote repository, waarbij we alle lokale wijzigingen weggooien. Dit wordt vaak een geforceerde pull genoemd.

Dit wekt de misvatting dat je hiervoor de optie --force in het commando git pull moet gebruiken. In dit artikel leer je dat dit verschillende dingen zijn.

Ben je nieuw met Git? Lees dan eerst onze Git push- en pull-tutorial.

We kunnen onze lokale status overschrijven met de remote status met de onderstaande commando’s. Vervang <branch_name> door de naam van je branch. Deze actie zal al je lokale wijzigingen permanent verwijderen in die branch.

git fetch
git reset --hard origin/<branch_name>

Dit artikel gaat dieper in op hoe deze commando’s werken. We gaan verder dan snelle oplossingen om de onderliggende mechanismen te begrijpen, zodat we effectievere methoden leren om het gewenste resultaat te bereiken zonder het risico belangrijke wijzigingen kwijt te raken. We ruimen ook de misvatting op en leggen uit wat de optie --force van git pull nu eigenlijk doet.

Wanneer je lokale wijzigingen overschrijven te overwegen

In het algemeen druist het op deze manier overschrijven van lokale wijzigingen in tegen het beoogde gebruik van Git. Voor vrijwel elke situatie bestaat er een juiste Git-workflow om het op te lossen. Sommige daarvan vragen echter om een dieper begrip van Git en kosten meer tijd om uit te voeren. Ook al is het geen best practice, lokale wijzigingen overschrijven kan soms de snelste manier zijn om voortgang te boeken.

Bij mij gebeurt dit meestal wanneer ik aan een feature werk waar ik niet erg vertrouwd mee ben. Ik begin met het aanmaken van een branch en maak een paar commits. Als ik vastloop, vraag ik een collega om hulp. Die begint vanaf een eerdere commit, voordat ik de fout maakte, lost het probleem op en pusht de wijzigingen. Op dat moment moet ik mijn lokale versie vervangen door de bijgewerkte remote versie en mijn lokale wijzigingen weggooien, zodat ik verder kan werken aan de feature.

Een ander voorbeeld is wanneer er een conflict is en het lastiger of tijdrovender voelt om het conflict op te lossen dan om onze wijzigingen opnieuw te implementeren op de nieuwste versie van de code.

Wat de reden ook is, zorg er altijd voor dat die wijzigingen echt niet nodig zijn en veilig verwijderd kunnen worden, of maak een back-up van je branch om te voorkomen dat je belangrijk werk kwijtraakt.

Lokale wijzigingen op de juiste manier overschrijven

De eerste stap is het fetchen (downloaden) van de nieuwste inhoud van de remote repository naar onze lokale machine. Dit doe je met het fetch-commando van git:

git fetch

Standaard haalt dit de inhoud van de huidige branch op, wat meestal is wat we nodig hebben. Optioneel kunnen we de branch specificeren die we willen downloaden (vervang hieronder <branch_name> door de naam van de branch die je wilt fetchen):

git fetch origin/<branch_name>

Als alternatief kun je de optie --all gebruiken om alles te downloaden:

git fetch –all

Het fetchen van wijzigingen uit de remote repository heeft geen invloed op de bestanden in de directory van je huidige werkbranch. Het commando fetch downloadt de wijzigingen van de remote repository maar voegt ze niet samen. De lokale repository houdt zowel een weergave van de lokale bestanden als van de remotebestanden bij, en fetchen werkt de weergave van de remotebestanden bij.

Na het downloaden van de wijzigingen van de remote repository kunnen we met het reset-commando van git de huidige werkbranch naar een bepaalde staat terugzetten:

git reset --hard origin/<branch_name>

De optie --hard is nodig om ervoor te zorgen dat de lokale wijzigingen worden overschreven door de remote wijzigingen. In het bovenstaande commando kozen we ervoor om de huidige werkbranch te vervangen door origin/<banch_name>, wat overeenkomt met de remote versie van die branch die zojuist is gefetcht.

Het commando git reset verwijdert lokale wijzigingen, zelfs als ze gecommit zijn. Het is daarom aan te raden om eerst een lokale back-up van de huidige branch te maken voordat je dat commando uitvoert. Dat kan met:

git branch <name_of_the_backup_branck>

Vervang <name_of_the_backup_branch> door de naam die je de back-upbranch wilt geven.

Alles bij elkaar kunnen we alle lokale wijzigingen van een bepaalde branch overschrijven met de nieuwste remote versie door het volgende te doen:

git fetch
git branch <name_of_the_backup_branch>
git reset --hard origin/<branch_name>

Wil je je kennis over werken met branches in Git uitbreiden? Bekijk dan de tutorial over een specifieke branch clonen met Git.

Volledige opschoning door niet-getrackte bestanden te verwijderen

Git raakt niet-getrackte bestanden (bestanden die nooit met git add zijn toegevoegd) niet aan. Als we deze bestanden ook willen verwijderen, kan dat met:

git clean -fdx

Dit is een gevaarlijk commando dat wordt gebruikt om niet-getrackte bestanden uit de werkdirectory te verwijderen. Het is essentieel om dit commando voorzichtig te gebruiken, omdat het deze bestanden permanent verwijdert, waardoor het onmogelijk is om ze via Git te herstellen. Hier is een toelichting op de opties:

  • -f: Deze optie dwingt de clean-operatie af. Zonder deze vlag weigert Git uit veiligheidsoverwegingen niet-getrackte bestanden in de werkdirectory te verwijderen om onbedoeld dataverlies te voorkomen.
  • -d: Dit vertelt git clean dat niet alleen niet-getrackte bestanden, maar ook niet-getrackte mappen verwijderd moeten worden. Standaard richt git clean zich alleen op niet-getrackte bestanden en negeert het mappen.
  • -x: Standaard respecteert git clean de regels in .gitignore en verwijdert het geen bestanden of mappen die aan deze patronen voldoen. Door -x op te nemen, wordt dit gedrag overschreven en instrueer je Git om ook de bestanden/mappen te verwijderen die door .gitignore of andere ignore-regels worden genegeerd. Dit resulteert in een zeer grondige opschoning, waarbij alle niet-getrackte bestanden in de repository worden verwijderd, inclusief buildoutputs, logbestanden en andere gegenereerde artefacten die doorgaans genegeerd worden.

Git pull begrijpen

De gebruikelijke manier om onze lokale repository bij te werken met remote wijzigingen is met het commando git pull. Dit commando kun je echter niet gebruiken om lokale wijzigingen te overschrijven, omdat git pull zal proberen onze lokale versie met de remote te mergen.

Onder de motorkap voert git pull de volgende stappen uit:

  1. Het gebruikt git fetch om de nieuwste versie van de remote repository te downloaden.
  2. Het gebruikt git merge om de lokale en remote branches samen te voegen.

Door de merge-stap is git pull geen geschikt commando in situaties waarin we lokale wijzigingen willen negeren en weggooien.

<code data-mce-selected=

Hoe zit het met git pull --force?

Als we de documentatie van het commando git pull bekijken, zien we dat het een optie --force biedt. Het is een veelvoorkomende misvatting dat deze optie git pull zal dwingen om de lokale wijzigingen te wissen en te vervangen door de remote wijzigingen.

Wanneer we de optie --force aan git pull meegeven, wordt deze optie doorgegeven aan git fetch. Dus git pull --force voert de volgende stappen uit:

  1. git fetch --force
  2. git merge

 <code data-mce-selected=

We hebben geleerd dat fetch wordt gebruikt om de lokale weergave van de remote repository bij te werken. Als het enige verschil tussen je huidige lokale repository en de remote repository ontbrekende commits zijn (de lokale branch loopt achter op de remote versie), dan zal git fetch eenvoudig de lokale weergave bijwerken door die nieuwe commits aan de geschiedenis toe te voegen.

Git fetch synchroniseert de lokale repository met de remote repository als de branchgeschiedenis overeenkomt

Het kan echter zijn dat iemand de commitgeschiedenis van de remote branch heeft herschreven, waardoor die onverenigbaar is met de lokale weergave. In dat geval werkt fetch niet.

Git fetch zal falen als de branchgeschiedenis niet overeenkomt, tenzij we de <code data-mce-selected=

We kunnen dit omzeilen met de optie --force, die Git dwingt de lokale geschiedenis te negeren en te overschrijven met de remote geschiedenis. 

We zien dat de optie --force te maken heeft met het forceren van het overschrijven van de commitgeschiedenis en niets te maken heeft met het negeren van lokale wijzigingen in de werkbranchdirectory.

Lokale wijzigingen integreren

Lokale wijzigingen overschrijven met de remote wijzigingen is geen gangbare Git-praktijk, maar kan nuttig zijn in bepaalde gevallen waarin we geen conflicten willen oplossen of niet om onze lokale wijzigingen geven.

Mergen

Meestal haal je de wijzigingen op en los je de conflicten op. Mensen zijn vaak bang om met conflicten om te gaan. Sommige conflicten kunnen overweldigend zijn, maar dat is niet vaak zo. Een conflict treedt op wanneer:

  1. Zowel de lokale branch als de remote branch wijzigingen hebben in hetzelfde deel van een bestand.
  2. Een bestand aan de ene kant is verwijderd en aan de andere kant gewijzigd.

In deze gevallen kan Git niet raden welke wijzigingen we willen behouden, en moeten we dat handmatig aangeven. Git markeert het conflict in de bestanden zoals hieronder:

Voorbeeld van Git-conflicten

Om een conflict op te lossen, bewerk je het bestand door deze regels te verwijderen, behalve de regels die je wilt behouden. Nadat je alle conflicten hebt afgehandeld, kun je de wijzigingen committen. Voor een uitgebreide gids, bekijk deze tutorial over het oplossen van mergeconflicten in Git.

Cherry-picken

Als we echt eerst onze lokale versie gelijk willen trekken met de remote versie en vervolgens onze wijzigingen willen integreren, kunnen we de commits uit de back-upbranch in de huidige branch halen, zoals hier:

git cherry-pick <commit_hash>

Hier is <commit_hash> de hash van de commit die we willen integreren. Dit veronderstelt dat we vóór git reset een back-upbranch hebben gemaakt met git branch <name_of_the_backup_branck> en dat de lokale wijzigingen gecommit waren.

Om de <commit_hash> te vinden, kunnen we de commit-hashes in de back-upbranch opvragen met het volgende commando:

git log <name_of_the_backup_branck>

Stashen

Als alternatief, als we onze wijzigingen niet willen committen of een back-upbranch willen maken, kunnen we onze wijzigingen voor het mergen stashen met:

git stash

Dit commando kan worden gebruikt om het huidige werk tijdelijk op te slaan zonder het aan de branchgeschiedenis toe te voegen. Stashen zet je aanpassingen opzij, zodat je een schone werkdirectory hebt.

Na het resetten kunnen we onze wijzigingen terughalen met het volgende commando:

git stash pop

Met stashen ziet de hele workflow er ongeveer zo uit:

git fetch
git stash
git reset --hard origin/<branch_name>
git stash pop

Wil je meer leren? Bekijk dan dit cheat sheet met stash en andere git-commando’s.

Conclusie

Ook al is het geen best practice, soms zitten we in een situatie waarin we gewoon onze lokale branch willen laten overeenkomen met de inhoud van de remote branch, terwijl we onze lokale wijzigingen weggooien.

Er bestaat een misvatting dat dit gebeurt met git pull --force. Dat klopt niet, want git pull --force combineert git fetch --force en git merge. Het probeert dus de lokale wijzigingen met de remote wijzigingen te mergen, niet ze te overschrijven.

De juiste manier om lokale wijzigingen te overschrijven met de inhoud van de remote repository is:

git fetch
git reset --hard origin/<branch_name>

Het commando git reset moet je voorzichtig gebruiken, omdat het lokale wijzigingen verwijdert, zelfs als ze zijn gecommit. Als we lokale commits hadden en een back-upbranch hebben gemaakt, kunnen we ze terughalen met git cherry-pick. Hebben we niet gecommit en willen we ons lokale werk tijdelijk opslaan, gebruik dan git stash vóór het resetten en daarna git stash pop om onze wijzigingen terug te zetten.

François Aubry

Auteur

François Aubry

Full-stack engineer & oprichter bij CheapGPT. Lesgeven is altijd mijn passie geweest. Al vanaf mijn studententijd zocht ik actief naar kansen om bijles te geven en andere studenten te helpen. Die passie bracht me ertoe om te promoveren, waarbij ik ook als onderwijsassistent werkte ter ondersteuning van mijn academische werk. In die jaren vond ik veel voldoening in de traditionele klasomgeving, waar ik verbindingen kon leggen en leren kon begeleiden. Met de opkomst van online leerplatforms zag ik echter het transformerende potentieel van digitaal onderwijs. Sterker nog, ik was actief betrokken bij de ontwikkeling van zo’n platform op onze universiteit. Ik zet me sterk in om traditionele didactische principes te combineren met innovatieve digitale methoden. Mijn passie is om cursussen te maken die niet alleen boeiend en informatief zijn, maar ook toegankelijk voor lerenden in dit digitale tijdperk.

Onderwerpen

Gerelateerd