Arbeiten mit Branches

Git Merge

 

In Git kannst du einen geforkten Verlauf per Merging wieder zusammensetzen. Mit dem Befehl git merge kannst du die unabhängigen Entwicklungszweige, die mit git branch erstellt wurden, in einen einzigen Branch integrieren.

Beachte, dass alle folgenden Befehle in den aktuellen Branch gemergt werden. Der aktuelle Branch wird aktualisiert, um den Projektstand des Merges wiederzugeben, doch der Ziel-Branch bleibt davon komplett unberührt. Zur Erinnerung: Der Befehl git merge wird also oft im Zusammenhang mit git checkout zum Auswählen des aktuellen Branches und git branch -d zum Löschen des veralteten Ziel-Branches genutzt.

Wie es funktioniert

Mit git merge werden mehrere Commit-Abfolgen in einen einheitlichen Verlauf zusammengeführt. Vor allem wird git merge genutzt, um zwei Branches zu vereinen. Auf dieses Branch-Merging-Muster konzentrieren sich die folgenden Beispiele. In diesen Fällen sucht git merge zwischen zwei Commit-Pointern, was für gewöhnlich die Branch-Spitzen sind, einen gemeinsamen Basis-Commit. Sobald Git den gemeinsamen Basis-Commit gefunden hat, wird ein neuer "Merge-Commit" erstellt, um die Änderungen jeder Abfolge von Merge-Commits in der Warteschlange zusammenzuführen.

Nehmen wir an, wir haben ein neues Branch-Feature, das auf Basis des Master-Branches erstellt wurde. Wir wollen diesen Feature Branch nun in den Master mergen.

Mit diesem Befehl wird das angegebene Branch-Feature in den aktuellen Branch, den sogenannten Master, gemergt. Git bestimmt den Merging-Algorithmus automatisch (später mehr dazu).

Merge-Commits haben zwei übergeordnete Commits und unterscheiden sich damit von anderen Commits. Git versucht beim Erstellen eines Merge-Commits ganz automatisch, die separaten Verläufe zu mergen. Daten, die in beiden Verläufen geändert wurden, können von Git nicht automatisch zusammengeführt werden. Dieses Szenario führt in Git zu einem Versionskontrollkonflikt und erfordert den Eingriff des Benutzers. 

Vorbereitung auf das Mergen

Damit das Mergen problemlos funktioniert, solltest du vorbereitend einige Schritte durchführen.

Den Merge-Ziel-Branch bestätigen

Führe git status aus, um sicherzustellen, dass der HEAD auch auf den Branch verweist, der wirklich das Ziel des Merge-Prozesses ist. Falls erforderlich kannst du mit git checkout <receiving> zu dem Branch wechseln, der das Merge-Ziel sein soll. In diesem Fall führen wir git checkout master aus.

Neueste Remote-Commits abrufen

Stelle sicher, dass der Merge-Ziel-Branch und der Merging-Branch mit den neuesten Remote-Änderungen aktualisiert wurden. Mit git fetch kannst du die neuesten Remote-Commits pullen. Vergewissere dich nach dem Abrufen der Commits, dass der Master-Branch über die neuesten Updates verfügt, indem du git pull ausführst.

Verschmelzung

Wenn die zuvor beschriebenen Schritte zur Vorbereitung auf das Mergen abgeschlossen sind, kann der Merge-Vorgang beginnen. Führe dazu git merge <branchname> aus, wobei <branchname> der Name des Branches ist, der in den Merge-Ziel-Branch gemergt wird.

Fast-Forward-Merge

Ein Fast-Forward-Merge findet statt, wenn ein linearer Pfad von der Spitze des aktuellen Branches zum Ziel-Branch existiert. Statt die Branches tatsächlich zusammenzuführen, muss Git zur Integration der verschiedenen Verläufe lediglich die Spitze des aktuellen Branches an die Spitze des Ziel-Branches verschieben ("fast-forward"). Dadurch werden die Verläufe miteinander kombiniert und alle Commits aus dem Ziel-Branch sind auch über den aktuellen Branch zugänglich. Ein Fast-Forward-Merge von ein-feature in den Master könnte etwa so aussehen:

Ein Fast-Forward-Merge ist jedoch nicht möglich, wenn die Branches voneinander abweichen. Wenn kein linearer Pfad zum Ziel-Branch existiert, kann Git die Branches nur mithilfe eines 3-Way-Merges zusammenführen. Für 3-Way-Merges gibt es einen speziellen Commit, um die beiden Verläufe miteinander zu verknüpfen. Die Benennung geht darauf zurück, dass Git drei Commits zum Durchführen des Merge-Commits verwendet: für die beiden Branch-Spitzen und ihren gemeinsamen Vorgänger.


Beide Merge-Strategien stehen dir offen. Viele Entwickler bevorzugen jedoch Fast-Forward-Merges (mit Unterstützung von Rebasing) für kleine Features oder Bugfixes und greifen nur zur Integration langlebigerer Features auf 3-Way-Merges zurück. Bei der letzten Strategie dient der entstehende Merge-Commit zum symbolischen Zusammenführen der beiden Branches.

Unser erstes Beispiel zeigt einen Fast-Forward-Merge. Mit dem Code unten werden ein neuer Branch erstellt, zwei Commits zu diesem Branch hinzugefügt und der Branch dann mit einem Fast-Forward-Merge in den Hauptzweig integriert.

