Skip to main content

Git leicht gemacht: Verlauf mit Rebase ändern – Teil II

Artikel von
Headshot of Or Kamara

Or Kamara

17. Juni 2020

0 Min. Lesezeit

Willkommen zurück zum zweiten Teil unserer Anleitung für die einfache Arbeit mit Git!

Im vorherigen Artikel haben wir die Grundlagen von Rebase sowie zwei häufige Szenarien besprochen, denen wir alle bei der Arbeit mit Git begegnen können. Den ersten Teil finden Sie hier. In diesem Teil sehen wir uns vier weitere häufige Szenarien und einige abschließende Erkenntnisse an.

Legen wir gleich los!

Häufige Szenarien (und wie Sie damit umgehen!) – Teil II

Setzen wir dort fort, wo wir beim letzten Mal aufgehört haben: mit einigen häufigen Szenarien, in denen Sie den Verlauf Ihres Codes ändern möchten, und damit, wie Sie das mit Git erledigen:

1. Einen älteren Commit in zwei aufteilen (edit)

Angenommen, Sie haben gerade bemerkt, dass Ihnen beim Zusammenführen von zwei Commits ein Fehler unterlaufen ist (weitere Informationen finden Sie in Teil I) und Sie möchten den Squash von 17a73c1 in e2952e7 rückgängig machen, damit die neue Datei in einem anderen Commit landet. Führen Sie dazu alle Commits bis zu dem Punkt aus, den Sie bearbeiten möchten, halten Sie an, nehmen Sie die Änderungen vor und fahren Sie fort. Anders gesagt: Verwenden Sie die Option edit.

Wir führen erneut git rebase -i master aus, diesmal jedoch mit der Option edit für den Commit, den wir aufteilen möchten (den zweiten Commit, `6871683`):

pick 682c27f feat: 1st commit
edit 6871683 feat: 2nd commit
pick 1b2f5b1 feat: 3rd commit

Nachdem Sie die Rebase-Ansicht geschlossen haben, sollte Folgendes angezeigt werden:

Stopped at 6871683...  feat: 2nd commit
You can amend the commit now, with

  git commit --amend '-S'

Once you are satisfied with your changes, run

  git rebase --continue

Git hat also im Grunde den ersten und zweiten Commit ausgeführt und angehalten. Jetzt sind wir am Zug und können alle gewünschten Änderungen vornehmen!

So sieht unser Git-Graph derzeit aus:

* 6871683 (HEAD) feat: 2nd commit* 682c27f feat: 1st commit
* 24aa86a (master) Init: Git History

Wie bereits erwähnt, möchten wir den zweiten Commit im Grunde in zwei separate Commits aufteilen, einen pro Datei. Als ersten Schritt setzen wir die Änderungen des zweiten Commits mit git reset --soft HEAD^ zurück. Der Befehl reset ändert den Commit, auf den der Branch-HEAD derzeit verweist. Mit der Option --soft behalten wir den Index und das Arbeitsverzeichnis bei, während alle Dateien, die zwischen dem ursprünglichen HEAD und dem Commit geändert wurden, zum Staging vorgemerkt werden.

Das ist der neue Git-Status. Wie Sie sehen, sind beide Dateien vorgemerkt:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 modified:   b.txt
 new file:   b2.txt

Nehmen wir nun b2.txt mit git restore --staged b2.txt aus der Liste der vorgemerkten Dateien:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 modified:   b.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
 b2.txt

Da nur b.txt als Datei vorgemerkt ist, können wir jetzt den ersten Teil des zweiten Commits erstellen. Anschließend nehmen wir b2.txt ins Staging auf und erstellen den zweiten Teil des zweiten Commits:

❯ git commit -m "feat: 2nd commit - part 1"
❯ git add b2.txt
❯ git commit -m "feat: 2nd commit - part 2"

So sieht unser Git-Graph jetzt aus:

* 96127c6 feat: 2nd commit - part 2
* 93260e5 feat: 2nd commit - part 1
* 9882b2f feat: 1st commit
* 24aa86a (master) Init: Git History

Im letzten Schritt setzen wir den Rebase fort und führen die übrigen Commits aus:

git rebase --continue. Der fertige Git-Graph sollte dann so aussehen:

* 09fe03e (HEAD -> feat/git-history) feat: 3rd commit
* 96127c6 feat: 2nd commit - part 2
* 93260e5 feat: 2nd commit - part 1
* 9882b2f feat: 1st commit
* 24aa86a (master) Init: Git History

