Git
Chapters ▾ 2nd Edition

10.3 Git Interna - Git Referenzen

Git Referenzen

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

In Git werden diese einfachen Namen „Referenzen“ oder „Refs“ genannt. Sie finden 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 Ihnen hilft, sich zu erinnern, wo sich Ihr letztes Commit befindet, können Sie einfach folgende machen:

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

Jetzt können Sie die soeben erstellte Kopfreferenz anstelle des SHA-1-Werts in Ihren 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 Sie eine Referenz aktualisieren möchten:

$ 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 erstellen Sie eine Verzweigung beim zweiten Commit:

$ git update-ref refs/heads/test cac0ca

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

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

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

Git-Verzeichnisobjekte mit Branch Head Referenzen.
Figure 151. Git-Verzeichnisobjekte mit Branch Head Referenzen.

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

HEAD

Die Frage ist nun, wenn Sie git branch <branch> ausführen, 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 Sie sich gerade befinden. 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 Ihr Repository in den Status "detached HEAD" versetzt wird.

Wenn Sie sich die Datei ansehen, sehen Sie normalerweise Folgendes:

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

Wenn Sie git checkout test ausführen, aktualisiert Git die Datei folgendermaßen:

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

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

Sie können diese Datei auch manuell bearbeiten, es gibt jedoch wieder einen sichereren Befehl: git symbolic-ref. Sie können den Wert Ihres HEAD über diesen Befehl lesen:

$ git symbolic-ref HEAD
refs/heads/master

Sie können den Wert von HEAD auch mit demselben Befehl festlegen:

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

Sie können 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 das gleiche Commit, gibt ihm aber einen lesbareren Namen.

Wie in Git Grundlagen beschrieben, gibt es zwei Arten von Tags: Annotierte- und Leichtgewichtige-Tags. Sie können einen leichtgewichtigen Tag erstellen, indem Sie Folgendes ausführen:

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

Das ist alles, was ein leichtgewichtiger Tag ist — eine Referenz, die sich nie bewegt. Ein annotierter Tag ist jedoch komplexer. Wenn Sie einen annotierten Tag erstellen, erstellt Git ein Tag-Objekt und schreibt dann einen Verweis, um darauf zu zeigen, anstatt direkt auf das Commit. Sie können dies sehen, indem Sie ein annotierten Tag erstellen (mit der Option -a):

$ 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ühren Sie 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

Beachten Sie, dass der Objekteintrag auf den Commit SHA-1-Wert verweist, den Sie getagged haben. Beachten Sie auch, dass es nicht auf ein Commit verweisen muss. Sie können 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. Sie können den öffentlichen Schlüssel anzeigen, indem Sie diesen in einem Klon des Git-Repositorys ausführen:

$ 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 Sie sehen, ist eine Remotereferenz. Wenn Sie ein Remote hinzufügen und darauf pushen, speichert Git den Wert, den Sie zuletzt an diesen Remote gesendet haben, für jeden Zweig im Verzeichnis refs/remotes. Zum Beispiel können Sie eine Remote mit dem Namen origin hinzufügen und Ihren master -Zweig 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 können Sie in der Datei refs/remotes/origin/master sehen, in welcher master Branch auf dem origin-Remote Sie das letzte Mal mit dem Server kommuniziert haben:

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

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