4 Punkte von GN⁺ 2025-01-18 | 1 Kommentare | Auf WhatsApp teilen
  • Da es bei Git schwierig ist, nach Lösungen zu suchen, wenn man den Namen der Fehlersituation nicht kennt, fasst diese Ressource häufige Befehle nach Recovery-Szenario so zusammen, dass man sie direkt nachschlagen und verwenden kann
  • Wenn man ein Repository stark beschädigt hat oder Fehler beim Löschen oder Mergen rückgängig machen muss, kann man mit git reflog die Arbeitshistorie finden und mit git reset HEAD@{index} zu einem früheren Zustand zurückkehren
  • Kleine Korrekturen am letzten Commit oder Änderungen an der Commit-Message lassen sich schnell mit git commit --amend erledigen; bei Commits, die bereits auf einen öffentlichen/geteilten Branch gepusht wurden, sollte man das jedoch nicht verwenden
  • Wenn man auf den falschen Branch committed hat, kann man die Änderungen über den Ablauf reset, stash, checkout, stash pop oder per cherry-pick verschieben; welche Methode sicher ist, hängt jedoch davon ab, ob bereits gepusht wurde
  • reset --hard und git clean -d --force, die den Zustand des Remote-Repositorys erzwingen, können sogar nicht getrackte Dateien löschen, daher sollte man vor der Ausführung genau prüfen

Warum Git-Fehler schwer zu beheben sind

  • Bei Git passieren Fehler leicht, und um ein Problem zu beheben, muss man oft bereits die nötigen Begriffsnamen kennen, um danach suchen zu können
  • Diese Ressource fasst häufige unangenehme Situationen in Git mit einfach verständlichem Englisch und Befehlsbeispielen zusammen
  • Sie ist keine vollständige Referenz; es kann auch andere Wege geben, dieselbe Aufgabe zu erledigen

Ein Repository auf einen früheren Zustand zurücksetzen

  • Wenn man ein Repository stark beschädigt hat oder gelöschte Inhalte wiederherstellen möchte, prüft man mit git reflog die Arbeitshistorie
git reflog


# Man kann eine Liste der Git-Aktionen über alle Branches hinweg sehen


# Jeder Eintrag hat einen Index in der Form HEAD@{index}
git reset HEAD@{index}
  • reflog kann in folgenden Situationen genutzt werden
    • Wiederherstellung versehentlich gelöschter Inhalte
    • Entfernen eines Versuchs, der das Repository beschädigt hat
    • Wiederherstellung nach einem schlechten Merge
    • Rückkehr zu einem Zeitpunkt, an dem tatsächlich noch alles funktionierte

Den letzten Commit korrigieren

  • Wenn direkt nach dem letzten Commit eine kleine Korrektur ergänzt werden muss, verwendet man git commit --amend --no-edit
# Nach den Änderungen
git add . # oder einzelne Dateien hinzufügen
git commit --amend --no-edit
  • Dieser Befehl nimmt die neuen Änderungen in den letzten Commit auf
  • Man könnte dieselbe Änderung auch als neuen Commit erstellen und anschließend mit rebase -i zusammenführen, aber amend ist deutlich schneller
  • Bei Commits, die bereits auf einen öffentlichen/geteilten Branch gepusht wurden, sollte man amend nicht verwenden
  • Um nur die Message des letzten Commits zu ändern, verwendet man folgenden Befehl
git commit --amend


# Commit-Message gemäß den Anweisungen ändern

Wenn man auf den falschen Branch committed hat

  • Wenn man versehentlich auf master committed hat, der Commit aber auf einen neuen Branch gehört hätte, erstellt man aus dem aktuellen Zustand einen neuen Branch und entfernt den letzten Commit von master
