Git: Mit Branches arbeiten (git merge)

Im Zusammenhang mit Branches sieht Git drei zentrale Operationen vor. Nach den ersten beiden Tutorials zum Erstellen von Branches und zum Auschecken von Branches wenden wir uns nun dem Mergen zu. Merging ist in Git der Weg, eine geforkte Historie wieder zusammenzuführen. Der Befehl git merge ermöglicht es uns, die unabhängigen Entwicklungslinien, die wir mit git branch erstellt haben, in einen einzelnen Branch zu integrieren.

Nutzung

git merge <branch>

Mergt den spezifischen Branch in den aktuellen Branch. Git bestimmt den Merge-Algorithmus automatisch, siehe unten.

git merge --no-ff <branch>

Mergt den spezifischen Branch in den aktuellen Branch, generiert aber immer einen Merge-Commit (selbst wenn es ein Fast-forward-Merge war). Das ist hilfreich für die Dokumentation aller Merges, die in einem Repository auftreten.

Hinweise

Wenn wir mit der Entwicklung eines Features in einem isolierten Branch fertig sind, ist es natürlich wichtig, diese Arbeit in die Haupt-Codebasis integrieren zu können. Je nach Repository-Struktur bietet Git verschiedene Algorithmen, um dies umzusetzen: einen Fast-forward-Merge oder einen 3-Way-Merge.

Fast-forward- und 3-Way-Merges
Ein Fast-forward-Merge kann auftreten, wenn es einen linearen Pfad von der Spitze des aktuellen Branchs zum Ziel-Branch gibt. Statt die Branches "tatsächlich" zu mergen, muss Git, um die Historien zu integrieren, nur die Spitze des aktuellen Branchs hoch an die Spitze des Ziel-Branchs bewegen. Dadurch werden die Historien effektiv kombiniert, da alle vom Ziel-Branch erreichbaren Commits nun über den aktuellen Branch erreichbar sind. Beispielsweise würde ein Fast-forward-Merge des Branchs some-feature in den master in etwa so aussehen:

git merge vor dem Mergen

Vor dem Mergen

git merge Fast Forward Merge

Nach einem Fast-forward-Merge

Allerdings ist ein Fast-forward-Merge nicht möglich, wenn die Branches divergiert sind. Gibt es keinen linearen Pfad zum Ziel-Branch, hat Git keine andere Wahl, als die Branches per 3-Way-Merge zu kombinieren. Ein solcher Merge nutzt einen dedizierten Commit, um die beiden Historien zu vereinen. Die Nomenklatur beruht auf dem Fakt, dass Git drei Commits nutzt, um den Merge-Commit zu generieren: die beiden Branch-Spitzen und ihren gemeinsamen "Vorfahren".

git merge vor dem Mergen 2

Vor dem Mergen

git merge 3-Way-Merge

Nach einem 3-Way-Merge

Viele Entwickler bevorzugen Fast-forward-Merges (erleichtert durch Rebasing) für kleine Features oder Bugfixes und reservieren 3-Way-Merges für die Integration großer, längerfristiger Features. Hier ist der resultierende Merge auch so etwas wie eine symbolische Zusammenführung der beiden Branches.

Konflikte lösen
Wenn die beiden Branches, die wir zu mergen versuchen, denselben Teil derselben Datei verändert haben, ist Git nicht fähig herauszufinden, welche Version genutzt werden soll. Wenn eine solche Situation eintritt, stoppt Git direkt vor dem Merge-Commit, sodass wir die Konflikte manuell lösen können.

Das Schöne am Merging-Prozess in Git ist, dass es den vertrauten Edit/Stage/Commit-Workflow nutzt, um Merge-Konflikte zu beheben. Wenn wir auf einen Merge-Konflikt stoßen, zeigt uns git status, welche Dateien behandelt werden müssen. Wenn beispielsweise beide Branches denselben Bereich in hello.py modifiziert haben, würden wir etwa die folgende Ausgabe sehen:

# On branch master
# Unmerged paths:
# (use "git add/rm ..." as appropriate to mark resolution)
#
# both modified: hello.py
#

