Git
Chapters ▾ 2nd Edition

5.3 Verteiltes Git - Ein Projekt verwalten

Ein Projekt verwalten

Sie müssen nicht nur wissen, wie Sie effektiv zu einem Projekt etwas beitragen. Sie sollten auch wissen, wie Sie ein Projekt verwalten. Sie müssen bspw. wissen wie sie Patches akzeptieren und anwenden, die über format-patch generiert und per E-Mail an Sie gesendet wurden. Weiterhin sollten sie wissen wie sie Änderungen in Remote-Branches für Repositorys integrieren, die Sie als Remotes zu Ihrem Projekt hinzugefügt haben. Unabhängig davon, ob Sie ein zentrales Repository verwalten oder durch Überprüfen oder Genehmigen von Patches helfen möchten, müssen Sie wissen, wie Sie die Arbeit anderer so akzeptieren, dass es für andere Mitwirkende transparent und auf lange Sicht auch nachhaltig ist.

Arbeiten in Themen Branches

Wenn man vorhat, neuen Quelltext zu integrieren, ist es im Allgemeinen eine gute Idee, sie in einem Topic Branch zu testen. Das ist ein temporärer Branch, der speziell zum Ausprobieren dieser neuen Änderungen erstellt wurde. Auf diese Weise ist es einfach, einen Patch einzeln zu optimieren und ihn nicht weiter zu bearbeiten, wenn er nicht funktioniert, bis Sie Zeit haben, sich wieder damit zu befassen. Sie sollten einen einfachen Branchnamen erstellen, der auf dem Thema der Arbeit basiert, die Sie durchführen, wie z.B. ruby_client oder etwas ähnlich Sprechendes. Dann können Sie sich später leichter daran erinnern, falls Sie den Branch für eine Weile haben ruhen lassen und später daran weiter arbeiten. Der Betreuer des Git-Projekts neigt auch dazu, diese Branches mit einem Namespace zu versehen – wie z. B. sc/ruby_client, wobei sc für die Person steht, die die Arbeit beigesteuert hat. Wie Sie sich erinnern werden, können Sie den Branch basierend auf Ihrem master-Branch wie folgt erstellen:

$ git branch sc/ruby_client master

Wenn sie anschließend sofort zum neuen Branch wechseln möchten, können Sie auch die Option checkout -b verwenden:

$ git checkout -b sc/ruby_client master

Jetzt können Sie die getätigte Arbeit zu diesem Branch hinzufügen und festlegen, ob Sie ihn mit Ihren bestehenden Branches zusammenführen möchten.

Integrieren von Änderungen aus E-Mails

Wenn Sie einen Patch per E-Mail erhalten, den Sie in Ihr Projekt integrieren müssen, müssen Sie den Patch in Ihrer Themen Branch einfließen lassen, damit sie ihn prüfen können. Es gibt zwei Möglichkeiten, einen per E-Mail versendeten Patch anzuwenden: mit git apply oder mit git am.

Änderungen mit apply integrieren

Wenn Sie den Patch von jemandem erhalten haben, der ihn mit git diff oder mit einer Variante des Unix-Befehls` diff` erzeugt hat (was nicht empfohlen wird; siehe nächster Abschnitt), können Sie ihn mit dem Befehl git apply integrieren. Angenommen Sie haben den Patch unter /tmp/patch-ruby-client.patch gespeichert. Dann können Sie den Patch folgendermaßen integrieren:

$ git apply /tmp/patch-ruby-client.patch

Hierdurch werden die Dateien in Ihrem Arbeitsverzeichnis geändert. Es ist fast identisch mit dem Ausführen eines patch -p1 Befehls zum Anwenden des Patches, obwohl es vorsichtiger ist und unscharfe Übereinstimmungen selektiver als patch akzeptiert. Damit kann man auch Dateien Hinzufügen, Löschen und Umbenennen, wenn diese im git diff-Format beschrieben sind, was mit patch nicht möglich ist. Zu guter Letzt ist git apply ein „wende alles oder nichts an“ Modell, bei dem entweder alles oder nichts angewendet wird. patch hingegen integriert Patchdateien eventuell nur teilweise und kann Ihr Arbeitsverzeichnis in einem undefinierten Zustand versetzen. git apply ist insgesamt sehr viel konservativer als patch. Es wird kein Commit erstellen. Nach dem Ausführen müssen Sie die eingeführten Änderungen manuell bereitstellen und comitten.