2. Alle Dateien in einem bestimmten Commit ändern (edit)

Das nächste Szenario kennen Sie wahrscheinlich alle: Sie haben Ihren Code, sauber auf mehrere Commits verteilt, gepusht – und stellen dann fest, dass Sie vergessen haben, den Linter für Ihren Code auszuführen. Wie bereits erläutert, sollte die Lösung nicht in einem weiteren Commit namens fix linting bestehen. Stattdessen sollten Sie das Linting für jeden einzelnen Commit korrigieren.

Bei einer kleinen Änderung können wir die Option squash verwenden. Gibt es jedoch mehrere Änderungen in mehreren Commits, ist es am besten, unser Linting-Skript einzeln für jeden Commit (oder für die Commits, die wir korrigieren möchten) auszuführen. Dafür können wir wieder die Option edit verwenden, wie im vorherigen Beispiel.

Führen wir erneut git commit rebase -i aus und verwenden dabei die Option edit für den ersten Commit (einen Commit mit mehreren Dateien):

edit 9882b2f feat: 1st commit
pick 93260e5 feat: 2nd commit - part 1
pick 96127c6 feat: 2nd commit - part 2
pick 09fe03e feat: 3rd commit

Wie zuvor hält Git nach dem Ausführen des ersten Commits an:

Stopped at 9882b2f...  feat: 1st commit
You can amend the commit now, with

  git commit --amend '-S'

Once you are satisfied with your changes, run

  git rebase --continue

Jetzt können wir unseren Linter (z. B. npm run lint) ausführen und den Status unseres Repos prüfen:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
 modified:   a.txt
 modified:   a1.txt
 modified:   a2.txt
 modified:   a3.txt
 modified:   a4.txt
 modified:   a5.txt

Wie Sie sehen, hat das Linting-Skript den Inhalt von sechs Dateien (a*.txt) geändert. Jetzt müssen wir nur noch die aktuellen Änderungen übernehmen (indem wir sie zum Staging hinzufügen und committen) und den Rebase fortsetzen:

❯ git add a*
❯ git commit --amend --no-edit
❯ git rebase --continue
Successfully rebased and updated refs/heads/feat/git-history.

Der Git-Graph sollte dann so aussehen:

* 8f404fe (HEAD -> feat/git-history) feat: 3rd commit
* 7e37b6c feat: 2nd commit - part 2
* f71ad8e feat: 2nd commit - part 1
* b50f18f feat: 1st commit
* 24aa86a (master) Init: Git History

3. Sicherstellen, dass alle Tests für jeden Commit bestehen (exec)

Wenn Sie Ihren Code auf mehrere Commits aufteilen, sollten Sie daran denken, dass jeder Commit für sich bestehen sollte. Anders gesagt: Nach dem Zusammenführen mehrerer Commits in einen der Haupt-Branches sollten Sie zu jedem einzelnen zurückspringen können und erwarten dürfen, dass sich der Code kompilieren lässt und alle Tests bestehen.

Wie können Sie das vor dem Zusammenführen der Commits überprüfen?

Führen wir unsere Tests für jeden einzelnen Commit manuell aus! Natürlich nur ein Scherz – wir sollten das automatisch erledigen :) Dafür können wir die Option exec verwenden.

Mit dieser Option können wir Shell-Skripte als Teil des Rebase-Prozesses ausführen. Dazu müssen wir nur an den gewünschten Stellen neue Zeilen mit dem exec-Befehl einfügen. Führen wir zum Beispiel erneut git rebase -i master aus:

pick b50f18f feat: 1st commit
pick f71ad8e feat: 2nd commit - part 1
pick 7e37b6c feat: 2nd commit - part 2
pick 8f404fe feat: 3rd commit

Jetzt kommt der spannende Teil: Wir führen nach jedem Commit die Tests aus, indem wir das Skript ./run_tests.sh ausführen:

pick b50f18f feat: 1st commit
exec ./run_tests.sh
pick f71ad8e feat: 2nd commit
exec ./run_tests.sh
pick 7e37b6c feat: 2nd commit - part 2
exec ./run_tests.sh
pick 8f404fe feat: 3rd commit
exec ./run_tests.sh

So sieht die Ausgabe aus, wenn Sie die Rebase-Ansicht schließen:

