Categoriearchief: git

git undo commit, soft style

Wat te doen als je een commit ongedaan wil maken, maar toch de wijzigingen wil behouden?

Gebruik simpelweg hetvolgende commando:

$ git reset --soft HEAD~1

Hiermee zet je de huidige branch 1 commit terug, maar de wijzigingen blijven in de index staan. Zou je direct weer een commit doen, dan krijg je exact dezelfde commit (afgezien dan van het tijdstip en de commit message).

Dit kan vooral handig zijn als je aanpassingen tijdelijk hebt opgeslagen in een commit, ipv een stash.
Hoewel je ook een amend commit kan doen, is het soms makkelijker te zien welke aanpassingen je allemaal hebt gedaan. Dan komt dit heel mooi uit.

git push en tracking remote branch

Vervelend soms als je een nieuwe git branch naar een remote repository pusht, maar hij niet gelijk tracking wordt. Hierdoor is git pull ineens lastig te gebruiken omdat je de juiste parameters moet gaan opgeven.

De oplossing is vrij simpel, bij het initieel pushen de parameter -u toevoegen.

$ git push -u origin mycoolbranch

Hiermee wordt direct de tracking informatie ingesteld waarmee een pull later wordt vereenvoudigd.

verwijderen van git branches en tags

Met regelmaat zal je een branch verwijderen in git. Lokaal is dit eenvoudig:

# verwijder een lokale branch
git branch -d <branch>

Maar remote is dat een iets complexer verhaal:
Gebruik je een versie ouder dan 1.7.0, dan is dit het antwoord:

# verwijder een remote branch, oude stijl
git push origin :<branch>

Niet echt duidelijk wat er nu gebeurd, daarom vanaf versie 1.7.0:

# verwijder een remote branch, nieuwe stijl
git push --delete origin <branch>

Voor tags is de syntax bijna hetzelfde:

# verwijder lokale tag
git tag -d <tag>

# verwijder remote tag in versie's ouder dan 1.7.0
git push origin :refs/tags/<tag>

# verwijder remote tag in versie's vanaf 1.7.0
git push --delete origin <tag>

Tenslotte worden remote branches ook wel eens door anderen verwijderd. Dit wordt niet automatisch bijgewerkt door git wanneer je pullt of fetched.
Ook hiervoor is een simpele methode om dit bij te werken:

# update de lokale database van remote branches
git remote prune origin

En daarmee staat weer een stukje van mijn geheugen online :).

git gebruiken bij het live zetten van nieuwe versies

Wanneer je regelmatig wijzigingen doet aan een applicatie die op meerdere computers draait, is het prettig dat dit makkelijk bijgewerkt kan worden. Dit kan je doen door tags te maken en bij het updaten de laatste tag op te zoeken. Groot nadeel hiervan is echter wel dat je op een gegeven moment een heleboel tags hebt die je niet meer gebruikt.

Een betere oplossing is een wandelende verwijzing naar welke versie je op dit moment wil gebruiken. Hiervoor is een branch meer geschikt. Tags zijn immers niet bedoeld om te verplaatsen.

Kortom, we maken een branch aan dit enkel gebruikt wordt om de versie aan te wijzen welke in productie gebruikt moet worden. Dit natuurlijk in je ontwikkelomgeving.

git branch production
git push -u origin production

Elke keer als je de versie wil wijzigen die in productie gebruikt moet worden wil wijzigen verplaats je de branch. Hoe je dat doet maakt niet zoveel uit, maar meestal zal het een merge zijn waarvoor fast-forward mogelijk is. Je branch production zal dus meestal achter je master aanlopen.

Op de locatie waar de code daadwerkelijk gebruikt wordt kan dan vrij simpel gecontroleerd worden of er een wijziging is:

# zorg dat de remote informatie bijgewerkt is
git fetch origin

# vind de hash van de huidige commit die actief is
commithead=`git rev-parse HEAD`

# vind de hash van de commit van de laatste versie
commitprod=`git rev-parse origin/production`

Het ophalen van de hashes maakt het in bash eenvoudig om te zien of de branch verplaatst is zonder een diff te hoeven opvragen.