git branch some-new-branch-name
git reset HEAD~ --hard
git checkout some-new-branch-name
  • Diese Methode ist nicht geeignet für Commits, die bereits auf einen öffentlichen/geteilten Branch gepusht wurden
  • Wenn man vorher bereits andere Versuche unternommen hat, kann statt HEAD~ eine Form wie HEAD@{number-of-commits-back} nötig sein
  • Wenn man komplett auf den falschen Branch committed hat, kann man die Änderungen über den Ablauf reset --soft, stash, checkout, stash pop verschieben
git reset HEAD~ --soft
git stash
git checkout name-of-the-correct-branch
git stash pop
git add . # oder einzelne Dateien hinzufügen
git commit -m "your message here"
  • In derselben Situation kann man auch cherry-pick verwenden
git checkout name-of-the-correct-branch
git cherry-pick master
git checkout master
git reset HEAD~ --hard

Änderungen und ältere Commits rückgängig machen

  • Wenn man Dateien geändert hat, git diff aber leer ist, wurden die Änderungen möglicherweise bereits gestaged; dann ist das Flag --staged nötig
git diff --staged
  • Um Änderungen von vor einigen Commits rückgängig zu machen, sucht man mit git log den Hash und führt git revert aus
git log
git revert [saved hash]
  • git revert erstellt einen neuen Commit, der den betreffenden Commit rückgängig macht
  • Wenn nicht der gesamte Commit, sondern nur eine bestimmte Datei zurückgesetzt werden soll, verwendet man den Ablauf, bei dem die alte Version der Datei in den Index übernommen und anschließend committed wird
git log
git checkout [saved hash] -- path/to/file
git commit -m "Wow, you don't have to copy-paste to undo"

Den Zustand des Remote-Repositorys erzwingen

  • Statt das Repository zu löschen und erneut zu klonen, kann man Git-Befehle verwenden, um es an den Zustand des Remote-Repositorys anzugleichen
git fetch origin
git checkout master
git reset --hard origin/master
git clean -d --force
  • git reset --hard origin/master setzt den Branch auf den Zustand des Remote-master
  • git clean -d --force löscht nicht getrackte Dateien und Verzeichnisse
  • Für jeden beschädigten Branch muss man checkout, reset und clean wiederholen
  • Diese Aktionen sind destruktiv und nicht wiederherstellbar, daher ist vor der Ausführung Vorsicht geboten

Umfang und Beiträge

  • Diese Ressource ist keine vollständige Git-Referenz, sondern eine Sammlung praktischer Recovery-Abläufe, die durch Trial-and-Error gefunden wurden
  • Wer beim Hinzufügen von Übersetzungen helfen möchte, kann auf GitHub einen PR einreichen

