10 Allgemeine Git-Probleme und deren Behebung

Ich habe diesen Artikel ursprünglich für Codementor im Oktober 2014 geschrieben. Es sollte für jeden etwas dabei sein, von ziemlich neuen Git-Benutzern bis zu erfahrenen Entwicklern.

1. Verwerfen Sie lokale Dateiänderungen

Manchmal ist es der beste Weg, ein Gefühl für ein Problem zu bekommen, mit dem Code herumzuspielen. Leider sind die Änderungen, die während des Vorgangs vorgenommen werden, manchmal nicht optimal. In diesem Fall kann es die schnellste und einfachste Lösung sein, die Datei in den ursprünglichen Zustand zurückzusetzen:

git checkout - Gemfile # Den angegebenen Pfad zurücksetzen
git checkout - lib bin # funktioniert auch mit mehreren Argumenten

Falls Sie sich fragen, ist der doppelte Bindestrich (-) eine gebräuchliche Methode für Befehlszeilendienstprogramme, um das Ende der Befehlsoptionen zu kennzeichnen.

2. Machen Sie lokale Commits rückgängig

Leider dauert es manchmal etwas länger, bis wir feststellen, dass wir auf dem falschen Weg sind, und bis dahin wurden möglicherweise bereits eine oder mehrere Änderungen vor Ort vorgenommen. Dies ist, wenn Git Reset nützlich ist:

git reset HEAD ~ 2 # macht die letzten zwei Commits rückgängig und behält die Änderungen bei
git reset --hard HEAD ~ 2 # macht die letzten beiden Commits rückgängig und verwirft die Änderungen

Seien Sie vorsichtig mit der Option --hard! Es setzt sowohl Ihren Arbeitsbaum als auch den Index zurück, sodass alle Ihre Änderungen endgültig verloren gehen.

3. Entfernen Sie eine Datei aus git, ohne sie aus Ihrem Dateisystem zu entfernen

Wenn Sie beim Hinzufügen eines Git nicht aufpassen, fügen Sie möglicherweise Dateien hinzu, die Sie nicht festschreiben möchten. Git rm entfernt es jedoch sowohl aus Ihrem Staging-Bereich als auch aus Ihrem Dateisystem, was möglicherweise nicht Ihren Wünschen entspricht. Stellen Sie in diesem Fall sicher, dass Sie nur die bereitgestellte Version entfernen und die Datei zu Ihrem .gitignore hinzufügen, um zu vermeiden, dass derselbe Fehler ein zweites Mal gemacht wird:

git reset filename # oder git rm - zwischengespeicherter Dateiname
Echo Dateiname >> .gitignore # Fügen Sie ihn zu .gitignore hinzu, um ein erneutes Hinzufügen zu vermeiden

4. Bearbeiten Sie eine Commit-Nachricht

Tippfehler passieren, aber zum Glück ist es bei Commit-Nachrichten sehr einfach, sie zu beheben:

git commit --amend # starte $ EDITOR, um die Nachricht zu bearbeiten
git commit --amend -m "Neue Nachricht" # Setzt die neue Nachricht direkt

Aber das ist nicht alles, was git-amend für Sie tun kann. Haben Sie vergessen, eine Datei hinzuzufügen? Fügen Sie es einfach hinzu und ändern Sie das vorherige Commit!

git add forgotten_file git commit --amend

Beachten Sie, dass --amend tatsächlich ein neues Commit erstellt, das das vorherige ersetzt. Verwenden Sie es also nicht, um Commits zu ändern, die bereits in ein zentrales Repository verschoben wurden. Eine Ausnahme von dieser Regel kann gemacht werden, wenn Sie absolut sicher sind, dass noch kein anderer Entwickler die vorherige Version ausgecheckt und ihre eigene Arbeit darauf basiert hat. In diesem Fall ist ein erzwungener Push (git push - force) möglicherweise noch in Ordnung. Die Option --force ist hier erforderlich, da der Verlauf des Baums lokal geändert wurde. Dies bedeutet, dass der Push vom Remoteserver abgelehnt wird, da keine schnelle Zusammenführung möglich ist.

5. Bereinigen Sie die lokalen Commits, bevor Sie sie verschieben

--Amend ist zwar sehr nützlich, aber es hilft nichts, wenn der Commit, den Sie umformulieren möchten, nicht der letzte ist. In diesem Fall bietet sich eine interaktive Rebase an:

Git Rebase - interaktiv
# wenn Sie keine Tracking-Informationen für diesen Zweig angegeben haben
# Sie müssen Informationen zu Upstream- und Remote-Zweigniederlassungen hinzufügen:
Git Rebase - Interaktiver Ursprungszweig