Sie können git apply verwenden, um zu prüfen, ob ein Patch ordnungsgemäß integriert werden kann, bevor Sie versuchen, ihn tatsächlich anzuwenden. Sie können git apply --check auf den Patch ausführen:

$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply

Wenn keine Ausgabe erfolgt, sollte der Patch ordnungsgemäß angewendet werden können. Dieser Befehl wird auch mit einem Rückgabewert ungleich Null beendet, wenn die Prüfung fehlschlägt. So können sie ihn bei Bedarf in Skripten verwenden.

Änderungen mit am integrieren

Wenn der Beitragende ein Git-Benutzer ist und den Befehl format-patch zum Generieren seines Patches verwendet hat, ist Ihre Arbeit einfacher. Der Patch enthält bereits Informationen über den Autor und eine entsprechende Commitnachricht. Wenn möglich, ermutigen Sie die Beitragenden format-patch anstelle von diff zum Erstellen von Patches zu verwenden. Sie sollten git apply nur für ältere Patche und ähnliche Dinge verwenden.

Um einen von format-patch erzeugten Patch zu integrieren, verwenden Sie git am (der Befehl heißt am, da er verwendet wird, um „eine Reihe von Patches aus einer Mailbox anzuwenden“). Technisch gesehen ist git am so aufgebaut, dass eine mbox-Datei gelesen werden kann. Hierbei handelt es sich um ein einfaches Nur-Text-Format zum Speichern einer oder mehrerer E-Mail-Nachrichten in einer Textdatei. Das sieht in etwa so aus:

From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

Dies ist der Anfang der Ausgabe des Befehls git format-patch, den Sie im vorherigen Abschnitt gesehen haben. Es zeigt ein gültiges mbox Email Format. Wenn Ihnen jemand den Patch ordnungsgemäß mit git send-email per E-Mail zugesendet hat und Sie ihn in ein mbox-Format herunterladen, können Sie git am auf diese mbox-Datei ausführen. Damit werden alle angezeigten Patches entsprechend angewendet. Wenn Sie einen Mail-Client ausführen, der mehrere E-Mails im Mbox-Format speichern kann, können Sie ganze Patch-Serien in einer Datei speichern. Diese können anschließend mit git am einzeln angewendet werden.

Wenn jemand eine mit git format-patch erzeugte Patch-Datei in ein Ticketing-System oder ähnliches hochgeladen hat, können Sie die Datei lokal speichern. Die Datei können sie dann an git am übergeben, um sie integrieren:

$ git am 0001-limit-log-function.patch
Applying: add limit to log function

Wie sie können sehen, wurde der Patch korrekt integriert und es wurde automatisch ein neue Commit für Sie erstellt. Die Autoreninformationen werden aus den Kopfzeilen From und Date der E-Mail entnommen und die Commitnachricht wird aus dem Subject und dem Textkörper (vor dem Patch) der E-Mail entnommen. Wenn dieser Patch bspw. aus dem obigen mbox-Beispiel angewendet würde, würde der erzeugte Commit in etwa so aussehen:

$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   add limit to log function

   Limit log functionality to the first 20

Die Commit-Informationen gibt die Person an, die den Patch angewendet hat und den Zeitpunkt, wann er angewendet wurde. Die Author-Information gibt die Person an, die den Patch ursprünglich erstellt hat und wann er ursprünglich erstellt wurde.

Es besteht jedoch die Möglichkeit, dass der Patch nicht sauber angewendet werden kann. Möglicherweise ist Ihr Hauptbranch zu weit vom Branch entfernt, von dem aus der Patch erstellt wurde. Oder aber der Patch hängt noch von einem anderen Patch ab, den Sie noch nicht angewendet haben. In diesem Fall schlägt der Prozess git am fehl und Sie werden gefragt, was Sie tun möchten:

