Skip to content

Latest commit

 

History

History
191 lines (145 loc) · 9.6 KB

nutshell.asc

File metadata and controls

191 lines (145 loc) · 9.6 KB

Branches in vogelvlucht

Om de manier waarop Git brancht echt te begrijpen, moeten we een stap terug doen en onderzoeken hoe Git zijn gegevens opslaat.

Zoals je je misschien herinnert van ch01-getting-started.asc, slaat Git zijn gegevens niet op als een reeks van wijzigingen of delta’s, maar in plaats daarvan als een serie snapshots.

Als je in Git commit, dan slaat Git een commit object op dat een verwijzing bevat naar het snapshot van de inhoud die je gestaged hebt. Dit object bevat ook de naam en mailadres van de auteur, het merge bericht dat ingetypt was, verwijzingen naar de commit of commits die de directe ouders van deze commit waren: nul ouders voor de eerste commit, één ouder voor een normale commit, en meerdere ouders voor een commit die het resultaat is van een merge van twee of meer branches.

Om dit te visualiseren, gaan we aannemen dat je een directory hebt met drie bestanden, en je staget en commit ze allemaal. Door de bestanden te stagen krijgen ze allemaal een checksum (de SHA-1 hash waar we het in ch01-getting-started.asc over hadden), worden die versies de bestanden in het Git repository (Git noemt ze blobs) opgeslagen, en worden die checksums aan de staging area toegevoegd:

$ git add README test.rb LICENSE
$ git commit -m 'initial commit of my project'

Als je de commit aanmaakt door git commit uit te voeren zal Git iedere directory in het project (in dit geval alleen de root) van een checksum voorzien en slaat ze als boomstructuur (tree) objecten in de Git repository op. Daarna creëert Git een commit object dat de metagegevens bevat en een verwijzing naar de hoofd-tree-object van het project zodat Git deze snapshot opnieuw kan oproepen als dat nodig is.

Je Git repository bevat nu vijf objecten: drie blobs (een blob voor de inhoud van ieder van de drie bestanden), een tree die de inhoud van de directory weergeeft en specificeert welke bestandsnamen opgeslagen zijn als welke blob, en een commit met de verwijzing naar die hoofd-tree en alle commit-metagegevens.

Een commit en zijn tree.
Figure 1. Een commit en zijn tree

Als je wat wijzigingen maakt en nogmaals commit, dan slaat de volgende commit een verwijzing op naar de commit die er direct aan vooraf ging.

Commits en hun ouders.
Figure 2. Commits en hun ouders

Een branch in Git is simpelweg een lichtgewicht verplaatsbare verwijzing naar een van deze commits. De standaard branch-naam in Git is master. Als je commits begint te maken, krijg je een master-branch die wijst naar de laatste commit die je gemaakt hebt. Iedere keer als je commit, beweegt de pointer van de master-branch automatisch vooruit.

Note

De `master'' branch in Git is geen speciale branch. Het is gelijk aan elke andere branch. De enige reden waarom vrijwel elke repository er een heeft is dat de `git init commando er standaard een maakt en de meeste mensen geen moeite doen om deze te wijzigen.

Een branch en zijn commit-historie.
Figure 3. Een branch en zijn commit-historie

Een nieuwe branch maken

Wat gebeurt er als je een nieuwe branch maakt? Nou, het aanmaken zorgt ervoor dat er een nieuwe verwijzing (pointer) voor je wordt gemaakt die je heen en weer kan bewegen. Stel dat je een nieuwe branch maakt en die testing noemt. Je doet dit met het git branch commando:

$ git branch testing

Dit maakt een nieuwe pointer op dezelfde commit als waar je nu op staat.

Twee branches die naar de zelfde reeks van commits verwijzen.
Figure 4. Twee branches die naar de zelfde reeks van commits verwijzen

Hoe weet Git op welke branch je nu zit? Het houdt een speciale verwijzing bij genaamd HEAD. Let op dat dit heel anders is dan het concept van HEAD in andere VCS’s waar je misschien gewend aan bent, zoals Subversion of CVS. In Git is dit een verwijzing naar de lokale branch waar je nu op zit. In dit geval zit je nog steeds op master. Het git branch-commando heeft alleen een nieuwe branch aangemaakt - we zijn nog niet overgeschakeld naar die branch.

HEAD verwijzend naar een branch.
Figure 5. HEAD verwijzend naar een branch

Je kunt dit simpelweg zien door een eenvoudige git log commando uit te voeren wat je laat zien waar de branch pointers naar verwijzen. Deze optie heet --decorate.

