Git Fast-Forward: Was ist ein Fast-Forward-Merge?
Ein Fast-Forward-Merge ist der einfachste Fall, den Git beim Zusammenführen zweier Branches kennt: Statt einen neuen Merge-Commit zu erzeugen, schiebt Git nur den Branch-Zeiger nach vorne auf den letzten Commit des anderen Branches. Es entsteht kein zusätzlicher Commit, und die Historie bleibt eine gerade Linie. Nach diesem Artikel wissen Sie, wann Git das automatisch tut, wie Sie es mit --ff-only erzwingen oder mit --no-ff verhindern und warum beim git push manchmal ein „non-fast-forward”-Fehler erscheint.
Wann Git einen Fast-Forward macht
Ein Fast-Forward ist genau dann möglich, wenn der Ziel-Branch seit dem Abzweigen keinen eigenen Commit bekommen hat. Der aktuelle Commit des Ziel-Branches liegt dann direkt in der Vorgeschichte des zu mergenden Branches – man sagt, er ist ein „Vorfahre” (ancestor).
Angenommen, Sie zweigen feature von main ab und arbeiten dort, während auf main nichts passiert:
A---B---C feature
/
o---o main (HEAD)
Wenn Sie jetzt feature in main zusammenführen, muss Git nichts kombinieren – es reicht, den main-Zeiger von seinem alten Stand auf C weiterzuschieben:
o---o---A---B---C main, feature
Die passenden Befehle:
git switch main # oder: git checkout main
git merge feature
In der Ausgabe erkennen Sie den Fast-Forward an der Zeile Fast-forward. Es wird kein Editor für eine Merge-Commit-Nachricht geöffnet, weil kein Merge-Commit entsteht.
Praxis-Tipp: Ob ein Merge als Fast-Forward laufen würde, lässt sich vorab prüfen, ohne ihn auszuführen:
git merge-base --is-ancestor main feature && echo "Fast-Forward möglich"
Das ist nützlich in CI-Skripten, die vor einem automatisierten Merge sicherstellen sollen, dass keine unerwartete Merge-Commit-Struktur entsteht. Einen visuellen Überblick über die gesamte Branch-Historie liefert git log --oneline --graph --all --decorate.
Der Gegenfall: divergierende Branches
Sobald beide Branches nach dem Abzweigpunkt eigene Commits haben, ist ein Fast-Forward unmöglich. Die Historie hat sich geteilt:
A---B---C feature
/
o---o---D---E main (HEAD)
Hier kann Git den main-Zeiger nicht einfach nach vorne schieben, ohne die Commits D und E zu verlieren. Stattdessen erzeugt es einen echten Merge-Commit M, der beide Linien wieder zusammenführt (ein sogenannter „3-Wege-Merge”):
A---B---C
/ \
o---o---D---E---M main
Diese Unterscheidung ist der Kern des Themas: Fast-Forward = gerade Linie ohne neuen Commit, echter Merge = Zusammenführung mit Merge-Commit. Verwandt ist der Unterschied zwischen git fetch und git pull, denn git pull führt intern genau so einen Merge (oder Rebase) aus.
Fast-Forward steuern: —ff, —no-ff und —ff-only
Git kennt drei Modi, die Sie an git merge (und teils an git pull) übergeben können:
--ff(Standard): Fast-Forward, wenn möglich; sonst wird ein Merge-Commit erzeugt.--no-ff: Erzwingt immer einen Merge-Commit, auch wenn ein Fast-Forward möglich wäre. So bleibt sichtbar, dass hier ein Feature-Branch eingeflossen ist.--ff-only: Führt den Merge nur aus, wenn er als Fast-Forward möglich ist. Andernfalls bricht Git mit einer Fehlermeldung ab, ohne etwas zu verändern.
git merge --no-ff feature # immer ein Merge-Commit
git merge --ff-only feature # nur wenn kein Merge-Commit nötig ist
--no-ff ist in vielen Teams für das Zusammenführen in main üblich, weil jeder Feature-Branch dadurch als abgegrenzter „Bogen” in der Historie erhalten bleibt – praktisch, um später ein ganzes Feature mit git revert -m 1 <merge-commit> zurückzunehmen. --ff-only ist dagegen die sichere Wahl, wenn Sie garantieren wollen, dass durch das Zusammenführen keine unerwartete Merge-Struktur entsteht.
Dauerhaft festlegen lässt sich das Verhalten über die Konfiguration:
git config --global merge.ff false # entspricht --no-ff als Standard
git config --global merge.ff only # entspricht --ff-only als Standard
git config --global pull.ff only # git pull nur als Fast-Forward
Fast-Forward beim git pull
Seit Git 2.27 (2020) warnt git pull, wenn nicht konfiguriert ist, wie divergierende Branches behandelt werden sollen. Sie legen das einmalig fest:
git config --global pull.ff only # bricht ab, wenn kein FF möglich ist
git config --global pull.rebase true # rebaset lokale Commits stattdessen
pull.ff only ist besonders für Einsteiger empfehlenswert: Statt stillschweigend einen Merge-Commit zu erzeugen, meldet Git, dass Ihr Branch abgewichen ist – und Sie entscheiden bewusst zwischen einem git rebase und einem echten Merge.
Der „non-fast-forward”-Fehler beim Push
Fast-Forward spielt nicht nur beim Zusammenführen eine Rolle, sondern auch beim Hochladen. Ein git push wird vom Server nur akzeptiert, wenn er für den Remote-Branch ein Fast-Forward ist – der Server soll seinen Zeiger nur vorwärts schieben, nie Commits verlieren. Hat jemand anderes in der Zwischenzeit gepusht, sehen Sie:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '...'
hint: Updates were rejected because the tip of your current branch is behind
Die richtige Reaktion ist fast immer: erst die neuen Commits holen und integrieren, dann erneut pushen.
git pull --rebase origin main
git push
Vermeiden Sie in diesem Fall reflexartig git push --force. Ein erzwungener Push überschreibt die Commits der Kollegen auf dem Server. Falls Sie wirklich überschreiben müssen (etwa nach einem bewussten Rebase Ihres eigenen Feature-Branches), nutzen Sie das sicherere git push --force-with-lease, das den Push nur durchlässt, wenn der Remote-Stand seit Ihrem letzten Abgleich unverändert ist. Mehr dazu im Beitrag über den Git Force Push.
Typische Fehler und Lösungen
- „fatal: Not possible to fast-forward, aborting.” – Sie haben
--ff-only(oderpull.ff only) aktiv, aber die Branches sind divergiert. Lösung: bewusstgit merge feature(Merge-Commit) odergit rebaseverwenden. - Unerwarteter Merge-Commit in
main. – Ihrmerge.ffsteht auffalseoder Sie haben--no-ffgetippt. Prüfen Sie die Konfiguration mitgit config merge.ff. - Nach
git pullplötzlich viele fremde Merge-Commits. – Setzen Siepull.ff onlyoderpull.rebase true, damit die Historie linear bleibt.
FAQ
Ist ein Fast-Forward-Merge dasselbe wie ein Rebase?
Nein. Ein Fast-Forward verschiebt nur einen Zeiger und verändert keine Commits. Ein git rebase schreibt Commits neu, indem es sie auf eine andere Basis aufsetzt (mit neuen Commit-Hashes). Beide führen zu einer linearen Historie, aber der Rebase ist der aktivere Eingriff.
Wann sollte ich —no-ff einsetzen?
Immer dann, wenn im Nachhinein erkennbar sein soll, dass mehrere Commits zusammen ein Feature bilden – typischerweise beim Merge eines Feature-Branches in main oder develop. Der Merge-Commit gruppiert die Arbeit und lässt sich als Ganzes zurücknehmen.
Warum lehnt der Server meinen Push als „non-fast-forward” ab?
Weil Ihr lokaler Branch nicht mehr die neueste Version des Remote-Branches enthält – jemand hat inzwischen gepusht. Holen Sie die Änderungen mit git pull --rebase und pushen Sie erneut. Ein --force ist nur in Ausnahmefällen und dann bevorzugt als --force-with-lease angebracht.