Dies öffnet Ihren konfigurierten Editor und zeigt Ihnen das folgende Menü an:

pick 8a20121 Aktualisiere die Ruby-Version auf 2.1.3
pick 22dcc45 Füge eine schicke Bibliothek hinzu
# Fcb7d7c..22dcc45 erneut auf fcb7d7c installieren
#
# Befehle: # p, pick = use commit
# r, reword = use commit, bearbeite aber die Commit-Nachricht
# e, edit = use commit, aber hör auf, es zu ändern
# s, squash = use commit, wird jedoch mit dem vorherigen Commit verschmolzen
# f, fixup = wie "squash", aber verwerfen Sie die Protokollnachricht dieses Commits
# x, exec = run command (der Rest der Zeile) using shell
#
# Diese Zeilen können nachbestellt werden; Sie werden von oben nach unten ausgeführt.
#
# Wenn Sie hier eine Zeile entfernen, geht DIESES COMMIT verloren.
#
# Wenn Sie jedoch alles entfernen, wird die Wiederherstellung abgebrochen.
#
# Beachten Sie, dass leere Commits auskommentiert werden

Oben sehen Sie eine Liste der lokalen Commits, gefolgt von einer Erläuterung der verfügbaren Befehle. Wählen Sie einfach die zu aktualisierenden Commits aus, ändern Sie pick in reword (oder kurz r), und Sie gelangen zu einer neuen Ansicht, in der Sie die Nachricht bearbeiten können.

Wie aus der obigen Auflistung hervorgeht, bieten interaktive Rebases jedoch viel mehr als die einfache Bearbeitung von Commit-Nachrichten: Sie können Commits vollständig entfernen, indem Sie sie aus der Liste löschen sowie bearbeiten, neu anordnen und komprimieren. Mit Squashing können Sie mehrere Commits zu einem zusammenführen, was ich gerne in Feature-Zweigen mache, bevor ich sie auf die Fernbedienung lege. Keine weiteren "Add forgotten file" - und "Fix typo" -Aufzeichnungen für die Ewigkeit!

6. Push-Commits zurücksetzen

Trotz der in den vorherigen Tipps beschriebenen Korrekturen gelangen fehlerhafte Commits gelegentlich in das zentrale Repository. Dennoch ist dies kein Grund zur Verzweiflung, da git eine einfache Möglichkeit bietet, einzelne oder mehrere Commits zurückzusetzen:

git revert c761f5c # setzt das Commit mit der angegebenen ID zurück
git revert HEAD ^ # setzt das vorletzte Commit zurück
git revert develop ~ 4..develop ~ 2 # setzt eine ganze Reihe von Commits zurück

Wenn Sie keine zusätzlichen Zurücksetzungs-Commits erstellen möchten, sondern nur die erforderlichen Änderungen an Ihrem Arbeitsbaum vornehmen möchten, können Sie die Option --no-commit / -n verwenden.

# den letzten Commit rückgängig machen, aber keinen Revert-Commit erstellen
Git wieder -n HEAD

Die Manualpage unter man 1 git-revert listet weitere Optionen auf und liefert einige zusätzliche Beispiele.

7. Vermeiden Sie wiederholte Zusammenführungskonflikte

Wie jeder Entwickler weiß, kann das Beheben von Zusammenführungskonflikten mühsam sein, aber das wiederholte Lösen desselben Konflikts (z. B. in Zweigstellen mit langer Laufzeit) ist geradezu ärgerlich. Wenn Sie in der Vergangenheit darunter gelitten haben, können Sie sich gerne über die unzureichende Wiederverwendung der Funktion für die Auflösung von Aufzeichnungen informieren. Fügen Sie es Ihrer globalen Konfiguration hinzu, um es für alle Projekte zu aktivieren:

git config --global rerere.enabled true

Alternativ können Sie es pro Projekt aktivieren, indem Sie das Verzeichnis .git / rr-cache manuell erstellen.

Dies ist sicher nicht für jeden eine Funktion, aber für Leute, die es brauchen, kann es echtzeitsparend sein. Stellen Sie sich vor, Ihr Team arbeitet gleichzeitig an verschiedenen Feature-Zweigen. Jetzt möchten Sie alle in einem testbaren Pre-Release-Zweig zusammenführen. Wie erwartet gibt es mehrere Zusammenführungskonflikte, die Sie beheben. Leider hat sich herausgestellt, dass einer der Zweige noch nicht vollständig vorhanden ist. Sie haben sich daher entschlossen, den Zusammenschluss wieder aufzuheben. Einige Tage (oder Wochen) später, wenn der Zweig endlich fertig ist, führen Sie ihn erneut zusammen. Dank der aufgezeichneten Auflösungen müssen Sie dieselben Zusammenführungskonflikte jedoch nicht erneut lösen.