$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Dieser Befehl fügt Konfliktmarkierungen in alle Dateien ein, in denen Probleme auftreten. Ähnlich wie bei einem Konflikt bei der Zusammenführung (engl. merge) bzw. bei der Reorganisation (engl. rebase). Sie lösen dieses Problem auf die gleiche Art: Bearbeiten Sie die Datei, um den Konflikt zu lösen. Anschließend fügen sie die neue Datei der Staging Area hinzu und führen dann git am --resolved aus, um mit dem nächsten Patch fortzufahren:

$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem

Wenn Sie möchten, dass Git den Konflikt etwas intelligenter löst, können Sie ihm die Option „-3“ übergeben, wodurch Git versucht, eine Dreifachzusammenführung durchzuführen. Diese Option ist standardmäßig nicht aktiviert, da sie nicht funktioniert, wenn sich der Commit, auf dem der Patch basiert, nicht in Ihrem Repository befindet. Wenn Sie diesen Commit haben – wenn der Patch auf einem öffentlichen Commit basiert -, ist die Option „-3“ im Allgemeinen viel intelligenter beim Anwenden eines Patch mit Konflikten:

$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.

In diesem Fall wäre der Patch ohne die Option -3 als Konflikt gewertet worden. Da die Option -3 verwendet wurde, konnte der Patch sauber angewendet werden.

Wenn Sie mehrere Patches aus mbox anwenden, können Sie auch den Befehl am im interaktiven Modus ausführen. Bei jedem gefundenen Patch wird angehalten und Sie werden gefragt, ob Sie ihn anwenden möchten:

$ git am -3 -i mbox
Commit Body is:
--------------------------
seeing if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all

Dies ist hilfreich, wenn Sie eine Reihe von Patches gespeichert haben. Sie können sich den Patch zuerst anzeigen lassen, wenn Sie sich nicht daran erinnern, worum es genau geht. Oder sie wenden den Patch nicht an, weil Sie es bereits getan haben.

Wenn alle Patches für Ihr Thema angewendet und in Ihrem Branch committet wurden, können Sie auswählen, ob und wie Sie sie in einen Hauptbranch integrieren möchten.

Remote Branches auschecken

Wenn sie einen Beitrag von einem Git-Nutzer erhalten, der sein eigenes Repository eingerichtet, eine Reihe von Änderungen vorgenommen und Ihnen dann die URL zum Repository und den Namen des Remote-Zweigs gesendet hat, in dem sich die Änderungen befinden, dann können Sie diesen als remote hinzufügen und die Änderungen lokal zusammenführen.

Wenn Jessica Ihnen bspw. eine E-Mail sendet, die besagt, dass sie eine großartige neue Funktion im ruby-client Branch ihres Repositorys hat, können Sie diese testen, indem Sie den Branch als remote hinzufügen und ihn lokal auschecken:

$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

Wenn Jessica Ihnen später erneut eine E-Mail mit einem anderen Branch sendet, der eine weitere großartige Funktion enthält, können Sie diese direkt abrufen und auschecken, da Sie bereits über das Remote Repository verfügen.

Dies ist am nützlichsten, wenn Sie durchgängig mit einer Person arbeiten. Wenn jemand nur selten einen Patch zur Verfügung steht, ist das Akzeptieren über E-Mail möglicherweise weniger zeitaufwendig. Andernfalls müsste jeder seinen eigenen Server unterhalten und Remotes hinzufügen und entfernen, um diese wenige Patches zu erhalten. Es ist auch unwahrscheinlich, dass Sie Hunderte von Remotes einbinden möchten für Personen, die nur ein oder zwei Patches beisteuern. Skripte und gehostete Dienste können dies jedoch vereinfachen – dies hängt weitgehend davon ab, wie Sie und die Mitwirkenden entwickeln.