1 Kommentare

 
GN⁺ 2025-01-18
Hacker-News-Kommentare
  • Es gibt ein paar Dinge, die ich ändern würde: Ich würde immer git switch statt git checkout verwenden und reset --hard möglichst vermeiden.
    Wenn man zum Beispiel „versehentlich auf master committet hat, obwohl der Commit auf einen neuen Branch hätte gehört“, kann man das mit git branch some-new-branch-name, git switch -d HEAD~, git switch -C master, git switch some-new-branch-name erledigen.
    Wenn man „auf den falschen Branch committet hat“, würde ich auf den richtigen Branch mit git switch wechseln und dann git cherry-pick master, git switch -d master~, git switch -C master verwenden.
    Und für „alles aufgeben und zum Remote-Stand zurückkehren“ würde ich es so aufbauen: git fetch origin, git restore -WS ., git clean -d --force, git switch -d origin/master, git switch -C master

    • Das interne Modell von Git ist mit Blobs, Commit-Bäumen und Commit-Zeigern wunderschön, aber die Kluft zur Kommandozeilenoberfläche ist viel zu groß.
      Selbst wenn man das Git-Modell gut versteht, sind solche Rezepte nicht intuitiv, und man muss auch noch die sonderbaren Verhaltensweisen der einzelnen Befehle kennen.
      Zum Beispiel gibt es zwar schon git switch -c some-new-branch-name, aber ein Befehl wie git move-branch master HEAD~, der nur den Branch auf einen anderen Commit verschiebt, wäre viel intuitiver.
    • Ich bin nicht dagegen, statt git checkout lieber git switch und git restore zu verwenden, aber diese Befehle wurden erst in Git v2.23 eingeführt, und dieses Release ist ungefähr 5 Jahre alt.
      Auf der Hilfeseite steht immer noch die Warnung THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE..
      Natürlich steht diese Warnung schon fast seit der Einführung des Befehls dort, und nach 5 Jahren könnte man wohl aufhören, das noch experimentell zu nennen.
      Trotzdem ist git checkout abwärtskompatibler, und man kann es auch so sehen, dass die ursprüngliche Git-Nutzung aus der Zeit widerspiegelt, in der diese Website entstanden ist.
      https://github.com/git/git/blob/757161efcca150a9a96b312d9e78...
      https://github.com/git/git/releases/tag/v2.23.0
      https://github.com/git/git/commit/4e43b7ff1ea4b6f16b93a432b6...
    • Wenn es darum geht, Änderungen in den Staging-Bereich zu übernehmen, finde ich es nicht gut, standardmäßig git add . zu lehren.
      Bestimmte Dateien explizit anzugeben lässt weniger Raum für spätere „Oh Mist“-Momente.
    • Mich würde die Begründung für diese Empfehlung interessieren.
      Ich würde gern wissen, warum switch besser als checkout sein soll und warum man reset --hard nicht verwenden sollte.
    • Ich verstehe nicht, was mit „reset --hard vermeiden“ gemeint ist.
      Mich würde konkret interessieren, warum es in der Praxis nicht ausreichen soll; ich benutze es ziemlich oft zusammen mit alias git-restore-file='git restore --source=HEAD --', und es scheint gut zu funktionieren.
  • Wenn man Git lernt, sollte man anfangen, GUI-Clients als Standardweg zu empfehlen.
    Damit wäre ein Drittel dieser Probleme gelöst, und ein weiteres Drittel würde gar nicht erst entstehen.
    Wenn man später entscheidet, dass die Kommandozeile schneller ist, kann man sie dann immer noch benutzen; zuerst müssen die Leute sehen, wie man visuell mit dem Baum interagiert.
    Ich persönlich mag fork.dev, aber die Clients heutzutage sind im Großen und Ganzen ähnlich.

    • Ich stimme zu, dass GUIs für Git oft die bessere User Experience bieten.
      Mit Magit sind Dinge wie „Cherry-Pick abbrechen“ innerhalb der Oberfläche auffindbar, und dafür gibt es dieselben Tastenkürzel wie für andere „X abbrechen“-Aktionen.
      Wenn ich nur die Git-Kommandozeile gehabt hätte, hätte ich gar nicht gewusst, wo ich anfangen soll.
      Ich habe bei interaktiven Rebase-Vorgängen auch schon Commits gelöscht, die ich nicht hätte löschen dürfen, aber da bei jedem Startpunkt eines Rebase ein über den Reflog erreichbarer Snapshot entsteht, kann man einen verpfuschten Rebase sicher zurückdrehen.
      Die Reflog-Oberfläche von Magit ist dieselbe wie die Log-Oberfläche, sodass ich mich dort auch beim ersten Mal nicht verirrt habe; mit der Git-CLI hätte ich wahrscheinlich nicht verstanden, was gerade passiert.
    • Ich habe angefangen, jj als Git-kompatible Alternative zu empfehlen.
      Mir gefällt, dass jj, wenn man es ohne Argumente ausführt, den relevanten Teil des Commit-Baums und die aktuelle Position zeigt.
      Man sieht auf einen Blick den Commit, an dem man gerade arbeitet, verbundene Branches und deren Verlauf, andere aktive Branches im Repository sowie die Nachricht und den Änderungsstatus jedes Commits, was hervorragend dabei hilft, wieder Orientierung zu gewinnen.
    • Git-Anfängern rate ich immer, eine UI zu finden, die zu ihnen passt, und diese statt der Kommandozeile zu benutzen.
      Aber fast niemand hört darauf, und am Ende quälen sie sich mit der CLI herum, ruinieren ihre Branches, fallen hinter main zurück und bekommen Angst vor Rebase.
      In einer UI ließe sich vieles per Drag-and-Drop oder auf ähnliche Weise lösen, und so wirkt es fast wie eine Form von Selbstquälerei.
    • Wenn man sehen muss, wie man mit dem Baum interagiert, gibt es ein interaktives Tutorial, das meinen Kollegen geholfen hat: https://learngitbranching.js.org/
    • Nach meinen früheren Erfahrungen mit GUI-Frontends für Versionsverwaltung waren sie in Ordnung, solange alles gut lief, aber sobald man ein verheddertes Problem entwirren musste, konnte einem kaum jemand helfen.
      Auch wenn die Oberfläche schrecklich war, war es für die Kommunikation einfacher, dieselbe Oberfläche zu verwenden wie die anderen im Team, und auch Online-Material zur Problemlösung ging meist vom „üblichen Weg“ aus und ließ sich daher leichter nutzen.
  • Dass Git im April 2025 schon 20 Jahre alt wird, fühlt sich ziemlich seltsam an
    Ich war damals dabei und erinnere mich noch daran, wie ich überlegte, ob ich git, darcs oder bazaar lernen sollte, um SVN oder CVS zu ersetzen
    Ich weiß nicht mehr, ob ich auch Mercurial ausprobiert habe
    Ich frage mich, ob der „GitHub-Effekt“ den Bedarf an neuen Marktteilnehmern bei Versionsverwaltungssystemen praktisch abgewürgt hat
    Vielleicht war das Yak-Shaving irgendwann einfach weit genug getrieben

    • Der GitHub-Effekt ist real, wie alle Netzwerkeffekte, aber das heißt nicht, dass Verbesserungen unmöglich sind
      Ich bin vollständig auf jj umgestiegen, und durch die Git-Kompatibilität muss ich nicht darauf warten, dass andere mitziehen
      Ich bin von mehreren Aspekten von GitHub zunehmend frustriert und hoffe, dass irgendwann alle auch darüber hinausgehen, aber der Weg dorthin wirkt nicht besonders einfach
    • SVN hat immer gut funktioniert
      SVN ist intuitiv und funktioniert in 99 % der Fälle gut genug, sodass man es den Leuten nicht extra „beibringen“ muss
      Ich wünschte, wir würden alle aufhören, den 1337-Hacker zu spielen, und anerkennen, dass Git für die meisten ein überdimensioniertes Werkzeug ist
    • Jede Verbesserung müsste zumindest Git-kompatibel sein, so wie zum Beispiel jj
  • Ich glaube, ich habe ein ziemlich gutes mentales Modell davon, was Git macht, aber sobald die Befehle auch nur ein bisschen komplexer werden, kann ich mir die Argumente nicht merken
    Die Befehle sind weder leicht auffindbar noch leicht zu merken
    Ich weiß nicht, ob die Text-UI schlecht ist oder ob es einfach grundsätzlich schwierig ist, die Manipulation von Bäumen in Textform zu beschreiben

  • Schön zu sehen, dass in diesem Thread Suchergebnisse für hg und Mercurial auftauchen
    Ich kann immer noch kaum glauben, dass Mercurial die Kriege der verteilten Versionsverwaltung verloren hat, denn ich halte es für das bessere Werkzeug

    • Auch nach heutigem Maßstab ist Mercurial ohne jeden Zweifel das bessere Werkzeug
      Es ist benutzbar, stabil, praxiserprobt und wird aktiv gepflegt
      Es kommt dem Motto „Sei die Veränderung, die du sehen willst“ nahe, also nutze es einfach
      Formulierungen wie „es hat verloren“ sind kontraproduktiv, und die implizite Abschreckung solcher Aussagen hält die Akzeptanz von Mercurial niedrig
      Hosting ist zumindest bei Sourcehut und heptapod.host möglich
      Ich betreibe auch eine private Heptapod-Instanz; das ist ein GitLab-Fork mit nativer Mercurial-Unterstützung, und es funktioniert einfach
    • Klingt interessant, aber ich frage mich, ob es einen GitHub-Link gibt
    • Mercurial ist das langsamere Werkzeug
    • Falls du Mercurial-Hosting brauchst, kannst du dieses Hosting nutzen: https://hg.reactionary.software/
  • Git war für mich immer eine dieser Technologien, die in vieler Hinsicht nicht der Intuition folgen und deshalb nie richtig im Kopf bleiben
    Wenn man es nicht schon lange benutzt, muss man für fast jede Aufgabe wahrscheinlich Google bemühen oder die man-Seiten lesen

    • Ich habe mit SVN angefangen und bin dann zu Git gewechselt; ich musste zwar die Unterschiede lernen, aber in vieler Hinsicht fühlte es sich einfacher an
      Gerade wenn man den Schrecken erlebt hat, eine große Codebasis in SVN mit vielen Branches zu verwalten und wieder zusammenzuführen, merkt man, dass Git in diesem Punkt absurd viel besser ist
      Für jemanden, der direkt mit Git einsteigt, mag die Lernkurve deutlich steiler sein, aber ich finde es trotzdem manchmal schwer zu verstehen, woran die Leute scheitern
    • Ich wollte gestern eine Datei aus der Historie löschen
      Wenn man dafür den eingebauten Weg über filter-branch nimmt, bekommt man nach langer Verzögerung eine Warnung, man solle lieber Control+C drücken und das Third-Party-Python-Skript filter-repo herunterladen
  • Nicht gerade etwas, worauf ich stolz bin, aber meine häufigste Git-Operation für den Fall „ich habe es kaputtgemacht“ ist: lokales Repository löschen, neu klonen und die Änderungen erneut anwenden
    Das funktioniert in 95 % der Fälle hervorragend, und für den Rest bitte ich die DevOps-Leute um Hilfe

    • Ich nutze Git jetzt seit fast 15 Jahren und habe sogar zweimal Programme/Produkte gebaut, die Git intern verwenden, um bestimmte Ergebnisse zu erzeugen, aber ich mache vor etwas heikleren Git-Aktionen immer noch manchmal sowas wie cp -R .git ../git-backup
      Wenn ich es richtig schlimm vermassle, kann ich einfach das gesamte .git-Verzeichnis durch die alte Kopie ersetzen
      Das ist viel schneller, als herauszufinden, wie man eine bestimmte Aktion oder ein ganzes Bündel von Aktionen korrekt rückgängig macht
    • Du bist nicht allein: https://xkcd.com/1597/
  • Was die Leute bei Git meiner Meinung nach unbedingt verinnerlichen sollten, ist, dass Git ein append-only Datenspeicher ist
    Um Arbeit ins Repository zu bekommen, muss man committen, und sobald die Arbeit einmal drin ist, lässt sie sich mit Git-Befehlen nicht mehr entfernen
    Nach diesem Prinzip funktioniert auch reflog
    Egal was man tut, man kann immer zu einem früheren Branch-Zustand zurückkehren und die Arbeit aus dem Datenspeicher wiederherstellen
    Wenn man aber nicht committet, kann Git nicht helfen, also sollte man einfach committen
    Immer committen, und wenn die Hürde dafür zu groß ist oder man es ständig vergisst, muss man die Werkzeuge verbessern
    Wenn man den ganzen Tag Code schreibt, sollte man im Allgemeinen mindestens einmal pro Stunde committen

  • Nur eine Kleinigkeit, aber jedes Mal, wenn ich git reflog sehe, lese ich es wie re-flog
    Vielleicht kann ich mir den Befehl deshalb nie richtig merken, weil ich unbewusst nicht an „reference log“ denke
    Stattdessen wirkt es so, als würde man Git sagen, irgendetwas wieder günstig zu verkaufen

  • Ich bin zwar kein Git-Nutzer, aber wenn ich so etwas sehe, denke ich immer stärker, dass git commit an sich keine Bedeutung hat und nur der Zeitpunkt wichtig ist, an dem ein Commit gepusht oder gemergt wird
    Ähnlich wie beim Speichern einer Textdatei
    Man schreibt ja auch nicht jedes Mal eine kurze Nachricht, wenn man eine Datei speichert, also kann man es einfach überspringen

    • Wenn die Änderung das Ergebnis einer mühsamen Debugging-Reise ist und andere später wahrscheinlich noch einmal darauf zurückkommen werden, schreibe ich eine Nachricht
      Nicht unbedingt kurz; je kleiner die Änderung, desto länger fällt die Erklärung manchmal aus
      Für 2–3 Zeilen mit großer Wirkung habe ich auch schon 2–3 Absätze geschrieben
    • Es wirkt völlig offensichtlich, dass lokale Commits und Remote-Commits unterschiedlichen Zwecken dienen
      Ein lokaler Commit ist eine Methode, an einem Punkt, an dem alles kompiliert und funktioniert, einen Checkpoint zu erstellen
      So kann man Änderungen stashen oder, wenn man etwas kaputtgemacht hat, zu einem funktionierenden Zustand zurückkehren
      Vor dem Pushen kann man per Reset in passende Commits aufteilen, sodass die Remote-Historie sauber bleibt und nicht zu einem wip-Haufen wird
      Oder man nutzt beim PR einen Squash Merge und bekommt so die Vorteile von beidem
    • Ein Commit ist sehr wichtig, weil Git dadurch intern eine Kopie der Dateien erstellt
      Das bedeutet aber nicht, dass man sinnlose Nachrichten wie more fixes eingeben muss
      Normalerweise mache ich einen ersten Commit wie (WIP) Funktionsname, wiederhole dann Amend-Commits, bis ich zufrieden bin, und entferne am Ende (WIP) aus der Nachricht
    • Man vergleicht es mit dem Speichern einer Textdatei, aber es gibt auch viele Menschen, die jedes Mal vor dem Schlafengehen einen kurzen Text über ihren Tag schreiben
      Ich sehe das Git-Revisionslog als etwas Ähnliches wie so ein Tagebuch
      Es ist geordneter als ein separates TODO-Dokument und regt dazu an, bereits erledigte Arbeit auf gut lesbare Weise zu beschreiben
      Wenn man entscheiden muss, wie weit man zurückgehen will, kann es nützlich sein zu wissen, was man sich beim damaligen Commit gedacht hat, und falls der Ansatz falsch war, hilft es auch bei der nachträglichen Analyse
      Wenn Arbeit, die noch nicht gemergt oder gepusht wurde, als separate Änderungen mit einem Ziel organisiert ist, ist es viel einfacher, nur die guten Teile zu behalten, wenn manches davon gut und manches schlecht ist
    • Vor dem Push ist alles lokal, also kann man machen, was man will
      Technisch gesehen sind Commits trotzdem nicht bedeutungslos
      Ohne Commits gibt es auch nichts, was man pushen oder mergen könnte
      Auch jenseits der Technik ist es sehr nützlich, in Klartext festzuhalten, warum man etwas ändert
      Wenn man solche Erklärungen zu oft schreiben muss, sollte man die Einheit einer „Änderung“ enger fassen oder sorgfältiger auswählen, woran man gleichzeitig arbeitet
      Wenn man Git nicht verwendet, frage ich mich, womit dann Versionsverwaltung und Änderungsmanagement gemacht werden