Het opvragen van de verschillen kan alsvolgt:

if [ "${commithead}" != "${commitprod}" ]
then
    # toon een samenvatting van de wijzigingen
    git diff ${commithead} ${commitprod} --stat

    # toon een compleet overzicht van de wijzigingen
    git diff ${commithead} ${commitprod}

    # checkout nieuwe wijzigingen
    git checkout origin/production
fi

In een iets uitgebreider script werkt dit natuurlijk beter. Maar het idee is zo simpel als dit.

voorkom een spaghetti historie in git

Wanneer je met meerdere aan een project werkt wil de historie van git nog wel eens een spaghetti worden. Met alle merge’s is het soms niet meer te volgen hoe de wijzigingen precies ontstaan zijn.

Hier is wat aan te doen door een iets andere aanpak van feature- en bugbranches. Maar vooral door het wijzigen van de aanpak van merge’s.

Allereerst moet je ervoor zorgen dat elke wijziging in een eigen branch ontwikkeld wordt. Ook al is het maar 1 commit, direct de master verplaatsen maakt het niet duidelijker.

$ git checkout -b feature-010

Wanneer de feature klaar is moet deze toegevoegd worden aan de master, hier verschilt de aanpak:

$ git checkout feature-010
$ git rebase master

Door je commit te rebasen op de master verdwijnt het rare pad wat een wijziging soms ondergaat. Nadeel is wel dat je hiermee ook je commit historie wijzigt, let dus op dat er niemand anders in de branch bezig is of deze als basis heeft gebruikt. Wanneer het puur om een lokale branch gaat is de kans hiervan niet zo groot, maar als de branch al gepushed is kan dat zomaar al gebeurd zijn.

Wanneer er een conflict is tijdens het rebase commando is dat gelijk een mooi moment om dat op te lossen.
Overigens is een rebase niet eens nodig wanneer de master branch niet gewijzigd is sinds je je feature branch begonnen bent.

Nadat de rebase gelukt is kan de feature branch gemerged worden met de master:

$ git checkout master
$ git merge --no-ff feature-010

Hiermee forceer je git om geen fast forward te doen ook al is dat mogelijk. De historie van de wijzigingen is daardoor wat beter te volgen als je naar de graph historie kijkt:

En daarmee is de historie van commits en branches in git opeens een stuk makkelijker te volgen.

git branch achteruit verplaatsen

Het is de meeste wel eens overkomen. Je bent hard aan het werk en hebt niet door dat je in de verkeerde branch zit te werken, die meestal ook nog eens de master blijkt te zijn (je ontwikkelt toch niet in de master branch toch?).
Helemaal mooi is het als je dit opmerkt direct na het commando:

$ git push origin
Counting objects: 16, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 944 bytes, done.
Total 9 (delta 5), reused 0 (delta 0)
To git@git.server:application.git
   01119ef..4ab523e  master -> master

Oops, je hebt net de master remote gewijzigd…

Gelukkig is er nog wat aan te doen. Om je branch lokaal terug te zetten, doe het volgende:

$ git checkout master
$ git reset --hard <commit-waar-je-heen-wil>

Dit verplaatst je master branch naar de opgegeven commit. Hoe je die specifieke commit aanduid maakt niet zoveel uit.

Om de branch remote ook terug te zetten doe je hetvolgende:

$ git checkout master
$ git push --force origin

Wanneer er anderen in dezelfde repository werken moeten ze ook zorgen dat de branch verplaatst wordt. Effectief hoeven ze er alleen maar voor te zorgen dat hun branch weer naar de juiste commit wijst, ze kunnen dat alsvolgt doen:

# update repository informatie
$ git fetch
# verplaats de branch naar waar deze staat in de remote repository
$ git reset --hard origin/master

Hierbij is het natuurlijk noodzakelijk dat er geen gewijzigde bestanden in de locale checkout staan. Dit kan voor problemen zorgen.

Hoewel hier getoond voor de master branch, werkt het uiteraard voor elke andere branch.

Om je wijzigingen niet kwijt te raken tijdens het verplaatsen van de branch is het verstandig om een nieuwe (feature) branch te maken voordat je de master branch terugzet.