❯ git rebase -i master
Executing: ./run_tests.sh
All tests passed!
Executing: ./run_tests.sh
5 tests failed :( 108 tests passed!
Executing: ./run_tests.sh
All tests passed!
Executing: ./run_tests.sh
All tests passed!
Successfully rebased and updated refs/heads/feat/git-history.

Wie Sie sehen, hat Git das Testskript nach jedem Commit ausgeführt. Jetzt wissen wir also, dass etwas im Commit feat: 2nd commit die Tests fehlschlagen lässt. Sie können auch nach jedem Commit einen einzelnen exec-Befehl hinzufügen, indem Sie Folgendes ausführen:

$ git rebase -i --exec "./run_tests.sh"

Das Letzte (eigentlich Erste), was Sie wissen sollten: So brechen Sie ab

Das ist kein Szenario, sondern eher eine Warnung: Der Rebase-Befehl kann gefährlich sein!Wenn Sie ihn verwenden, ohne die Auswirkungen der Befehle zu verstehen, können Sie Ihre Arbeit ruinieren. Deshalb möchte ich zunächst zwei Empfehlungen geben:

  • Bevor Sie mit den Commits experimentieren, sollten Sie immer sicherstellen, dass Sie ein geeignetes Backup Ihres Git-Repos haben. Am einfachsten ist es meiner Meinung nach, alle Änderungen in einen Remote-Branch zu pushen. Falls etwas schiefgeht, kann ich jederzeit zu meinem ursprünglichen Code zurückkehren (mit git reset).

  • Stellen Sie sicher, dass Sie die Befehle verstehen. Testen Sie sie zunächst in einem temporären Git-Repo, statt sie zum ersten Mal auf Ihren tatsächlichen Code anzuwenden.

Denken Sie daran: Sie können den Rebase-Prozess jederzeit anhalten und zum ursprünglichen Zustand zurückkehren:

  • Löschen Sie auf der ersten Rebase-Seite einfach alle Commit-Zeilen. Dann wird nichts geändert.

  • Wenn Sie sich mitten im Rebase-Prozess aus irgendeinem Grund nicht mehr zurechtfinden und nicht wissen, was zu tun ist, können Sie jederzeit git rebase --abort ausführen. Dieser Befehl stoppt den Rebase-Prozess und setzt Ihr Repository auf den ursprünglichen Zustand zurück (also auf den Zustand vor git rebase -i).

Zusammenfassung

Wie wir in den letzten Beispielen gesehen haben, ist git rebase ein leistungsstarker Befehl – insbesondere im interaktiven Modus. Es gibt unzählige Anwendungsfälle, aber ich hoffe, ich konnte Ihnen ein grundlegendes Verständnis dafür vermitteln, wann die einzelnen Optionen hilfreich sind.

Die wichtigste Erkenntnis aus diesem Artikel: Verwenden Sie git rebase clever und mutig. Ich weiß, dass der Einstieg etwas beängstigend sein kann und Sie vielleicht jemanden an Ihrer Seite haben möchten, wenn Sie die Eingabetaste drücken – das ist völlig in Ordnung. Wenn Sie regelmäßig üben, wird es zur Routine. Außerdem gibt es sicher noch viele weitere Szenarien, in denen dieser Befehl für Ihre konkreten Anwendungsfälle nützlich ist.

Viel Spaß!

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.

Gepostet in:

Weiterlesen

Blog

Stadium-Sommer: Die Snyk Connect Fan Zone Tour

Die Fan Zone Tour von Snyk brachte KI-Security-Workshops, Networking und freundschaftlichen Wettbewerb in 8 Städte und 3 virtuelle Sessions. Die Teilnehmenden bauten ihre Kompetenzen aus, tauschten Ideen aus und entwickelten sich gemeinsam weiter.

Blog

Snyk VulnBench JS 1.0: Finden LLMs zweimal dieselben Fehler?

Snyk VulnBench JS 1.0: 300 wiederholte Scans zeigen, dass LLM-Sicherheitsergebnisse von Durchlauf zu Durchlauf variieren, während SAST und Modelle unterschiedliche Sicherheitslücken aufdecken.

feature insights context
Blog

KI-Sicherheit gemeinsam mit unseren Kunden gestalten: 5 Erkenntnisse aus dem Design-Partner-Programm von Evo

Lernen Sie 5 wichtige Erkenntnisse aus Snyks Design-Partner-Programm für Evo kennen. Erfahren Sie, wie KI-Erkennung, Risikoanalyse und automatisierte Richtlinien Teams dabei helfen, generative KI abzusichern und die Ausbreitung von KI-Anwendungen im großen Maßstab zu steuern.