Kapitel ▾ 2. Auflage

7.12 Git Tools - Bündeln

Bündeln

Obwohl wir die gängigen Methoden zum Übertragen von Git-Daten über ein Netzwerk (HTTP, SSH usw.) behandelt haben, gibt es tatsächlich noch eine weitere Methode, die nicht häufig verwendet wird, aber tatsächlich sehr nützlich sein kann.

Git ist in der Lage, seine Daten in eine einzige Datei zu „bündeln“. Dies kann in verschiedenen Szenarien nützlich sein. Vielleicht ist Ihr Netzwerk ausgefallen und Sie möchten Änderungen an Ihre Kollegen senden. Vielleicht arbeiten Sie auswärts und haben aus Sicherheitsgründen keinen Zugriff auf das lokale Netzwerk. Vielleicht ist Ihre Wireless/Ethernet-Karte gerade kaputtgegangen. Vielleicht haben Sie im Moment keinen Zugriff auf einen gemeinsamen Server, möchten jemandem Updates per E-Mail senden und möchten nicht 40 Commits über format-patch übertragen.

Hier kann der Befehl git bundle hilfreich sein. Der Befehl bundle packt alles, was normalerweise mit einem git push Befehl über die Leitung gesendet würde, in eine Binärdatei, die Sie per E-Mail an jemanden senden oder auf einen USB-Stick kopieren und dann in ein anderes Repository entpacken können.

Sehen wir uns ein einfaches Beispiel an. Nehmen wir an, Sie haben ein Repository mit zwei Commits

$ git log
commit 9a466c572fe88b195efd356c3f2bbeccdb504102
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:10 2010 -0800

    Second commit

commit b1ec3248f39900d2a406049d762aa68e9641be25
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:01 2010 -0800

    First commit

Wenn Sie dieses Repository an jemanden senden möchten und keinen Zugriff auf ein Repository haben, auf das Sie pushen können, oder einfach keines einrichten möchten, können Sie es mit git bundle create bündeln.

$ git bundle create repo.bundle HEAD master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 441 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)

Jetzt haben Sie eine Datei namens repo.bundle, die alle Daten enthält, die zum Neuerstellen des master-Branches des Repositorys benötigt werden. Mit dem Befehl bundle müssen Sie jeden Verweis oder einen bestimmten Commit-Bereich auflisten, der enthalten sein soll. Wenn Sie beabsichtigen, dies woanders zu klonen, sollten Sie auch HEAD als Verweis hinzufügen, so wie wir es hier getan haben.

Sie können diese Datei repo.bundle per E-Mail an jemand anderen senden oder sie auf einen USB-Stick kopieren und ihn persönlich überbringen.

Auf der anderen Seite, nehmen wir an, Ihnen wird diese Datei repo.bundle zugesandt und Sie möchten an dem Projekt arbeiten. Sie können aus der Binärdatei in ein Verzeichnis klonen, ähnlich wie Sie es von einer URL tun würden.

$ git clone repo.bundle repo
Cloning into 'repo'...
...
$ cd repo
$ git log --oneline
9a466c5 Second commit
b1ec324 First commit

Wenn Sie HEAD nicht in die Verweise aufnehmen, müssen Sie auch -b master oder einen anderen enthaltenen Branch angeben, da Git sonst nicht weiß, welchen Branch es auschecken soll.

Nehmen wir nun an, Sie führen drei Commits durch und möchten die neuen Commits über ein Bundle auf einem USB-Stick oder per E-Mail senden.

$ git log --oneline
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo
9a466c5 Second commit
b1ec324 First commit

Zuerst müssen wir den Commit-Bereich ermitteln, den wir in das Bundle aufnehmen möchten. Im Gegensatz zu den Netzwerkprotokollen, die für uns den minimalen Datensatz für die Übertragung über das Netzwerk ermitteln, müssen wir dies manuell ermitteln. Nun könnten Sie genauso vorgehen und das gesamte Repository bündeln, was funktionieren wird, aber es ist besser, nur die Differenz zu bündeln – nur die drei Commits, die wir gerade lokal gemacht haben.

