Wie kann man Git-Objekte wiederherstellen, die durch einen Festplattenausfall beschädigt wurden?

92

Ich hatte einen Festplattenfehler, der dazu führte, dass einige Dateien eines Git-Repositorys beschädigt wurden. Beim Laufen git fsck --fullbekomme ich folgende Ausgabe:

error: .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack SHA1 checksum mismatch
error: index CRC mismatch for object 6c8cae4994b5ec7891ccb1527d30634997a978ee from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack at offset 97824129
error: inflate: data stream error (invalid code lengths set)
error: cannot unpack 6c8cae4994b5ec7891ccb1527d30634997a978ee from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack at offset 97824129
error: inflate: data stream error (invalid stored block lengths)
error: failed to read object 0dcf6723cc69cc7f91d4a7432d0f1a1f05e77eaa at offset 276988017 from .git/objects/pack/pack-6863e0a0e4b4ded6090fac5d12eba6ca7346b19c.pack
fatal: object 0dcf6723cc69cc7f91d4a7432d0f1a1f05e77eaa is corrupted

Ich habe Backups des Repositorys, aber das einzige Backup, das die Pack-Datei enthält, hat es bereits beschädigt. Ich denke also, ich muss einen Weg finden, um die einzelnen Objekte aus verschiedenen Backups abzurufen und Git irgendwie anzuweisen, ein neues Paket mit nur korrekten Objekten zu erstellen.

Können Sie mir bitte Hinweise geben, wie ich mein Repository reparieren kann?

Christian
quelle
2
Das ist mir gerade passiert. Ich möchte mich nicht mit Git-Objekten anlegen ... also klonen Sie das Projekt erneut aus dem Remote-Repository in einen neuen Ordner und kopieren Sie dann einfach alle Dateien aus meinen problematischen Repositorys ( .gitnatürlich ohne den Ordner) in das frisch geklonte Repo ... und dann git statusim neuen Repo ... git erkennt alle betroffenen Änderungen an meinen Dateien korrekt und ich kann meine Arbeit wieder aufnehmen.
Rosdi Kasim

Antworten:

82

In einigen früheren Sicherungen wurden Ihre fehlerhaften Objekte möglicherweise in verschiedene Dateien gepackt oder sind noch lose Objekte. So können Ihre Objekte wiederhergestellt werden.

Es scheint, dass Ihre Datenbank einige fehlerhafte Objekte enthält. Sie können es also manuell machen.

Wegen git hash-object, git mktreeund git commit-treedie Objekte nicht schreiben , weil sie in der Packung zu finden sind, dann beginnen dies zu tun:

