Git
Chapters ▾ 2nd Edition

10.3 Git Binnenwerk - Git Referenties

Git Referenties

Je kunt iets als git log 1a410e aanroepen om door je hele geschiedenis te laten kijken, maar je moet nog steeds onthouden dat 1a410e de laatste commit is om die geschiedenis te doorlopen om deze objecten tevinden. Je hebt een bestand nodig waar je de SHA-1 waarde kunt bewaren onder een eenvoudige naam, zodat je die verwijzing kunt gebruiken in plaats van de kale SHA-1 waarde.

In Git worden deze “referenties” of “refs” genoemd, en je kunt de bestanden die deze SHA-1 waarden bevatten vinden in de .git/refs directory. In het huidige project, bevat deze directory geen bestanden, maar wat er wel in zit is een simpele structuur:

$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/tags
$ find .git/refs -type f

Om een nieuwe referentie te maken die je gaat helpen onthouden waar je laatste commit is, kan je technisch gezien iets simpels als dit doen:

$ echo "1a410efbd13591db07496601ebc7a059dd55cfe9" > .git/refs/heads/master

Nu kan je de head referentie die je zojuist gemaakt hebt in je Git commando’s gebruiken in plaats van de SHA-1 waarde:

$ git log --pretty=oneline  master
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Het wordt je niet aangeraden om de referentiebestanden direct te bewerken. Git voorziet in een veiliger commando genaamd update-ref als je een referentie wilt bijwerken:

$ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9

Dit is wat een branch in Git eigenlijk is: een eenvoudige verwijzing of referentie naar de head van een bepaalde werkomgeving. Om achteraf een branch te maken naar de tweede commit, kan je dit doen:

$ git update-ref refs/heads/test cac0ca

Je branch zal alleen werk bevatten van die commit en daarvoor:

$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit

Nu ziet je Git database er conceptueel zo ongeveer uit:

Git directory objecten inclusief branch head referenties.
Figure 152. Git directory objecten inclusief branch head referenties.

Als je commando’s aanroept zoals git branch (branchnaam), roept Git feitelijk die update-ref commando aan om de SHA-1 van de laatste commit op de branch waar je op zit te plaatsten in de referentie die je op dat moment wilt aanmaken.

De HEAD

De vraag is nu, als je git branch (branchnaam) aanroept, hoe weet Git de SHA-1 van de laatste commit? Het antwoord is het HEAD bestand.

Het HEAD bestand is een symbolische referentie naar de branch waar je op dit moment op zit. Met symbolische referentie bedoelen we dat, in tegenstelling tot een normale referentie, het over het algemeen geen SHA-1 waarde bevat, maar een verwijzing naar een andere referentie. Als je naar het bestand kijkt, zie je normaalgesproken zoiets als dit:

$ cat .git/HEAD
ref: refs/heads/master

Als je git checkout test aanroept, werkt Git het bestand bij om er zo uit te zien:

$ cat .git/HEAD
ref: refs/heads/test

Als je git commit aanroept, wordt het commit object aangemaakt, waarbij de ouder van dat commit object wordt gespecificeerd door de SHA-1 waarde die staat in de referentie waar HEAD op dat moment naar verwijst.

Je kunt dit bestand ook handmatig bijwerken, maar alweer is er een veiliger comando om dit te doen: symbolic-ref. Je kunt de waarde van je HEAD met dit commando lezen:

$ git symbolic-ref HEAD
refs/heads/master

Je kunt ook de waarde van HEAD opgeven:

$ git symbolic-ref HEAD refs/heads/test
$ cat .git/HEAD
ref: refs/heads/test

Je kunt geen symbolische referentie waarde invullen die niet valt in de refs-stijl:

$ git symbolic-ref HEAD test
fatal: Refusing to point HEAD outside of refs/

Tags

We zijn zojuist geeindigt met het bespreken van de drie hoofd objecttypen van Git, maar er is een vierde. Het tag object lijkt erg op een commit object - het bevat een tagger, een datum, een bericht en een verwijzing. Het belangrijkste verschil is dat een tag object over het algemeen verwijst naar een commit in plaats van een boom. Het lijkt op een branch referentie, maar het zal nooit bewegen - het verwijst altijd naar dezelfde commit, maar geeft het een vriendelijkere naam.

Zoals besproken in Git Basics, zijn er twee soorten tags: geannoteerd en lichtgewicht. Je kunt een lichtgewicht tag aanmaken door iets als het volgende aan te roepen:

$ git update-ref refs/tags/v1.0 cac0cab538b970a37ea1e769cbbde608743bc96d

Dat is alles wat een lichtgewicht tag is - een referentie die nooit zal bewegen. Een geannoteerde tag is echter veel complexer. Als je een geannoteerde tag aanmaakt, maakt Git een tag object en schrijft daarna een referentie die daarnaar verwijst, in plaats van direct te verwijzen naar de commit. Je kunt dit zien door een geannoteerde tag aan te maken (-a geeft aan dat dit een geannoteerde tag is):

$ git tag -a v1.1 1a410efbd13591db07496601ebc7a059dd55cfe9 -m 'test tag'

Hier is de SHA-1 waarde van het object die is aangemaakt:

$ cat .git/refs/tags/v1.1
9585191f37f7b0fb9444f35a9bf50de191beadc2

En roep nu het cat-file commando aan op die SHA-1 waarde;

$ git cat-file -p 9585191f37f7b0fb9444f35a9bf50de191beadc2
object 1a410efbd13591db07496601ebc7a059dd55cfe9
type commit
tag v1.1
tagger Scott Chacon <schacon@gmail.com> Sat May 23 16:48:58 2009 -0700

test tag

Merk op dat vermelding van het object wijst naar de SHA-1 waarde van de commit die je hebt getagged. Merk ook op dat het niet perse hoeft te verwijzen naar een commit; je kunt elke Git object taggen. In de broncode van Git, bijvoorbeeld, heeft de onderhouder hun GPG publieke sleutel als een blob object toegevoegd en toen deze getagged. Je kunt de publieke sleutel bekijken door het volgende aan te roepen in een cloon van de Git repository:

$ git cat-file blob junio-gpg-pub

De Linux kernel repository heeft ook een tag die niet naar een commit wijst - de eerste tag die gemaakt is verwijst naar de initiële tree van de import van de broncode.

Remotes

Het derde type referentie die je zult zien is een remote referentie. Als je een remote toevoegt en ernaar pusht, slaat Git voor elke de waarde die je het laatst naar die remote hebt gepushed in de refs/remotes directory. Bijvoorbeeld, je kunt een remote genaamd origin toevoegen en daar je master branch naar pushen:

$ git remote add origin git@github.com:schacon/simplegit-progit.git
$ git push origin master
Counting objects: 11, done.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (7/7), 716 bytes, done.
Total 7 (delta 2), reused 4 (delta 1)
To git@github.com:schacon/simplegit-progit.git
  a11bef0..ca82a6d  master -> master

Je kunt zien wat de master branch op de origin remote was op met moment dat je voor het laatst met de server hebt gecommuniceerd, door het refs/remotes/origin/master bestand te bekijken:

$ cat .git/refs/remotes/origin/master
ca82a6dff817ec66f44342007202690a93763949

Referenties van remotes verschillen van branches (refs/heads referenties) voornamelijk in het feit dat ze als alleen-lezen worden beschouwd. Je kunt naar een git checkout doen, maar Git zal HEAD nooit naar een laten verwijzen, dus je zult er een kunnen bijwerken met een commit commando. Git gebruikt ze als boekleggers naar de laatst bekende staat van waar deze branches op stonden op die servers.