Oh Shit, Git!?!

Git ist schwer: Fehler zu machen ist leicht, und herauszufinden wie man sie wieder behebt ist am Anfang fast unmöglich. Auch die Dokumentation hilft oft nicht weiter, weil man erst die passenden Begriffe kennen muss, mit denen man sein Problem lösen kann.

Deswegen folgen hier einige Situationen, in denen ich mich schon befunden habe, und wie ich da wieder rausgekommen bin – alles in simpler Sprache.

Oh shit, ich habe etwas komplett verkackt! Bitte sag mir, dass git eine magische Zeitmaschine eingebaut hat!?!

git reflog
# Du siehst eine Liste mit allem, was du in
# git getan hast, in allen Branches.
# Jeder Eintrag hat einen Index: HEAD@{index}
# Finde den Eintrag VOR demjenigen, der alles
# kaputt gemacht hat
git reset HEAD@{index}
# Alles ist jetzt wieder wie es vorher war

Damit kannst du Dateien zurückholen, die du gelöscht hast, oder Dinge rückgängig machen, die dein Repo zerstört haben, oder einen nicht geglückten Merge oder einfach zu einem Stand zurückkehren, als bestimmte Dinge noch funktioniert haben. Ich nutze reflog wirklich sehr häufig. Vielen Dank an die vielen, vielen, vielen Leute, die vorgeschlagen haben, es hier aufzunehmen.

Oh shit, ich habe schon commited und dann gemerkt dass ich eine kleine Änderung vergessen habe!

# Mach deine Änderung
git add . # oder füge einzelne Dateien hinzu
git commit --amend --no-edit
# Jetzt enthält dein letzter Commit auch die neuen Änderungen!
# WARNUNG: Niemals solltest du "--amend" bei einem 
# Commit verwenden, der schon gepusht wurde (es sei denn
# du bist der einzige Entwickler in dem Repo)

Normalerweise passiert mir das, wenn ich commite und dann erst Tests oder Linter laufen lasse. Dann fallen irgendwelche kleinen Formatierungsfehler auf, zum Beispiel ein vergessenes Leerzeichen. Man könnte die Änderung auch als neuen Commit erstellen und dann mit rebase -i beide Commits zusammenführen, aber das hier ist tausendmal schneller.

Warnung: Du solltest niemals das "--amend"-Flag verwenden, wenn dein Commit schon gepusht wurde. Verwende das nur bei lokalen Commits, ansonsten wird es kompliziert.

Oh shit, Ich muss die letzte Commit-Message ändern!

git commit --amend
# Anschließend öffnet sich dein normaler Editor und
# du kannst eine neue Commit-Message eingeben

Immer diese nervigen Anforderungen für den Aufbau von Commit-Messages.

Oh shit, ich habe aus Versehen etwas auf master commited, das eigentlich auf einen eigenen Branch gehört

# Erstelle einen neuen Branch mit dem Stand des master
git branch neuer-branch-name
# Entferne den letzten Commit vom master
# und wechsel zum neuen Branch
git reset HEAD~ --hard
git checkout some-new-branch-name
# Dein Commit lebt jetzt in dem neuen Branch weiter :)

Hinweis: Das funktioniert nicht, wenn du den Commit schon auf einen öffentlichen Branch gepusht hast. Wenn du vorher schon mehr Dinge probiert hast, musst du vielleicht git reset HEAD@{anzahl-der-commits} statt HEAD~ verwenden. Vielen Dank an die vielen vielen Leute, die vorgeschlagen haben wie man dieses Problem an schnellsten löst – ich kannte diese Lösung noch nicht.

Oh shit, ich habe auf dem falschen Branch commited!

# Mach den letzten Commit rückgängig, aber erhalte die 
# Änderungen
git reset HEAD~ --soft
git stash
# Navigiere zum richtigen Branch
git checkout name-des-richtigen-branch
git stash pop
git add . # oder füge einzelne Dateien hinzu
git commit -m "Deine Nachricht hier"
# Jetzt sind die Änderungen auf dem richtigen Branch

Viele haben vorgeschlagen cherry-pick hierfür zu verwenden. Das ist auch absolut okay, also wähle die Variante, die dir besser gefällt!

git checkout name-des-richtigen-branch
# Wähle den letzten Commit vom master
git cherry-pick master
# Und lösche den Commit vom master
git checkout master
git reset HEAD~ --hard

Oh shit, ich wollte einen Diff anzeigen lassen, aber er ist leer?!

Wenn du dir sicher bist, dass du Dateien geändert hast, aber diff trotzdem nichts anzeigt, dann hast du die Dateien wahrcheinlich schon ge-add-et und musst deswegen ein spezielles Flag verwenden.

git diff --staged

Das gehört zur Kategorie ¯\(ツ)/¯ (Ja, ich weiß dass das Verhalten ein Feature ist, aber für Einsteiger ist das super verwirrend!)

Oh shit, ich muss einen Commit rückgängig machen, der schon 5 Commits zurückliegt!

# Finde den betreffenden Commit
git log
# Verwende die Pfeiltasten um in der History zu scrollen
# und kopiere dir den Hash des betreffenden Commits
git revert [betreffender hash]
# git erstellt einen neuen Commit, der den gewählten
# Commit rückgängig macht. Du musst dafür noch eine
# Commit-Message eingeben oder einfach abspeichern

Man muss also doch nicht alten Code auswählen und in die aktuellen Dateien einfügen, um Änderungen rückgängig zu machen. Wenn du einen Bug commited hast, kannst du also den ganzen commit mit revert wieder zurücknehmen.

Du kannst auch nur die Änderungen in einer einzigen Datei rückgängig machen, anstatt eines ganzen Commits. Aber in typischer Git-Manier sind das natürlich komplett andere Befehle. Wäre ja sonst auch zu einfach!

Oh shit, ich muss Änderungen an einer einzigen Datei rückgängig machen!

# Finde den Hash eines Commits vor deinen Änderungen
git log
# Verwende die Pfeiltasten um in der History zu scrollen
# und kopiere dir den entsprechenden Hash
git checkout [gewählter hash] -- pfad/zur/datei
# Die alte Version ist jetzt wiederhergestellt
git commit -m "Wow, rückgängig machen ohne Copy & Paste"

Als ich das herausgefunden habe, war das für mich ein riesiges Ding! Ernsthaft, auf welchem verdammten Planeten macht es Sinn, den Befehl zum rückgängig machen von Änderungen in einer Datei checkout -- zu nennen :shakes-fist-at-linus-torvalds:

Scheiß Unordnung, ich gebe auf!

cd ..
sudo rm -r scheiss-git-repo
git clone https://some.github.url/scheiss-git-repo.git
cd scheiss-git-repo

Danke an Eric V. für diesen Tipp. Alle Beschwerden über die Verwendung von sudo bitte an ihn schicken.

Aber mal ernsthaft: wenn dein Branch soo verhunzt ist, dass du den Stand deines Repos auf den Stand auf dem Git-Server zurücksetzen musst, dann solltest du das hier ausprobieren. Aber aufpassen, denn diese Kommandos lassen sich nicht rückgängig machen!

# Hole den neusten Stand vom Server
git fetch origin
git checkout master
git reset --hard origin/master
# Lösche alle Dateien und Ordner, die noch nicht 
# eingecheckt wurden
git clean -d --force
# Wiederhole checkout/reset/clean für jeden verhunzten Branch