mv .git/objects/pack/* <somewhere>
for i in <somewhere>/*.pack; do
  git unpack-objects -r < $i
done
rm <somewhere>/*

(Ihre Pakete werden aus dem Repository entfernt und erneut darin entpackt. Nur die guten Objekte befinden sich jetzt in der Datenbank.)

Du kannst tun:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

und überprüfen Sie den Typ des Objekts.

Wenn der Typ blob ist: Rufen Sie den Inhalt der Datei aus früheren Sicherungen ab (mit git showoder git cat-fileoder git unpack-file; dann können Sie git hash-object -wdas Objekt in Ihrem aktuellen Repository neu schreiben.

Wenn der Typ Baum ist: Sie können git ls-treeden Baum aus früheren Sicherungen wiederherstellen. dann git mktree, um es erneut in Ihr aktuelles Repository zu schreiben.

Wenn der Typ ist begehen: das gleiche mit git show, git cat-fileund git commit-tree.

Natürlich würde ich Ihre ursprüngliche Arbeitskopie sichern, bevor ich diesen Prozess starte.

Schauen Sie sich auch an, wie Sie ein beschädigtes Blob-Objekt wiederherstellen können .

Daniel Fanjul
quelle
1
Danke, das hat mich gerettet! Ich werde meine genauen Schritte als separate Antwort veröffentlichen.
Christian
Nur eine Korrektur: Der Befehl für endet mit "erledigt" und nicht mit "Ende".
Felipe
Ich versuche dies zu tun, aber es .git/objects/pack/ist leer
kirill_igum
für mich a; fehlte nach git Unpack-Objekte -r <$ i
Mithrandir
@mithrandir: Wenn Sie das "erledigt" in die vorherige Zeile setzen: Ja, Sie benötigen ein Semikolon. Wenn Sie genau das eingeben, was ich geschrieben habe, tun Sie das nicht.
Daniel Fanjul
38

Banengusk brachte mich auf den richtigen Weg. Als weitere Referenz möchte ich die Schritte veröffentlichen, die ich unternommen habe, um die Beschädigung meines Repositorys zu beheben. Ich hatte das Glück, alle benötigten Objekte entweder in älteren Paketen oder in Repository-Backups zu finden.

# Unpack last non-corrupted pack
$ mv .git/objects/pack .git/objects/pack.old
$ git unpack-objects -r < .git/objects/pack.old/pack-012066c998b2d171913aeb5bf0719fd4655fa7d0.pack
$ git log
fatal: bad object HEAD

$ cat .git/HEAD 
ref: refs/heads/master

$ ls .git/refs/heads/

$ cat .git/packed-refs 
# pack-refs with: peeled 
aa268a069add6d71e162c4e2455c1b690079c8c1 refs/heads/master

$ git fsck --full 
error: HEAD: invalid sha1 pointer aa268a069add6d71e162c4e2455c1b690079c8c1
error: refs/heads/master does not point to a valid object!
missing blob 75405ef0e6f66e48c1ff836786ff110efa33a919
missing blob 27c4611ffbc3c32712a395910a96052a3de67c9b
dangling tree 30473f109d87f4bcde612a2b9a204c3e322cb0dc

# Copy HEAD object from backup of repository
$ cp repobackup/.git/objects/aa/268a069add6d71e162c4e2455c1b690079c8c1 .git/objects/aa
# Now copy all missing objects from backup of repository and run "git fsck --full" afterwards
# Repeat until git fsck --full only reports dangling objects

# Now garbage collect repo
$ git gc
warning: reflog of 'HEAD' references pruned commits
warning: reflog of 'refs/heads/master' references pruned commits
Counting objects: 3992, done.
Delta compression using 2 threads.
fatal: object bf1c4953c0ea4a045bf0975a916b53d247e7ca94 inconsistent object length (6093 vs 415232)
error: failed to run repack

# Check reflogs...
$ git reflog

# ...then clean
$ git reflog expire --expire=0 --all

# Now garbage collect again
$ git gc       
Counting objects: 3992, done.
Delta compression using 2 threads.
Compressing objects: 100% (3970/3970), done.
Writing objects: 100% (3992/3992), done.
Total 3992 (delta 2060), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.
# Done!
Christian
quelle
3
Hinzu kommt: Wenn das Backup die fehlenden Dateien in einem Paket enthält, ist der richtige Weg, um einen Blob aus dem Paket zu entfernen, "git cat-file blob <SHA1>> file.dat" und um ihn wieder in den beschädigten zu bringen repo, mach 'git hash-object -w file.dat', wie in Daniels Antwort.
Emil Styrke
Wie finden Sie das letzte nicht beschädigte Paket? danke
Romain Ourgorry
18

Versuchen Sie zunächst die folgenden Befehle (führen Sie sie bei Bedarf erneut aus):

$ git fsck --full
$ git gc
$ git gc --prune=today
$ git fetch --all
$ git pull --rebase

Und dann haben Sie immer noch die Probleme, versuchen Sie können:

  • Entfernen Sie alle beschädigten Objekte, z

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • Entfernen Sie alle leeren Objekte, z

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • Überprüfen Sie eine Nachricht "defekter Link" durch:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Hier erfahren Sie, aus welcher Datei der beschädigte Blob stammt!

  • Wenn Sie eine Datei wiederherstellen möchten, haben Sie möglicherweise großes Glück und es ist möglicherweise die Version, die Sie bereits in Ihrem Arbeitsbaum ausgecheckt haben:

    git hash-object -w my-magic-file
    

    wieder, und wenn es den fehlenden SHA1 (4b945 ..) ausgibt, sind Sie jetzt fertig!

  • Unter der Annahme, dass eine ältere Version defekt war, ist der einfachste Weg:

    git log --raw --all --full-history -- subdirectory/my-magic-file
    

    und das zeigt Ihnen das gesamte Protokoll für diese Datei (bitte beachten Sie, dass der Baum, den Sie hatten, möglicherweise nicht der Baum der obersten Ebene ist, sodass Sie selbst herausfinden müssen, in welchem ​​Unterverzeichnis er sich befand), dann können Sie das jetzt neu erstellen fehlendes Objekt mit Hash-Objekt wieder.

  • um eine Liste aller Refs mit fehlenden Commits, Bäumen oder Blobs zu erhalten:

    $ git for-each-ref --format='%(refname)' | while read ref; do git rev-list --objects $ref >/dev/null || echo "in $ref"; done
    

    Es ist möglicherweise nicht möglich, einige dieser Verweise mit den regulären Befehlen branch -d oder tag -d zu entfernen, da sie sterben, wenn git die Beschädigung bemerkt. Verwenden Sie stattdessen den Installationsbefehl git update-ref -d $ ref. Beachten Sie, dass bei lokalen Verzweigungen dieser Befehl möglicherweise die veraltete Zweigkonfiguration in .git / config zurücklässt. Es kann manuell gelöscht werden (siehe Abschnitt [Zweig "$ ref"]).

  • Nachdem alle Refs sauber sind, kann es immer noch zu fehlerhaften Commits im Reflog kommen. Sie können alle Reflogs mit git reflog expire --expire = now --all löschen. Wenn Sie nicht alle Reflogs verlieren möchten, können Sie die einzelnen Refs nach defekten Reflogs durchsuchen:

    $ (echo HEAD; git for-each-ref --format='%(refname)') | while read ref; do git rev-list -g --objects $ref >/dev/null || echo "in $ref"; done
    

    (Beachten Sie die hinzugefügte Option -g zur git-Rev-Liste.) Verwenden Sie dann git reflog expire --expire = now $ ref für jede dieser Optionen. Wenn alle defekten Refs und Reflogs verschwunden sind, führen Sie git fsck --full aus, um zu überprüfen, ob das Repository sauber ist. Baumelnde Objekte sind in Ordnung.


Im Folgenden finden Sie eine erweiterte Verwendung von Befehlen, die möglicherweise zum Verlust Ihrer Daten in Ihrem Git-Repository führen können, wenn sie nicht mit Bedacht verwendet werden. Erstellen Sie daher ein Backup, bevor Sie versehentlich weitere Schäden an Ihrem Git anrichten. Probieren Sie es auf eigenes Risiko aus, wenn Sie wissen, was Sie tun.


So ziehen Sie den aktuellen Zweig nach dem Abrufen über den vorgelagerten Zweig:

$ git pull --rebase

Sie können auch versuchen, einen neuen Zweig auszuchecken und den alten zu löschen:

$ git checkout -b new_master origin/master

Versuchen Sie den folgenden Befehl, um das beschädigte Objekt in git zum Entfernen zu finden:

while [ true ]; do f=`git fsck --full 2>&1|awk '{print $3}'|sed -r 's/(^..)(.*)/objects\/\1\/\2/'`; if [ ! -f "$f" ]; then break; fi; echo delete $f; rm -f "$f"; done

Verwenden Sie für OSX sed -Eanstelle von sed -r.


Eine andere Idee ist, alle Objekte aus Packdateien zu entpacken, um alle Objekte in .git / object neu zu generieren. Versuchen Sie daher, die folgenden Befehle in Ihrem Repository auszuführen:

$ cp -fr .git/objects/pack .git/objects/pack.bak
$ for i in .git/objects/pack.bak/*.pack; do git unpack-objects -r < $i; done
$ rm -frv .git/objects/pack.bak

Wenn dies nicht hilft, können Sie versuchen, die Git-Objekte von einem anderen Repo zu synchronisieren oder zu kopieren, z

$ rsync -varu git_server:/path/to/git/.git local_git_repo/
$ rsync -varu /local/path/to/other-working/git/.git local_git_repo/
$ cp -frv ../other_repo/.git/objects .git/objects

So beheben Sie den defekten Zweig beim Auschecken wie folgt:

$ git checkout -f master
fatal: unable to read tree 5ace24d474a9535ddd5e6a6c6a1ef480aecf2625

Versuchen Sie es erneut zu entfernen und aus dem Upstream auszuchecken:

$ git branch -D master
$ git checkout -b master github/master

Falls Git Sie in einen getrennten Zustand versetzt, checken Sie den ab masterund führen Sie den getrennten Zweig darin zusammen.


Eine andere Idee ist, den vorhandenen Master rekursiv neu zu gründen:

$ git reset HEAD --hard
$ git rebase -s recursive -X theirs origin/master

Siehe auch:

Kenorb
quelle
2

Hier sind die Schritte, die ich ausgeführt habe, um ein beschädigtes Blob-Objekt wiederherzustellen.

1) Identifizieren Sie einen beschädigten Blob

git fsck --full
  error: inflate: data stream error (incorrect data check)
  error: sha1 mismatch 241091723c324aed77b2d35f97a05e856b319efd
  error: 241091723c324aed77b2d35f97a05e856b319efd: object corrupt or missing
  ...

Der beschädigte Blob lautet 241091723c324aed77b2d35f97a05e856b319efd

2) Bewegen Sie den beschädigten Blob an einen sicheren Ort (nur für den Fall)

mv .git/objects/24/1091723c324aed77b2d35f97a05e856b319efd ../24/

3) Holen Sie sich Eltern von korrupten Blob

git fsck --full
  Checking object directories: 100% (256/256), done.
  Checking objects: 100% (70321/70321), done.
  broken link from    tree 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180
              to    blob 241091723c324aed77b2d35f97a05e856b319efd

Der übergeordnete Hash lautet 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Holen Sie sich den Dateinamen, der dem beschädigten Blob entspricht

git ls-tree 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180
  ...
  100644 blob 241091723c324aed77b2d35f97a05e856b319efd    dump.tar.gz
  ...

Suchen Sie diese bestimmte Datei in einem Backup oder im Upstream-Git-Repository (in meinem Fall dump.tar.gz ). Kopieren Sie es dann irgendwo in Ihr lokales Repository.

5) Fügen Sie zuvor beschädigte Dateien zur Git-Objektdatenbank hinzu

git hash-object -w dump.tar.gz

6) Feiern!

git gc
  Counting objects: 75197, done.
  Compressing objects: 100% (21805/21805), done.
  Writing objects: 100% (75197/75197), done.
  Total 75197 (delta 52999), reused 69857 (delta 49296)
Jonathan Maim
quelle
Das hat bei mir nicht funktioniert. Schritt 4 führte dazu git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152d, und ich versuchte es auch noch einmal ohne Schritt 2, und das führte zugit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan
1

Git Checkout kann tatsächlich einzelne Dateien aus einer Revision auswählen. Geben Sie einfach den Commit-Hash und den Dateinamen ein. Nähere Infos hier.

Ich denke, der einfachste Weg, dies sicher zu beheben, besteht darin, auf die neueste nicht festgeschriebene Sicherung zurückzugreifen und dann selektiv nicht beschädigte Dateien aus neueren Commits auszuwählen. Viel Glück!

Tim Lin
quelle
1

Hier sind zwei Funktionen, die hilfreich sein können, wenn Ihre Sicherung beschädigt ist oder wenn Sie auch einige teilweise beschädigte Sicherungen haben (dies kann passieren, wenn Sie die beschädigten Objekte sichern).

Führen Sie beide in dem Repo aus, das Sie wiederherstellen möchten.

Standardwarnung: Nur verwenden, wenn Sie wirklich verzweifelt sind und Ihr (beschädigtes) Repo gesichert haben. Dies löst möglicherweise nichts, sollte aber zumindest den Grad der Korruption hervorheben.

fsck_rm_corrupted() {
    corrupted='a'
    while [ "$corrupted" ]; do
        corrupted=$(                                  \
        git fsck --full --no-dangling 2>&1 >/dev/null \
            | grep 'stored in'                          \
            | sed -r 's:.*(\.git/.*)\).*:\1:'           \
        )
        echo "$corrupted"
        rm -f "$corrupted"
    done
}

if [ -z "$1" ]  || [ ! -d "$1" ]; then
    echo "'$1' is not a directory. Please provide the directory of the git repo"
    exit 1
fi

pushd "$1" >/dev/null
fsck_rm_corrupted
popd >/dev/null

und

unpack_rm_corrupted() {
    corrupted='a'
    while [ "$corrupted" ]; do
        corrupted=$(                                  \
        git unpack-objects -r < "$1" 2>&1 >/dev/null \
            | grep 'stored in'                          \
            | sed -r 's:.*(\.git/.*)\).*:\1:'           \
        )
        echo "$corrupted"
        rm -f "$corrupted"
    done
}

if [ -z "$1" ]  || [ ! -d "$1" ]; then
    echo "'$1' is not a directory. Please provide the directory of the git repo"
    exit 1
fi

for p in $1/objects/pack/pack-*.pack; do
    echo "$p"
    unpack_rm_corrupted "$p"
done
go2null
quelle
0

Ich habe dieses Problem behoben, um einige Änderungen wie git add -A und git commit erneut hinzuzufügen.

Dmitriy S.
quelle