Maandelijks archief: november 2012

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.

resultaat script afvangen

Bij het schrijven van bash script gebruik je uiteraard weer andere scripts in je eigen script (je kopieert toch niet elke keer dat zelfde stukje code??).
Om te beoordelen of een “sub”script goed is uitgevoerd moet je naar de exitcode van het script kijken, dit kan door gebruik te maken van de variabele $?.

ls -l
if [ $? -gt 0 ]
then
    # exit code is groter dan 0, dus een fout trad op
else
    # exit code is 0, alles ok
fi

Wat je precies met het resultaat doet is natuurlijk afhankelijk van je script.
Let wel op dat bij elk volgend uitgevoerd commando de variabele opnieuw ingesteld wordt. Ook wordt de uitvoer van het commando niet afgevangen. Dat kan je eventueel ondervangen door hetvolgende te doen:

output=`ls -l`
resultcode=$?

Je hebt nu 2 variabelen. $output bevat de output van het commando, $resultcode bevat de errorcode.

linux bash script interactief of niet?

Scripts onder linux bash kunnen op verschillende manieren gestart worden. De gebruiker kan handmatig het script starten met een druk op de knop. Maar wat vaker gebeurd is dat een script gestart wordt door linux zelf zonder tussenkomst van de gebruiker. Dit zal meestal een geplande cronjob zijn.
Groot verschil hierin is of de gebruiker input kan geven, bij afwezigheid van een gebruiker kan je in een script lang wachten op antwoord. Ook is het soms handiger om minder output te geven in een niet-interactief script. Hoewel foutmeldingen wel prettig zijn is “algemene voortgang” minder relevant, er zit immers niemand naar te kijken.

Uitvinden of het script interactief is is heel simpel:

if [ -z $PS1 ]
then
    # interactief
else
    # niet interactief
fi

wat is de huidige gebruiker in een linux bash script?

Soms heb je een bash script wat uitgevoerd moet worden door root, of juist door een andere specifieke gebruiker. Elke keer opletten of je met de juiste user het script start is iets wat snel misgaat. Dus controleren in het script wordt uitgevoerd met de juiste user is geen overbodige luxe.

Er zijn meerdere manieren om te bepalen wie de huidige user is, het meerst voor de hand ligt het commando `id`:

# toon lijst met informatie
id
### >> uid=0(root) gid=0(root) groups=0(root)

Maar dat is niet echt prettig om te verwerken, gelukkig zijn er parameters.

# toon user id
id -u
### >> 0

# toon primaire group id
id -g
### >> 0

# toon de id's van alle groepen waar de user lid van is
id -G
### >> 0

Als extra parameter kan -n worden meegegeven waarmee niet het id, maar de naam wordt getoond.

Bij het controleren of de huidige user root is is het niet verstandig om te kijken naar de username “root”. De username zou gewijzigd kunnen zijn. Verstandiger is om te kijken naar het userid 0.

Er zijn ook nog alternatieven.
Het commando `whoami` is ook een optie, maar die is beperkter en komt feitelijk neer op het commando `id -un`.

bash script, in welke directory staat dit script?

Bij het schrijven van een bash script in Linux is het soms nodig om te weten wat de directory is waar het script in staat. Relatief gebruik van paden werkt dan niet goed omdat je of het absolute pad moet meegeven als parameter of om dat relatief bepaald wordt aan de hand van de directory waarvandaan het script werd gestart.

Maar hoe kom je daarachter?
Het blijkt heel simpel te bepalen te zijn met de volgende combinatie van commando’s:

readlink -f ${0} | xargs dirname

Let wel op dat dit geen “/” aan het einde van de directory geeft, die moet je eventueel zelf nog toevoegen als je die nodig hebt.

In bash scripts waar ik dit nodig heb zie je vaak de volgende constructie staan:

scriptdir="`readlink -f ${0} | xargs dirname`/"

# ergens verderop
${scriptdir}/../../relatief/script.sh

Hierdoor hoef ik het maar 1x te bepalen en kan ik daarna simpelweg de variabele gebruiken. Qua snelheid zal het niet veel uitmaken, maar leesbaarder is het zeker.

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.

En weer een blog erbij…

Toen was er weer een blog erbij op internet.

Waarom dit blog? Niet zozeer om geweldig interessante artikelen te posten op het internet, zoveel zinnigs hebi k nu ook weer niet te vertellen. Het is meer een geheugensteuntje voor mezelf. De jaartjes beginnen op te lopen dus ook ik moet aan een “notititieblokje”. Om misschien anderen er toch nog blij mee te maken, een publiek notitieblokje.

Wat voor blogs kan je hier verwachten? Vooral technische posts, gerelateerd aan PHP, Git, SubVersion, HTML, CSS, Javascript en wat ik maar tegenkom. Als ik het belangrijk genoeg vind voor mezelf om te onthouden zal het waarschijnlijk hier terecht komen.

Kortom, verwacht je een gezellig blog van al mijn belevenissen en avonturen? Dan zit je verkeerd, niet dat ik daar last van heb natuurlijk…