Der andere Vorteil dieses Ansatzes ist, dass Sie auch die Historie der Commits erhalten. Obwohl Sie möglicherweise berechtigte Probleme bei der Zusammenführungen haben, wissen Sie, wo in Ihrer Historie deren Arbeit basiert. Eine ordnungsgemäße Drei-Wege-Zusammenführung ist die Standardeinstellung, anstatt ein „-3“ einzugeben, und zu hoffen, dass der Patch aus einem öffentlichen Commit generiert wurde, auf den Sie Zugriff haben.

Wenn Sie nicht durchgängig mit einer Person arbeiten, aber dennoch auf diese Weise von dieser Person abrufen möchten, können Sie die URL des Remote-Repositorys für den Befehl git pull angeben. Dies führt einen einmaligen Abruf durch und speichert die URL nicht als Remote-Referenz:

$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by the 'recursive' strategy.

Bestimmen, was eingeführt wird

Sie haben nun einen Themen Branch mit neuen Beiträgen. An dieser Stelle können Sie festlegen, was Sie damit machen möchten. In diesem Abschnitt werden einige Befehle noch einmal behandelt. Mit diesen können sie genau überprüfen, was Sie einführen, wenn Sie die Beiträge in Ihrem Hauptbranch zusammenführen.

Es ist oft hilfreich, eine Überprüfung über alle Commits zu erhalten, die sich in diesem Branch jedoch nicht in Ihrem Masterbranch befinden. Sie können Commits im Masterbranch ausschließen, indem Sie die Option --not vor dem Branchnamen hinzufügen. Dies entspricht dem Format master..contrib, welches wir zuvor verwendet haben. Wenn Ihr Mitarbeiter Ihnen bspw. zwei Patches sendet und Sie einen Branch mit dem Namen contrib erstellen und diese Patches dort anwenden, können Sie Folgendes ausführen:

$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    seeing if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    updated the gemspec to hopefully work better

Denken Sie daran, dass Sie die Option -p an git log übergeben können, um zu sehen, welche Änderungen jeder Commit einführt.

Um einen vollständigen Überblick darüber zu erhalten, was passieren würde, wenn Sie diesen Branch mit einem anderen Branch zusammenführen würden, müssen Sie möglicherweise einen ungewöhnlichen Kniff anwenden, um die richtigen Ergebnisse zu erzielen. Eventuell denken sie daran folgendes auszuführen:

$ git diff master

Dieser Befehl gibt Ihnen den Unterschied zurück, jedoch kann dies irreführend sein. Wenn Ihr Masterbranch vorgerückt ist, seit Sie den Themenbranch daraus erstellt haben, erhalten Sie scheinbar unerwartete Ergebnisse. Dies geschieht, weil Git den Snapshots des letzten Commits des Branches, in dem Sie sich befinden, und den Snapshot des letzten Commits des Branches master direkt miteinander vergleicht. Wenn Sie bspw. eine Zeile in eine Datei im Branch master eingefügt haben, sieht ein direkter Vergleich der Snapshots so aus, als würde der Themen Branch diese Zeile entfernen.

Wenn master ein direkter Vorgänger Ihres Themenbranches ist, ist dies kein Problem. Wenn aber beiden Historien voneinander abweichen, sieht es so aus, als würden Sie alle neuen Inhalte in Ihrem Themenbranch hinzufügen und alles entfernen, was im master Branch eindeutig ist.

Was Sie wirklich sehen möchten, sind die Änderungen, die dem Themenbranch hinzugefügt wurde. Die Arbeit, die Sie hinzufügen, wenn Sie den neuen Branch mit master zusammenführen. Sie tun dies, indem Git das letzte Commit in Ihrem Themen Branch mit dem ersten gemeinsamen Vorgänger aus dem master Branch vergleicht.

Technisch gesehen können Sie dies tun, indem Sie den gemeinsamen Vorgänger explizit herausfinden und dann Ihr diff darauf ausführen:

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

oder kurzgefasst:

$ git diff $(git merge-base contrib master)

Beides ist jedoch nicht besonders praktisch, weshalb Git eine andere Weg für diesen Vorgang bietet: die Drei-Punkt-Syntax (engl. Triple-Dot-Syntax). Im Kontext des Befehls git diff können Sie drei Punkte nach einem anderen Branch einfügen, um ein diff zwischen dem letzten Commit ihres aktuellen Branch und dem gemeinsamen Vorgänger eines anderen Branches zu erstellen:

$ git diff master...contrib

Dieser Befehl zeigt Ihnen nur die Arbeit an, die Ihr aktueller Branch seit dem gemeinsamen Vorgänger mit master eingeführt hat. Dies ist eine sehr nützliche Syntax, die sie sich merken sollten.

Beiträge integrieren

Wenn ihr Themenbranch bereit ist, um in einen Hauptbranch integriert zu werden, lautet die Frage, wie Sie dies tun können. Welchen Workflow möchten Sie verwenden, um Ihr Projekt zu verwalten? Sie haben eine Reihe von Möglichkeiten, daher werden wir einige davon behandeln.

Zusammenführungs Workflow (engl. mergen)

Ein grundlegender Workflow besteht darin, all diese Arbeiten einfach direkt in Ihrem master-Branch zusammenzuführen. In diesem Szenario haben Sie einen master-Branch, der stabilen Code enthält. Wenn Sie in einem Branch arbeiten, von dem Sie glauben, dass Sie ihn abgeschlossen haben, oder von jemand anderem beigesteuert und überprüft haben, führen Sie ihn in Ihrem Hauptbranch zusammen. Löschen sie anschließend diesen gerade zusammengeführten Branch und wiederholen den Vorgang bei Bedarf.

Wenn wir zum Beispiel ein Repository mit zwei Branches namens ruby_client und php_client haben, die wie Historie mit mehreren Topic Branches. aussehen, und wir ruby_client gefolgt von php_client zusammenführen, sieht Ihr Verlauf so aus Status nach einem Themen Branch Merge..

Historie mit mehreren Topic Branches.
Figure 72. Historie mit mehreren Topic Branches.
Status nach einem Themen Branch Merge.
Figure 73. Status nach einem Themen Branch Merge.

Das ist wahrscheinlich der einfachste Workflow. Es kann jedoch zu Problemen können, wenn Sie größere oder stabilere Projekte bearbeiten. Bei diesen müssen mit der Einführung von Änderungen sehr vorsichtig sein.

Wenn Sie ein wichtigeres Projekt haben, möchten Sie möglicherweise einen zweistufigen Merge Prozess verwenden. In diesem Szenario haben Sie zwei lange laufende Branches namens master und develop. Sie legen fest, dass master nur dann aktualisiert wird, wenn eine sehr stabile Version vorhanden ist und der gesamte neue Code in den Branch develop integriert wird. Sie pushen diese beiden Branches regelmäßig in das öffentliche Repository. Jedes Mal, wenn Sie einen neuen Branch zum Zusammenführen haben (Vor einem Themen Branch Merge.), führen Sie ihn in develop (<< merwf_d >>) zusammen. Wenn Sie nun ein Release mit einem Tag versehen, spulen Sie master an die Stelle weiter, an der sich der jetzt stabile develop-Branch befindet (Nach einem Projekt Release.).

Vor einem Themen Branch Merge.
Figure 74. Vor einem Themen Branch Merge.
Nach einem Themen Branch Merge.
Figure 75. Nach einem Themen Branch Merge.
Nach einem Projekt Release.
Figure 76. Nach einem Projekt Release.

Auf diese Weise können Benutzer, die das Repository Ihres Projekts klonen, entweder master oder develop auschecken. Mit master können sie die neueste stabile Version erstellen und somit recht einfache auf dem neuesten Stand bleiben. Oder sie können develop auschecken, welchen den aktuellsten Inhalt enthält. Sie können dieses Konzept auch erweitern, indem Sie einen integrate-Branch einrichten, in dem alle Arbeiten zusammengeführt werden. Wenn die Codebasis auf diesem Branch stabil ist und die Tests erfolgreich sind, können Sie sie zu einem Entwicklungsbranch zusammen führen. Wenn sich dieser dann als stabil erwiesen hat, können sie ihren master-Branch fast-forwarden.

Workflows mit umfangreichen Merges

