Git
Chapters ▾ 2nd Edition

10.3 Git Interna - Git Referenzen

Git Referenzen

Wenn du den Verlauf deines Repositorys sehen möchtest, der über Commit erreichbar ist, z. B. 1a410e, kannst du so etwas wie git log 1a410e ausführen, um diesen Verlauf anzuzeigen. Dennoch musst du sich weiterhin daran erinnern, dass 1a410e der Commit ist den du als Ausgangspunkt für diese Historie verwenden möchtest. Es wäre aber einfacher, wenn du eine Datei hättest, in der du diesen SHA-1-Wert unter einem einfachen Namen speichern kannst, sodass du diesen einfachen Namen anstelle des unformatierten SHA-1-Werts verwenden könntest.

In Git werden diese einfachen Namen „Referenzen“ oder „Refs“ genannt. Du findest die Dateien, die diese SHA-1-Werte enthalten, im Verzeichnis .git/refs. Im aktuellen Projekt enthält dieses Verzeichnis keine Dateien, es enthält eine einfache Struktur:

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

Um eine neue Referenz zu erstellen, die dir hilft, dich zu erinnern, wo sich dein letzter Commit befindet, kannst du einfach folgendes machen:

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

Jetzt kannst du die soeben erstellte Kopfreferenz anstelle des SHA-1-Werts in deinem Git-Befehlen verwenden:

$ git log --pretty=oneline master
1a410efbd13591db07496601ebc7a059dd55cfe9 Third commit
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit

Es wird nicht empfohlen, die Referenzdateien direkt zu bearbeiten. Stattdessen bietet Git den sichereren Befehl git update-ref, um dies zu tun, wenn du eine Referenz aktualisieren möchtest:

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

Das ist im Grunde genommen ein Branch in Git: ein einfacher Zeiger oder ein Verweis auf den Kopf einer Arbeitslinie. So erstellst du eine Verzweigung beim zweiten Commit:

$ git update-ref refs/heads/test cac0ca

Dein Branch enthält nur Arbeiten von diesem Commit an abwärts:

$ git log --pretty=oneline test
cac0cab538b970a37ea1e769cbbde608743bc96d Second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d First commit

Nun sieht deine Git-Datenbank konzeptionell ungefähr so aus:

Git-Verzeichnisobjekte mit Branch Head Referenzen
Abbildung 176. Git-Verzeichnisobjekte mit Branch Head Referenzen

Wenn du Befehle wie git branch <branch> ausführst, führt Git grundsätzlich den Befehl update-ref aus, um den SHA-1 des letzten Commits des Branches, in dem du sich befindest, in die neue Referenz einzufügen, die du erstellen möchtest.

HEAD

Die Frage ist nun, wenn du git branch <branch> ausführst, woher kennt Git den SHA-1 des letzten Commits? Die Antwort ist die HEAD-Datei.

Normalerweise ist die HEAD-Datei ein symbolischer Verweis auf den Branch, in dem du dich gerade befindest. Mit symbolischer Referenz meinen wir, dass sie im Gegensatz zu einer normalen Referenz einen Zeiger auf eine andere Referenz enthält.

In einigen seltenen Fällen kann die HEAD-Datei jedoch den SHA-1-Wert eines Git-Objekts enthalten. Dies geschieht beim Auschecken eines Tags, Commits oder eines Remote-Branches, wodurch dein Repository in den Status "detached HEAD" versetzt wird.

Wenn du dir die Datei ansiehst, siehst du normalerweise Folgendes:

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

Wenn du git checkout test ausführst, aktualisiert Git die Datei folgendermaßen:

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

Wenn du git commit ausführst, wird das Commitobjekt erstellt, wobei das übergeordnete Objekt dieses Commitobjekts als der SHA-1-Wert angegeben wird, auf den die Referenz in HEAD verweist.

Du kannst diese Datei auch manuell bearbeiten, es gibt jedoch wieder einen sichereren Befehl: git symbolic-ref. Du kannst den Wert deines HEAD über diesen Befehl lesen:

$ git symbolic-ref HEAD
refs/heads/master

Du kannst den Wert von HEAD auch mit demselben Befehl festlegen:

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

Du kannst keine symbolische Referenz außerhalb des Refs-Stils festlegen:

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

Tags

Wir haben gerade die drei Hauptobjekttypen von Git (blobs, trees und commits) besprochen, aber es gibt einen vierten. Das tag-Objekt ähnelt stark einem Commitobjekt — es enthält einen Tagger, ein Datum, eine Nachricht und einen Zeiger. Der Hauptunterschied besteht darin, dass ein Tag-Objekt im Allgemeinen eher auf ein Commit als auf einen Baum verweist. Es ist wie eine Branchreferenz, aber es bewegt sich nie — es zeigt immer auf den gleichen Commit, gibt ihm aber einen lesbareren Namen.

Wie in Git Grundlagen beschrieben, gibt es zwei Arten von Tags: Annotierte- und Leichtgewichtige-Tags. Du kannst einen leichtgewichtigen Tag erstellen, indem du Folgendes ausführst:

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

Das ist alles, was ein leichtgewichtiges Tag ist — eine Referenz, die sich nie bewegt. Ein annotiertes Tag ist jedoch komplexer. Wenn du ein annotiertes Tag erstellst, erstellt Git ein Tag-Objekt und schreibt dann einen Verweis, um darauf zu zeigen, anstatt direkt auf den Commit. Du kannst dies sehen, indem du ein annotiertes Tag (mit der Option -a) erstellst:

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

Hier ist der Wert für das Objekt SHA-1, das erstellt wurde:

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

Führe nun git cat-file -p für diesen SHA-1-Wert aus:

$ 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

Du kannst sehen, dass der Objekteintrag auf den Commit SHA-1-Wert verweist, den du getagged hast. Beachte auch, dass es nicht auf ein Commit verweisen muss. Du kannst jedes Git-Objekt taggen. Beispielsweise hat der Betreuer im Git-Quellcode seinen öffentlichen GPG-Schlüssel als Blob-Objekt hinzugefügt und dann mit Tags versehen. Du kannst den öffentlichen Schlüssel anzeigen, indem du diesen in einem Klon des Git-Repositorys ausführst:

$ git cat-file blob junio-gpg-pub

Das Linux-Kernel-Repository verfügt auch über ein Tag-Objekt, das nicht auf Commits verweist. Das erste erstellte Tag verweist auf den ursprünglichen Baum des Imports des Quellcodes.

Remotes

Der dritte Referenztyp, den du siehst, ist eine Remotereferenz. Wenn du ein Remote hinzufügst und darauf pushst, speichert Git den Wert, den du zuletzt an diesen Remote gesendet hast, für jeden Branch im Verzeichnis refs/remotes. Zum Beispiel kannst du eine Remote mit dem Namen origin hinzufügen und deinen master -Branch darauf 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

Anschließend kannst du in der Datei refs/remotes/origin/master sehen, in welchen master Branch auf dem origin Remote du das letzte Mal mit dem Server kommuniziert hast:

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

Remote Referenzen unterscheiden sich von Branches (refs/heads Referenzen) hauptsächlich darin, dass sie als schreibgeschützt gelten. Du kannst git checkout darauf ausführen, aber HEAD wird nicht symbolisch darauf referenzieren, so dass du es niemals mit einem commit Befehl aktualisieren kannst. Git verwaltet sie als Lesezeichen für den letzten bekannten Status, in dem sich diese Branches auf diesen Servern befinden.

scroll-to-top