# 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

Dieser Workflow ist für kurzlebige Themen-Branches üblich, die weniger als Organisationstool für langlebigere Features als vielmehr zur isolierten Entwicklung genutzt werden.

Außerdem sollte Git bei git branch -d kein Problem melden, da der Zugriff auf neues-feature nun vom Master-Branch aus möglich ist.

Falls du während eines Fast-Forward-Merges zu Dokumentationszwecken einen Merge-Commits durchführen musst, führe git merge mit der Option --no-ff aus.

git merge --no-ff <branch>

Mit diesem Befehl wird der angegebene Branch in den aktuellen Branch gemergt. Dabei wird immer ein Merge-Commit erzeugt (auch bei Fast-Forward-Merges). Das ist nützlich, wenn du alle Merges in deinem Repository dokumentieren willst.

3-Way-Merge

Das nächste Beispiel ist sehr ähnlich. Hier ist jedoch ein 3-Way-Merge erforderlich, weil der Master-Branch fortgeführt und gleichzeitig das Feature bearbeitet wird. Dieses Szenario kommt häufig vor, wenn es um umfangreiche Features geht oder mehrere Entwickler zeitgleich 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

Beachte, dass es für Git unmöglich ist, einen Fast-Forward-Merge durchzuführen, da der Master ohne Zurückverfolgung nicht zu new-feature verschoben werden kann.

In den meisten Workflows wäre new-feature ein viel größeres Feature mit einer langen Entwicklungszeit. Daher würden neue Commits in der Zwischenzeit im Master erscheinen. Wenn dein Feature Branch aber denselben Umfang hat wie im Beispiel oben, würdest du ihn wahrscheinlich besser auf den master rebasen und einen Fast-Forward-Merge durchführen. So vermeidest du, dass unnötige Merge-Commits den Projektverlauf aufblähen.

Konflikte lösen

Wenn du versuchst, zwei Branches zu mergen, und es in beiden Branches Änderungen in demselben Teil derselben Datei gibt, dann weiß Git nicht, welche Version übernommen werden soll. In einer solchen Situation wird der Vorgang vor dem Merge-Commit angehalten und du kannst den Konflikt manuell lösen.

Das Tolle am Merging-Prozess in Git: Mit dem gewohnten Workflow zum Bearbeiten, Stagen und Durchführen von Commits kannst du auch Merge-Konflikte lösen. Führe bei einem Merge-Konflikt einfach den Befehl git status aus und die betroffenen Dateien werden angezeigt. Wenn zum Beispiel in beiden Branches derselbe Abschnitt von hello.py geändert wurde, wird dir in etwa Folgendes angezeigt:

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

Darstellung von Konflikten

Wenn Git während eines Merge-Vorgangs einen Konflikt feststellt, werden in den betroffenen Dateien Anfang und Ende der Inhalte, die in Konflikt stehen, optisch gekennzeichnet. Die optische Kennzeichnung sieht wie folgt aus: <<<<<<<, =======, and >>>>>>>. Es ist hilfreich, während eines Merges nach dieser Kennzeichnung Ausschau zu halten, um mögliche Konflikte zu erkennen.

here is some content not affected by the conflict
<<<<<<< master
this is conflicted text from master
=======
this is conflicted text from feature branch
>>>>>>> feature branch;

Im Allgemeinen handelt es sich bei dem Inhalt vor der Kennzeichnung ======= um den Merge-Ziel-Branch, während der Teil danach der Merging-Branch ist.

Sobald du ausgemacht hast, welche Abschnitte in Konflikt stehen, kannst du den Merge nach deinen Vorstellungen korrigieren. Wenn du den Merge abgeschlossen hast, musst du lediglich den Befehl git add auf die in Konflikt stehende(n) Datei(en) ausführen, um Git zu sagen, dass sie gelöst sind. Anschließend führst du einen normalen git commit aus, um den Merge-Commit zu erzeugen. Der Prozess ist derselbe wie beim Committen eines gewöhnlichen Snapshots. Normale Entwickler können eigene Merges also ganz einfach verwalten.

Beachte, dass es nur bei 3-Way-Merges zu Merge-Konflikten kommt. In Fast-Forward-Merges können keine Änderungskonflikte entstehen. 

Summary

Wir haben hier einen Überblick über den Befehl git merge gegeben. Das Mergen ist bei der Arbeit mit Git ein wesentlicher Prozess. Dabei haben wir die internen Mechanismen hinter dem Merging und die Unterschiede zwischen einem Fast-Forward-Merge und einem echten, sogenannten 3-Way-Merge behandelt. Das sind einige der wichtigsten Punkte:
 

  1. Git merging combines sequences of commits into one unified history of commits.
  2. There are two main ways Git will merge: Fast Forward and Three way
  3. Git can automatically merge commits unless there are changes that conflict in both commit sequences.

In diesem Tutorial wurden unter anderem auch folgende Git-Befehle genutzt oder auf sie verwiesen: git branch, git pull und git fetch. Erfahre mehr über diese Befehle und besuche die jeweiligen Seiten zu diesen Themen. 

Möchtest du Branching ausprobieren?

Sieh dir dieses interaktive Tutorial an.

Jetzt loslegen