Einführung
Git hat zwei grundlegend verschiedene Merge-Ergebnisse: Fast-Forward, bei dem die Spitze eines Branches einfach verschoben wird, und Three-Way, bei dem ein neuer Commit mit zwei Eltern erstellt wird. Die Wahl hängt davon ab, ob die Branches divergiert sind.
Fast-Forward
Wenn die Spitze von main ein Vorfahre von feature ist, ist kein echter Merge nötig. Git rückt einfach main vor:
A---B---C feature
/
main
# nach git merge feature
A---B---C main, feature
Befehl:
git switch main
git merge feature
Um einen echten Merge-Commit zu erzwingen, auch wenn Fast-Forward möglich ist:
git merge --no-ff feature
Three-Way-Merge
Wenn beide Branches divergiert sind, findet Git die Merge-Basis (den jüngsten gemeinsamen Vorfahren) und kombiniert die Änderungen beider Seiten:
A---B---C feature
/
A---D---E main
# nach git merge feature
A---D---E---M main
\ /
B---C feature
Gits Standard-Merge-Strategie seit 2.33 ist ort; davor war es recursive. Beide berechnen einen Three-Way-Merge pro Datei mit der Merge-Basis.
Inspizieren
git merge-base main feature
git log --oneline --graph --all
git show --stat HEAD # Merge-Commit ansehen
Strategien und Optionen
git merge -X ours feature # bei Konflikten unsere Seite bevorzugen
git merge -X theirs feature # ihre Seite bevorzugen
git merge -X ignore-space-change feature
git merge --squash feature # Änderungen ohne Merge-Commit anwenden
--squash erstellt einen einzigen Commit, der alle Änderungen von feature enthält, ohne Eltern-Verknüpfung dorthin. Nützlich für saubere Historie, verliert aber die Detailgenauigkeit pro Commit.
Abbrechen und rückgängig machen
git merge --abort # mitten im Merge zum Vor-Merge-Zustand zurückkehren
git reset --hard ORIG_HEAD # nach abgeschlossenem Merge ihn rückgängig machen
Fast-Forward-Richtlinie konfigurieren
git config --global merge.ff false # immer Merge-Commit erstellen
git config --global merge.ff only # nur Fast-Forward, sonst ablehnen
git config --global pull.ff only # gleiches für Pulls
Merge-Driver und Attribute
Manche Dateien mergen schlecht mit textbasiertem Three-Way (maschinengenerierte Lockfiles, generierte Dokumentation). Konfigurieren Sie einen eigenen Merge-Driver via .gitattributes und git config:
# .gitattributes
package-lock.json merge=ours
Gemfile.lock merge=union
git config merge.ours.driver true
git config merge.union.name "Line union merge"
git config merge.union.driver "git merge-file --union %A %O %B"
Die eingebaute union-Strategie behält beide Seiten; ours wählt immer unsere Version; eigene Driver können beliebige Programme ausführen. Sparsam einsetzen; überraschendes Merge-Verhalten verwirrt Reviewer.
Häufige Fehler
Glauben, Fast-Forward sei irgendwie speziell oder riskant; ist es nicht, es ist nur ein Zeiger-Move. --no-ff bei jedem Merge in einem schnellen Projekt verwenden, was die Historie mit leeren Merge-Commits verschmutzt. -X ours verwenden, wenn Sie --strategy=ours meinten (was die andere Seite vollständig verwirft) oder umgekehrt. Schließlich: Die Merge-Commit-Nachricht nachträglich manuell editieren und die standardisierte "Merge branch 'X'"-Formulierung brechen, auf die Tools sich verlassen; falls Sie editieren müssen, tun Sie es vor git commit.