Das Git-Projekt selber hat vier kontinuierlich laufende Branches: master, next und pu (vorgeschlagene Updates) für neue Arbeiten und maint für Wartungs-Backports. Wenn neue Arbeiten von Mitwirkenden eingereicht werden, werden sie in ähnlicher Weise wie oben beschrieben in Themenbranches im Projektrepository des Betreuers gesammelt (siehe Verwaltung einer komplexen Reihe paralleler Themenbranches.). Zu diesem Zeitpunkt werden die Themen evaluiert, um festzustellen, ob sie korrekt sind und zur Weiterverarbeitung bereit sind oder ob sie Nacharbeit benötigen. Wenn sie korrekt sind, werden sie zu next zusammengeführt, und dieser Branch wird dann gepushed, damit jeder die miteinander integrierten Themen testen kann.

Verwaltung einer komplexen Reihe paralleler Themenbranches.
Figure 77. Verwaltung einer komplexen Reihe paralleler Themenbranches.

Wenn die Themen noch bearbeitet werden müssen, werden sie in pu gemerged. Wenn festgestellt wird, dass sie absolut stabil sind, werden die Themen wieder zu master zusammengeführt. Die Branches next und pu werden dann von master neu aufgebaut. Dies bedeutet, dass master fast immer vorwärts geht, next wird gelegentlich und pu häufiger rebased:

Zusammenführen von Themen Branches in langfristige Integrationsbranches.
Figure 78. Zusammenführen von Themen Branches in langfristige Integrationsbranches.

Wenn ein Branch schließlich zu master zusammengeführt wurde, wird er aus dem Repository entfernt. Das Git-Projekt hat auch einen maint-Branch, der von letzten release geforkt wurde, um für den Fall, dass eine Wartungsversion erforderlich ist, Backport-Patches bereitzustellen. Wenn Sie das Git-Repository also klonen, stehen Ihnen vier Branches zur Verfügung, mit denen Sie das Projekt in verschiedenen Entwicklungsstadien bewerten können, je nachdem, wie aktuell Sie sein möchten oder wie Sie einen Beitrag leisten möchten. Der Betreuer verfügt über einen strukturierten Workflow, der ihm hilft, neue Beiträge zu überprüfen. Der Workflow des Git-Projekts ist sehr speziell. Um dies zu verstehen, können Sie das Git Maintainer’s guide lesen.

Rebasing und Cherry-Picking Workflows

Andere Betreuer bevorzugen es, die Arbeit auf ihrem Masterbranch zu rebasen oder zu cherry-picken, anstatt sie zusammenzuführen, um einen linearen Verlauf beizubehalten. Wenn Sie in einem Themen Branch arbeiten und sich dazu entschlossen haben, ihn zu integrieren, wechseln Sie in diesen Branch und führen den rebase Befehl aus, um die Änderungen auf ihrem master (oder develop-Branch usw.) aufzubauen. Wenn das gut funktioniert, können Sie Ihren master-Branch fast-forwarden, und Sie erhalten eine lineare Projekthistorie.

Eine andere Möglichkeit, die eingeführte Arbeit von einem Branch in einen anderen zu verschieben, besteht darin, sie zu cherry-picken. Ein Cherry-Pick in Git ist wie ein Rebase für ein einzelnes Commit. Es nimmt den Patch, der in einem Commit eingeführt wurde, und versucht ihn erneut auf den Branch anzuwenden, auf dem Sie sich gerade befinden. Dies ist nützlich, wenn Sie eine Reihe von Commits für einen Branch haben und nur eine davon integrieren möchten. Oder aber wenn Sie nur einen Commit für einen Branch haben und es vorziehen, diesen zu cherry-picken, anstatt ein Rebase auszuführen. Angenommen, Sie haben ein Projekt, das folgendermaßen aussieht:

Beispiel Historie vor einem Cherry-Pick.
Figure 79. Beispiel Historie vor einem Cherry-Pick.

Wenn Sie das Commit „e43a6“ in Ihren Master-Branch ziehen möchten, können Sie folgendes ausführen:

$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