Die Manpage (man git-rerere) enthält weitere Informationen zu weiteren Anwendungsfällen und Befehlen (git rerere status, git rerere diff, etc).

8. Suchen Sie das Commit, das nach einer Zusammenführung einen Fehler verursacht hat

Das Aufspüren des Commits, das nach einer großen Zusammenführung einen Fehler verursacht hat, kann ziemlich zeitaufwändig sein. Zum Glück bietet git eine großartige binäre Suchfunktion in Form von git-bisect. Zuerst müssen Sie die Ersteinrichtung durchführen:

git bisect start # startet die Halbierungssitzung
git bisect bad # markiert die aktuelle Revision als schlecht
git bisect good revision # markiert die letzte bekannte gute Revision

Nach diesem git wird automatisch eine Revision zwischen den bekannten "guten" und "schlechten" Versionen ausgecheckt. Sie können jetzt Ihre Spezifikationen erneut ausführen und das Commit entsprechend als "gut" oder "schlecht" markieren.

git bisect good # oder git bisec bad

Dieser Vorgang wird fortgesetzt, bis Sie zu dem Commit gelangen, das den Fehler ausgelöst hat.

9. Vermeiden Sie häufige Fehler mit Git Hooks

Einige Fehler treten wiederholt auf, können jedoch leicht vermieden werden, indem bestimmte Überprüfungen oder Bereinigungsaufgaben in einer definierten Phase des Git-Workflows ausgeführt werden. Dies ist genau das Szenario, für das Hooks entwickelt wurden. Fügen Sie zum Erstellen eines neuen Hooks eine ausführbare Datei zu .git / hooks hinzu. Der Name des Skripts muss einem der verfügbaren Hooks entsprechen. Eine vollständige Liste dieser Hooks finden Sie auf der Manualpage (man githooks). Sie können auch globale Hooks definieren, die in all Ihren Projekten verwendet werden sollen, indem Sie ein Vorlagenverzeichnis erstellen, das git beim Initialisieren eines neuen Repositorys verwendet (weitere Informationen finden Sie unter man git-init). So sieht der relevante Eintrag in ~ / .gitconfig und ein Beispiel für ein Vorlagenverzeichnis aus:

[drin]
    templatedir = ~ / .git_template
  
→ Baum .git_template
  .git_template
  └── Haken
      └── Pre-Commit

Wenn Sie ein neues Repository initialisieren, werden die Dateien im Vorlagenverzeichnis an den entsprechenden Speicherort im .git-Verzeichnis Ihres Projekts kopiert.

Was folgt, ist ein leicht erfundenes Beispiel für einen Commit-msg-Hook, der sicherstellt, dass jede Commit-Nachricht auf eine Ticketnummer wie "# 123" verweist.

#! / usr / bin / env ruby
message = File.read (ARGV [0])

es sei denn, message = ~ / \ s * # \ d + /
  setzt "[POLICY] Ihre Nachricht hat kein Ticket referenziert."
  Ausfahrt 1
Ende

10. Wenn alles andere fehlschlägt

Bisher haben wir eine Menge darüber besprochen, wie häufig auftretende Fehler bei der Arbeit mit Git behoben werden können. Die meisten von ihnen haben genug einfache Lösungen, aber es gibt Zeiten, in denen man die großen Waffen rausholen und die Geschichte einer ganzen Branche neu schreiben muss. Ein häufiger Anwendungsfall hierfür ist das Entfernen vertraulicher Daten (z. B. Anmeldeinformationen für Produktionssysteme), die an ein öffentliches Repository übergeben wurden:

Git-Filter-Zweig --force --index-filter \
  'git rm --cached --ignore-unmatch secrets.txt' \
  --prune-empty --tag-name-filter cat - --all

Dadurch wird die Datei secrets.txt aus jedem Zweig und Tag entfernt. Es werden auch alle Commits entfernt, die aufgrund der obigen Operation leer wären. Beachten Sie, dass hierdurch der gesamte Verlauf Ihres Projekts neu geschrieben wird, was in einem verteilten Workflow sehr störend sein kann. Auch wenn die fragliche Datei jetzt entfernt wurde, sollten die darin enthaltenen Anmeldeinformationen immer noch als gefährdet betrachtet werden!

GitHub hat ein sehr gutes Tutorial zum Entfernen sensibler Daten und man git-filter-branch hat alle Details zu den verschiedenen verfügbaren Filtern und deren Optionen.

Ursprünglich veröffentlicht bei gist.github.com.