Dazu müssen Sie die Differenz berechnen. Wie bereits in Commit Ranges beschrieben, können Sie einen Commit-Bereich auf verschiedene Weise angeben. Um die drei Commits zu erhalten, die wir in unserem master-Branch haben und die nicht im Branch enthalten waren, den wir ursprünglich geklont haben, können wir etwas wie origin/master..master oder master ^origin/master verwenden. Sie können dies mit dem Befehl log testen.

$ git log --oneline master ^origin/master
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo

Nachdem wir nun die Liste der Commits haben, die wir in das Bundle aufnehmen möchten, bündeln wir sie. Dies geschieht mit dem Befehl git bundle create, wobei wir den Dateinamen, den wir für unser Bundle wünschen, und den Commit-Bereich, den wir hineinpacken möchten, angeben.

$ git bundle create commits.bundle master ^9a466c5
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 775 bytes, done.
Total 9 (delta 0), reused 0 (delta 0)

Jetzt haben wir eine Datei commits.bundle in unserem Verzeichnis. Wenn wir diese an unseren Partner senden, kann sie sie in das ursprüngliche Repository importieren, auch wenn inzwischen dort weitere Arbeit geleistet wurde.

Wenn sie das Bundle erhält, kann sie es überprüfen, um zu sehen, was es enthält, bevor sie es in ihr Repository importiert. Der erste Befehl ist bundle verify, der sicherstellt, dass die Datei tatsächlich ein gültiges Git-Bundle ist und dass Sie alle notwendigen Vorfahren haben, um es korrekt wiederherzustellen.

$ git bundle verify ../commits.bundle
The bundle contains 1 ref
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
The bundle requires these 1 ref
9a466c572fe88b195efd356c3f2bbeccdb504102 second commit
../commits.bundle is okay

Wenn der Bundler ein Bundle nur mit den letzten beiden Commits erstellt hätte, anstatt mit allen drei, hätte das ursprüngliche Repository es nicht importieren können, da ihm die erforderliche Historie fehlt. Der Befehl verify hätte stattdessen wie folgt ausgesehen:

$ git bundle verify ../commits-bad.bundle
error: Repository lacks these prerequisite commits:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 Third commit - second repo

Unser erstes Bundle ist jedoch gültig, sodass wir Commits daraus abrufen können. Wenn Sie sehen möchten, welche Branches im Bundle enthalten sind, die importiert werden können, gibt es auch einen Befehl, um nur die Heads aufzulisten:

$ git bundle list-heads ../commits.bundle
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master

Der Unterbefehl verify gibt ebenfalls die Heads aus. Der Punkt ist, zu sehen, was abgerufen werden kann, sodass Sie die Befehle fetch oder pull verwenden können, um Commits aus diesem Bundle zu importieren. Hier rufen wir den master-Branch des Bundles in einen Branch namens other-master in unserem Repository ab:

$ git fetch ../commits.bundle master:other-master
From ../commits.bundle
 * [new branch]      master     -> other-master

Jetzt können wir sehen, dass wir die importierten Commits auf dem Branch other-master haben, sowie alle Commits, die wir inzwischen auf unserem eigenen master-Branch gemacht haben.

$ git log --oneline --decorate --graph --all
* 8255d41 (HEAD, master) Third commit - first repo
| * 71b84da (other-master) Last commit - second repo
| * c99cf5b Fourth commit - second repo
| * 7011d3d Third commit - second repo
|/
* 9a466c5 Second commit
* b1ec324 First commit

git bundle kann also sehr nützlich sein, um Netzwerk-ähnliche Operationen auszutauschen oder durchzuführen, wenn Sie nicht über das richtige Netzwerk oder ein gemeinsames Repository verfügen.