$ git log --oneline --decorate
f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface
34ac2 Fixed bug #1328 - stack overflow under certain conditions
98ca9 The initial commit of my project

Je kunt de master'' en testing'' branches zien die direct naast de f30ab commit staan.

Tussen branches schakelen (switching)

Om te schakelen (switch) naar een bestaande branch, kan je het git checkout commando gebruiken. Laten we eens switchen naar de nieuwe testing-branch:

$ git checkout testing

Dit vertelt HEAD om te verwijzen naar de testing-branch.

HEAD verwijst naar de huidige branch.
Figure 6. HEAD verwijst naar de huidige branch

Wat is daar nu belangrijk aan? Nou, laten we nog eens een commit doen:

$ vim test.rb
$ git commit -a -m 'made a change'
De HEAD branch beweegt voorwaarts als een commit wordt gedaan.
Figure 7. De HEAD branch beweegt voorwaarts als een commit wordt gedaan

Dit is interessant: omdat je testing-branch nu naar voren is bewogen, maar je master branch nog steeds op het punt staat waar je was toen je git checkout uitvoerde om van branch te switchen. Laten we eens terug switchen naar de master-branch:

$ git checkout master
HEAD verplaatst als je checkout uitvoert.
Figure 8. HEAD verplaatst als je checkout uitvoert

Dat commando deed twee dingen. Het verplaatste de HEAD pointer terug om te verwijzen naar de master-branch, en het draaide de bestanden terug in je werk directory naar de stand van de snapshot waar master naar verwijst. Dit betekent ook dat de wijzigingen die je vanaf nu maakt zullen afwijken van een oudere versie van het project. In essentie draait dit het werk terug dat je in je testing-branch gedaan hebt zodat je in een andere richting kunt bewegen.

Note
Branches switchen wijzigt bestanden in je directory

Het is belangrijk op te merken dat wanneer je tussen branches switcht in Git, bestanden in je werk directory zullen wijzigen. Als je naar een oudere branch switcht, zal je werk directory teruggedraaid worden zodat de inhoud gelijk is aan hoehet eruit zag toen je voor het laatst committe op die branch. Als Git dat niet op een nette manier kan doen, zal het je niet laten switchen.

Laten we een paar wijzigingen aanbrengen en opnieuw committen:

$ vim test.rb
$ git commit -a -m 'made other changes'

Nu is je project historie uiteengelopen (zie Uiteengelopen histories). Je hebt een branch gemaakt en bent er naartoe overgeschakeld, hebt er wat werk op gedaan, en bent toen teruggeschakeld naar je hoofd-branch en hebt nog wat ander werk gedaan. Al die veranderingen zijn geïsoleerd van elkaar in aparte branches: je kunt heen en weer schakelen tussen de branches en ze mergen als je klaar bent. En je hebt dat alles gedaan met eenvoudige branch, checkout en commit commando’s.

Uiteengelopen histories.
Figure 9. Uiteengelopen histories

Je kunt dit ook eenvoudig zien met het git log commando. Als je git log --oneline --decorate --graph --all uitvoert zal het de historie van jouw commits afdrukken, laten zien waar jouw branch pointers zijn en hoe je historie uiteengelopen is.

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

Omdat een branch in Git in werkelijkheid een eenvoudig bestand is dat de SHA-1 checksum van 40 karakters bevat van de commit waar het naar verwijst, zijn branches goedkoop om te maken en te verwijderen. Een nieuwe branch maken is net zo snel en simpel te maken als 41 bytes naar een bestand te schrijven (40 karakters en een newline).

Dit is in schril contrast met de manier waarop de meeste oudere VCS applicaties branchen, wat het kopiëren van alle bestanden van het project in een tweede directory inhoudt. Dit kan een aantal seconden of zelfs minuten duren, afhankelijk van de grootte van het project. Dit terwijl bij Git het proces altijd onmiddelijk gereed is. Daarbij komt dat, omdat we de ouders bijhouden als we een commit maken, het vinden van een goede merge basis voor het merge proces automatisch voor ons gedaan is en dit doorgaans eenvoudig te doen is. Deze kenmerken helpen ontwikkelaars aan te moedigen om vaak en veel branches aan te maken.

Laten we eens kijken waarom je dat zou moeten doen.

Note
Het maken van een nieuwe branch en tegelijkertijd ernaar switchen

Het is vrij normaal om een nieuwe branch te maken en dan meteen naar die nieuwe branch te switchen. Dit kan worden gedaan in een operatie met git checkout -b <newbranchname>.