Dann können wir den Merge nach Belieben in Ordnung bringen. Ist das geschehen, führen wir git add für die Konfliktdatei(en) aus, um Git zu sagen, dass der Konflikt behoben ist. Anschließend generiert ein normales git commit den Merge-Commit. Das ist exakt derselbe Prozess wie beim Committen eines gewöhnliches Snapshots; entsprechend einfach ist es für Entwickler, ihre eigenen Merges zu managen.

Beispiele

Fast-forward-Merge
Unser erstes Beispiel demonstriert einen Fast-forward-Merge. Mit dem Code unten erstellen wir einen neuen Branch, fügen ihm zwei Commits hinzu und integrieren ihn schließlich in die Haupt-Codelinie. Danach löschen wir den Branch new-feature.

# Start a new feature
git checkout -b new-feature master

# Edit some files
git add <file>
git commit -m "Start a feature"

# Edit some files
git add <file>
git commit -m "Finish a feature"

# Merge in the new-feature branch
git checkout master
git merge new-feature
git branch -d new-feature

Das ist ein gebräuchlicher Workflow für kurzlebige Branches, die wir eher für eine isolierte Entwicklung nutzen und nicht als Organisationswerkzeuge für langfristige Features. (Hinweis: Über git branch -d sollte Git sich nicht beschweren, da new-feature nun vom master-Branch zugänglich ist.)

3-Way-Merge
Das nächste Beispiel ist recht ähnlich, erfordert aber einen 3-Way-Merge, da der master voranschreitet, während das Feature in Arbeit ist. Dieses Szenario tritt häufig auf, wenn diverse Entwickler gleichzeitig an einem Projekt arbeiten.

# Start a new feature
git checkout -b new-feature master

# Edit some files
git add <file>
git commit -m "Start a feature"

# Edit some files
git add <file>
git commit -m "Finish a feature"

# Develop the master branch
git checkout master

# Edit some files
git add <file>
git commit -m "Make some super-stable changes to master"

# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature

Git kann hier also keinen Fast-forward-Merge durchführen. Normalerweise dürfte new-feature ein viel größeres Feature sein, das lange Zeit in Entwicklung war, weshalb in der Zwischenzeit neue Commits in den master vorgenommen wurden. (Wenn unser Feature-Branch aber tatsächlich so klein wie im obigen Beispiel ist, würden wir mit einem Rebasing auf den master und einem anschließenden Fast-forward-Merge wahrscheinlich besser fahren. So könnten wir überflüssige Merge-Commits vermeiden, die die Projekthistorie zumüllen.)

In demnächst folgenden Tutorials werden wir uns dann mit erweiterten Fragen und Funktionen beschäftigen.

Weiterführende Infos: Ihr Partner für Git und Stash

Kennen Sie Stash, Atlassians Git-Repository-Managementsystem? Stash bietet eine zentrale Lösung zum Management des gesamten distributierten Codes: Hier kommen alle Git-Repositories im Unternehmen zusammen, hier finden Entwickler immer die letzte offizielle Version eines Projekts, hier können Projektverantwortliche Berechtigungen kontrollieren, um sicherzustellen, dass die richtigen Nutzer Zugriff auf den richtigen Code haben.

Möchten Sie mehr erfahren? Wir sind offizieller Vertriebspartner von Atlassian und einer der größten Atlassian Experts Partner weltweit. Gerne unterstützen wir Sie bei der Evaluierung, Lizenzierung und Adaption von Stash. Und wenn Sie Atlassian-Lizenzen bei //SEIBERT/MEDIA kaufen, gewähren wir Ihnen einen Rabatt in Höhe von 10% der Lizenzkosten in Form von Beratungsleistungen. Bitte sprechen Sie uns einfach an.

99 Argumente für Stash als Git-Repository-Manager
Branch-basierte Git-Workflows mit Stash adaptieren
Echte Integration: Das Zusammenspiel von JIRA, Stash und Bamboo
Interview: Die Vorteile von Git in der Software-Entwicklung und die Möglichkeiten von Stash
So funktioniert die Lizenzierung von Atlassian-Produkten