Dies zieht die gleiche Änderung nach sich, die in e43a6 eingeführt wurde. Sie erhalten jedoch einen neuen Commit SHA-1-Wert, da das angewendete Datum unterschiedlich ist. Nun sieht die Historie so aus:

Historie nach Cherry-Picken eines Commits auf einen Themen Branch.
Figure 80. Historie nach Cherry-Picken eines Commits auf einen Themen Branch.

Jetzt können Sie Ihren Themen Branch entfernen und die Commits löschen, die Sie nicht einbeziehen wollten.

Rerere

Wenn Sie viel mergen und rebasen oder einen langlebigen Themenbranch pflegen, hat Git eine Funktion namens „rerere“, die nützlich sein kann.

Rerere steht für „reuse recorded resolution“ (deutsch „Aufgezeichnete Lösung wiederverwenden“). Es ist eine Möglichkeit, die manuelle Konfliktlösung zu verkürzen. Wenn rerere aktiviert ist, behält Git eine Reihe von Pre- und Postimages von erfolgreichen Commits bei. Wenn es feststellt, dass ein Konflikt genauso aussieht, wie der, den Sie bereits behoben haben, wird die Korrektur vom letzten Mal verwendet, ohne nochmal nachzufragen. Diese Funktion besteht aus zwei Teilen: einer Konfigurationseinstellung und einem Befehl. Die Konfigurationseinstellung lautet rerere.enabled. Man kann sie in die globale Konfiguration eingeben:

$ git config --global rerere.enabled true

Wenn Sie nun einen merge durchführen, der Konflikte auflöst, wird diese Auflösung im Cache gespeichert, falls Sie sie in Zukunft benötigen.

Bei Bedarf können Sie mit dem rerere Cache interagieren mittels des Befehls git rerere. Wenn der Befehlt ausgeführt wird, überprüft Git seine Lösungsdatenbank und versucht eine Übereinstimmung mit aktuellen Mergekonflikten zu finden und diesen zu lösen (dies geschieht jedoch automatisch, wenn rerere.enabled auf` true` gesetzt ist). Es gibt auch Unterbefehle, um zu sehen, was aufgezeichnet wird, um eine bestimmte Konfliktlösung aus dem Cache zu löschen oder um den gesamten Cache zu löschen. Wir werden uns in Rerere eingehender mit rerere beschäftigen.

Tagging ihres Releases

Wenn Sie sich entschieden haben, ein Release zu erstellen, dann möchten Sie wahrscheinlich einen Tag zuweisen, damit Sie dieses Release in Zukunft jederzeit neu erstellen können. Sie können einen neuen Tag erstellen, wie in Git Grundlagen beschrieben. Wenn Sie den Tag als Betreuer signieren möchten, sieht der Tag möglicherweise folgendermaßen aus:

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

Wenn Sie Ihre Tags signieren, haben Sie möglicherweise das Problem, den öffentlichen PGP-Schlüssel zu verteilen, der zum Signieren Ihrer Tags verwendet wird. Der Betreuer des Git-Projekts hat dieses Problem behoben, indem er seinen öffentlichen Schlüssel als Blob in das Repository aufgenommen und anschließend einen Tag hinzugefügt hat, der direkt auf diesen Inhalt verweist. Um dies zu tun, können Sie herausfinden, welchen Schlüssel Sie möchten, indem Sie gpg --list-keys ausführen:

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]

Anschließend können Sie den Schlüssel direkt in die Git-Datenbank importieren, indem Sie ihn exportieren und diesen über git hash-object weiterleiten. Dadurch wird ein neuer Blob mit diesen Inhalten in Git geschrieben und Sie erhalten den SHA-1 des Blobs zurück:

$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92

Nachdem Sie nun den Inhalt Ihres Schlüssels in Git haben, können Sie einen Tag erstellen, der direkt darauf verweist. Dies tun sie indem Sie den neuen SHA-1-Wert angeben, den Sie mit dem Befehl hash-object erhalten haben:

$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Wenn Sie git push --tags ausführen, wird der maintainer-pgp-pub-Tag für alle freigegeben. Wenn jemand einen Tag verifizieren möchte, kann er Ihren PGP-Schlüssel direkt importieren, indem er den Blob direkt aus der Datenbank zieht und in GPG importiert:

