Rebase

Rebase: Verzweigung auf neue Basis setzen

Als Alternative für ein merge, kann auch ein rebase1 gemacht werden. Die Unterschiede schauen wir uns jetzt an.

Was ist ein rebase?

Git Rebase ist eine Methode, um die Commit-Historie zu verändern und zu bereinigen. Im Gegensatz zu git merge, der zwei Branches zusammenführt und einen neuen Merge-Commit erstellt, sortiert git rebase die Commits neu und wendet sie in linearer Folge an, was eine sauberere Projekthistorie erzeugt.

Ablauf bei einem rebase

Bei einem Rebase nimmt Git alle Commits aus deinem aktuellen Branch (z.B. feature-branch) und wendet sie erneut auf einen anderen Branch (z.B. main) an. Dies geschieht wie folgt:

  1. Git identifiziert den gemeinsamen Vorfahren-Commit beider Branches
  2. Git speichert temporäre Kopien der Änderungen jedes Commits im Feature-Branch
  3. Der Feature-Branch wird auf den Ziel-Branch (main) zurückgesetzt
  4. Die gespeicherten Änderungen werden einzeln als neue Commits auf den Ziel-Branch angewendet (die Historie ändert sich damit)

Git merge graph
Graph für git merge

Git branch graph
Git Graph vor dem Rebase

Git branch graph after rebase
Git Graph nach dem Rebase

Einen rebase durchführen

Um en rebase zu erstellen, sollten wir vorher Änderungen auf zwei Verzweigungen erstellen, so dass ein merge nicht mehr durch ein “fast forward” durchgeführt werden kann.

Erstellen wir für Rebase zuerst einen neuen Branch.

repo_1$ git checkout -b feat_kap_7
Switched to a new branch 'feat_kap_7'

Erstellen Sie nun eine neue Daten kapitel_7.txt mit folgenden Inhalt und fügen Sie diese Datei zur Versionsverwaltung hinzu.

Fügen Sie folgenden Inhalt der Datei hinzu und schreiben Sie die Version fest.

# Kapitel 7
Der Ausgang der Geschichte wird angepasst ...

Wechseln Sie nun zurück zu dem main Branch, um auch hier einige Änderungen vorzunehmen.

repo_1$ git checkout main
Switched to branch 'main'

Machen Sie nun folgende Änderungen in der Datei kapitel_6.txt.

# Kapitel 6
Und es wurde endlich besser...

Die Historie sieht nun wie folgt aus (gekürzt):

repo_1$ git pl
* cf10f7f (HEAD -> main) Besser
| * 67f77fa (feat_kap_7) Neue Geschichte
| * 9a14335 Start mit Kapitel 7
|/  
*   4a8d01e Merge branch 'feat_kap_5'
|\  
| * 654fae0 Kapitel 4 überarbeitet
| * a7d8ad7 Kapitel 1 überarbeitet
| * 602f965 Fahrt im Zug
* | a915c82 Kapitel 6 gestartet
* | 4893d66 Kapitel 4 überarbeitet
* | 401162d Kapitel 1 ausgearbeitet
|/  
*   ae2918a Merge branch 'develop'

An den Graphen sehen wir, dass hier ein Merge zu keinem “Fast Merge” führen würde (keine lineare Historie), sondern einen “Merge”-Commit (wie bei der Zusammenführung des feat_kap_5 Branches). Das können wir nun mit einen Rebase verhindern. Wechseln Sie zuerst auf den feat_kap_7 branch. Dieser soll nun neue “Basis” bekommen.

repo_1$ git checkout feat_kap_7
Switched to branch 'feat_kap_7'

Nun führen wir ein Rebase auf diesem Branch durch. D.h. der Branch bekommt eine neue Basis. Aktuell liegt diese auf dem Commit Merge branch 'feat_kap_5'.

git rebase main
Successfully rebased and updated refs/heads/feat_kap_7.

In dem Graphen sehen wir nun, dass die Basis sich von Merge branch 'feat_kap_5' zu Besser verschoben hat.

repo_1$ git pl
* 874e90c (HEAD -> feat_kap_7) Neue Geschichte
* 71c03a0 Start mit Kapitel 7
* cf10f7f (main) Besser
*   4a8d01e (origin/main) Merge branch 'feat_kap_5'
|\  
| * 654fae0 Kapitel 4 überarbeitet
| * a7d8ad7 Kapitel 1 überarbeitet
| * 602f965 Fahrt im Zug
* | a915c82 Kapitel 6 gestartet
* | 4893d66 Kapitel 4 überarbeitet
* | 401162d Kapitel 1 ausgearbeitet
|/  
*   ae2918a Merge branch 'develop'

Mit diesen Graphen können wir den Branch feat_kap_7 relativ einfach über einen “Fast Merge” in unser main Branch integrieren.

repo_1$ git checkout main
Switched to branch 'main'
repo_1$ git merge feat_kap_7
Updating cf10f7f..874e90c
Fast-forward
 kapitel_7.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 kapitel_7.txt
repo_1$ git branch -d feat_kap_7
Deleted branch feat_kap_7 (was 874e90c).

Konflikte beim rebase lösen

Genau wie beim merge kann es auch bei rebase zu Konflikten führen, die gelöst werden müssen. Im Gegensatz zu merge können die Konflikte mehrmals vorkommen, praktisch bei jeder “Wiedergabe” (replay) eines Commits auf die neue Basis. Dabei stoppt das Rebase und erlaubt uns die Konflikte zu lösen (ähnlich wie bei merge).

  • Mit git add markieren wir eine Datei mit dem Konflikt, dass der Konflikt beseitigt ist.
  • Mit git rebase --continue setzen wir das Rebase fort (wiedergeben den nächsten Commit auf die neue Basis).
  • Mit git rebase --abort können wir das Rebase abbrechen (ähnlich wie beim merge während eines Konfliktes).
Warnung

Da ein rebase die Historie verändert, sollte dieser nur auf den Commits durchgeführt werden, die noch nicht zu einem Zentralen git-Server synchronisiert wurden (oder nur auf Branches auf denen man allein arbeitet).

docs