$ git show maintainer-pgp-pub | gpg --import

Mit diesem Schlüssel können sie alle Ihre signierten Tags überprüfen. Wenn Sie der Tag-Nachricht Anweisungen hinzufügen, können Sie dem Endbenutzer mit git show <tag> genauere Anweisungen zur Tag-Überprüfung geben.

Eine Build Nummer generieren

Git verfügt nicht über ansteigende Zahlen wie v123 oder ähnliches für jedes Commit. Wenn sie einen lesbaren Namen für ihren Commit benötigen, dann können Sie für dieses Commit den Befehl git describe ausführen. Als Antwort generiert Git eine Zeichenfolge, die aus dem Namen des jüngsten Tags vor diesem Commit besteht, gefolgt von der Anzahl der Commits seit diesem Tag, gefolgt von einem partiellen SHA-1-Wert des beschriebene Commits (vorangestelltem wird dem Buchstaben „g“ für Git):

$ git describe master
v1.6.2-rc1-20-g8c5b85c

Auf diese Weise können Sie einen Snaphot exportieren oder einen Build erstellen und einen verständlichen Namen vergeben. Wenn Sie Git aus den Quellcode erstellen, der aus dem Git-Repository geklont wurde, erhalten Sie mit git --version etwas, das genauso aussieht. Wenn Sie einen Commit beschreiben, den Sie direkt getaggt haben, erhalten Sie einfach den Tag-Namen.

Standardmäßig erfordert der Befehl git describe mit Anmerkungen versehene Tags (Tags, die mit dem Flag -a oder -s erstellt wurden). Wenn Sie auch leichtgewichtige (nicht mit Anmerkungen versehene) Tags verwenden möchten, fügen Sie dem Befehl die Option --tags hinzu. Sie können diese Zeichenfolge auch als Ziel der Befehle git checkout oder git show verwenden, obwohl sie auf dem abgekürzten SHA-1-Wert am Ende basiert, sodass sie möglicherweise nicht für immer gültig ist. Zum Beispiel hat der Linux-Kernel kürzlich einen Sprung von 8 auf 10 Zeichen gemacht, um die Eindeutigkeit von SHA-1-Objekten zu gewährleisten, sodass ältere Ausgabenamen von git describe ungültig wurden.

Ein Release vorbereiten

Nun möchten Sie einen Build freigeben. Eines der Dinge, die Sie tun möchten, ist ein Archiv des neuesten Schnappschusses Ihres Codes für die armen Seelen zu erstellen, die Git nicht verwenden. Der Befehl dazu lautet git archive:

$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

Wenn jemand dieses Archiv öffnet, erhält er den neuesten Schnappschuss Ihres Projekts in einem Projektverzeichnis. Sie können auch ein zip-Archiv auf die gleiche Weise erstellen, indem Sie jedoch die Option --format=zip an git archive übergeben:

$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

Sie haben jetzt einen schönen Tarball und ein Zip-Archiv Ihrer Projektversion, die Sie auf Ihre Website hochladen oder per E-Mail an andere Personen senden können.

Das Shortlog

Es ist Zeit, eine E-Mail an die Personen Ihre Mailingliste zu senden, die wissen möchten, was in Ihrem Projekt vor sich geht. Mit dem Befehl git shortlog können Sie schnell eine Art Änderungsprotokoll dessen abrufen, was Ihrem Projekt seit Ihrer letzten Veröffentlichung oder ihrer letzte E-Mail hinzugefügt wurde. Es fasst alle Commits in dem von Ihnen angegebenen Bereich zusammen. Im Folgenden finden Sie als Beispiel eine Zusammenfassung aller Commits seit Ihrer letzten Veröffentlichung, sofern Ihre letzte Veröffentlichung den Namen v1.0.1 hat:

$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (6):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2

Sie erhalten eine übersichtliche Zusammenfassung aller Commits seit Version 1.0.1, gruppiert nach Autoren, die Sie per E-Mail an Ihre Mailingliste senden können.