Как восстановить объекты Git, поврежденные в результате сбоя жесткого диска?

92

У меня произошел сбой жесткого диска, в результате которого были повреждены некоторые файлы репозитория Git. При запуске git fsck --fullя получаю следующий результат:

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

У меня есть резервные копии репозитория, но единственная резервная копия, которая включает файл пакета, уже повреждена. Поэтому я думаю, что мне нужно найти способ получить отдельные объекты из разных резервных копий и каким-то образом проинструктировать Git создать новый пакет только с правильными объектами.

Не могли бы вы подсказать, как исправить мой репозиторий?

Христианин
источник
2
Это случилось со мной. Я не хочу связываться с объектами git ... поэтому повторно клонировал проект из удаленного репозитория в новую папку, а затем просто скопировал все файлы из моих проблемных репозиториев (за исключением .gitпапки, конечно) в только что клонированное репо ... а затем сделал git statusв новом репо ... git правильно обнаруживает все затронутые изменения в моих файлах, и я могу снова начать свою работу.
Росди Касим

Ответы:

82

В некоторых предыдущих резервных копиях плохие объекты могли быть упакованы в другие файлы или еще могут быть незакрепленными. Так что ваши объекты могут быть восстановлены.

Кажется, в вашей базе данных есть несколько плохих объектов. Так что вы можете сделать это вручную.

Из - за git hash-object, git mktreeи git commit-treeне писать объекты потому , что они находятся в пакете, а затем начать делать это:

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

(Ваши пакеты перемещаются из репозитория и снова распаковываются в нем; теперь в базе данных находятся только хорошие объекты)

Ты можешь сделать:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

и проверьте тип объекта.

Если типом является blob: получить содержимое файла из предыдущих резервных копий (с помощью git showили git cat-fileили git unpack-file; тогда вы можете git hash-object -wпереписать объект в текущем репозитории.

Если тип - дерево: вы можете использовать его git ls-treeдля восстановления дерева из предыдущих резервных копий; затем git mktreeснова записать его в текущий репозиторий.

Если тип фиксации: то же самое с git show, git cat-fileи git commit-tree.

Конечно, я бы сделал резервную копию вашей исходной рабочей копии перед тем, как начать этот процесс.

Также ознакомьтесь с разделом «Как восстановить поврежденный объект Blob» .

Даниэль Фанжул
источник
1
Спасибо, что меня спасли! Я опубликую свои точные шаги в виде отдельного ответа.
Кристиан,
Небольшая поправка: команда для заканчивается на «готово», а не на «конец».
Фелипе
я пытаюсь сделать это, но ничего .git/objects/pack/не
найдено
для меня a; отсутствовал после git unpack-objects -r <$ i
mithrandir
@mithrandir: если вы поставили "готово" в предыдущей строке: да, вам нужна точка с запятой. Если вы напечатаете именно то, что я написал, вы этого не сделаете.
Daniel Fanjul
38

Баненгуск направил меня на верный путь. Для получения дополнительной информации я хочу опубликовать шаги, которые я предпринял, чтобы исправить повреждение моего репозитория. Мне посчастливилось найти все необходимые объекты либо в старых пакетах, либо в резервных копиях репозитория.

# 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!
Христианин
источник
3
Дополнение к этому: если в резервной копии есть недостающие файлы в пакете, правильный способ получить blob из пакета - это 'git cat-file blob <SHA1>> file.dat' и вернуть его в поврежденный репо, выполните 'git hash-object -w file.dat', как в ответе Даниэля.
Эмиль Стырке
Как найти последний неповрежденный пакет? спасибо
Romain Ourgorry
18

Сначала попробуйте выполнить следующие команды (при необходимости повторите попытку):

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

И тогда у вас все еще есть проблемы, попробуйте:

  • удалите все поврежденные объекты, например

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • удалите все пустые объекты, например

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • проверьте сообщение о неработающей ссылке:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Это сообщит вам, из какого файла был получен поврежденный объект!

  • для восстановления файла вам может очень повезти, и это может быть та версия, которую вы уже отметили в своем рабочем дереве:

    git hash-object -w my-magic-file
    

    снова, и если он выводит отсутствующий SHA1 (4b945 ..), все готово!

  • предполагая, что это была некоторая более старая версия, которая была сломана, самый простой способ сделать это - сделать:

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

    и это покажет вам весь журнал для этого файла (пожалуйста, поймите, что дерево, которое у вас было, может не быть деревом верхнего уровня, поэтому вам нужно выяснить, в каком подкаталоге оно находилось самостоятельно), теперь вы можете воссоздать снова отсутствует объект с хеш-объектом.

  • чтобы получить список всех ссылок с отсутствующими коммитами, деревьями или каплями:

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

    Возможно, невозможно удалить некоторые из этих ссылок с помощью обычных команд branch -d или tag -d, поскольку они умрут, если git заметит повреждение. Поэтому используйте команду сантехники git update-ref -d $ ref. Обратите внимание, что в случае локальных веток эта команда может оставить устаревшую конфигурацию ветки в .git / config. Его можно удалить вручную (ищите раздел [ветка «$ ref»]).

  • После того, как все ссылки будут чистыми, в журнале ссылок все еще могут быть неработающие коммиты. Вы можете очистить все рефлоги, используя git reflog expire --expire = now --all. Если вы не хотите терять все свои журналы, вы можете поискать в отдельных ссылках сломанные журналы:

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

    (Обратите внимание на добавленную опцию -g для git rev-list.) Затем используйте git reflog expire --expire = now $ ref для каждого из них. Когда все сломанные ссылки и журналы исчезнут, запустите git fsck --full, чтобы проверить чистоту репозитория. Висячие предметы в порядке.


Ниже вы можете найти расширенное использование команд, которые потенциально могут привести к потере ваших данных в вашем репозитории git, если их не использовать с умом, поэтому сделайте резервную копию, прежде чем вы случайно нанесете дополнительный ущерб вашему git. Попробуйте на свой страх и риск, если знаете, что делаете.


Чтобы перетащить текущую ветку поверх восходящей ветки после выборки:

$ git pull --rebase

Вы также можете попробовать проверить новую ветку и удалить старую:

$ git checkout -b new_master origin/master

Чтобы найти поврежденный объект в git для удаления, попробуйте следующую команду:

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

Для OSX используйте sed -Eвместо sed -r.


Другая идея - распаковать все объекты из файлов пакета, чтобы восстановить все объекты внутри .git / objects, поэтому попробуйте выполнить следующие команды в своем репозитории:

$ 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

Если приведенное выше не помогает, вы можете попробовать rsync или скопировать объекты git из другого репо, например

$ 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

Чтобы исправить сломанную ветку при попытке оформления заказа, выполните следующие действия:

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

Попробуйте удалить его и снова оформить заказ из восходящего потока:

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

В случае, если git переведет вас в отдельное состояние, проверьте masterи объедините с ним отдельную ветку.


Другая идея - рекурсивно переустановить существующий мастер:

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

Смотрите также:

Kenorb
источник
2

Вот шаги, которые я выполнил для восстановления после поврежденного объекта blob.

1) Определите поврежденный blob

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

Поврежденный большой двоичный объект - 241091723c324aed77b2d35f97a05e856b319efd

2) Переместите испорченный BLOB-объект в безопасное место (на всякий случай)

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

3) Получить родительский объект поврежденной капли

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

Родительский хеш - 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Получите имя файла, соответствующее поврежденному BLOB-объекту

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

Найдите этот конкретный файл в резервной копии или в исходном репозитории git (в моем случае это dump.tar.gz ). Затем скопируйте его где-нибудь в локальном репозитории.

5) Добавить ранее поврежденный файл в базу объектов git

git hash-object -w dump.tar.gz

6) Празднуйте!

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)
Джонатан Мэйм
источник
У меня это не сработало. Результатом стал шаг 4 git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152d, и я также попробовал все это снова, без шага 2, и это привело кgit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Райан
1

Git checkout действительно может выбирать отдельные файлы из ревизии. Просто дайте ему хеш фиксации и имя файла. Более подробная информация здесь.

Я думаю, что самый простой способ безопасно исправить это - вернуться к новейшей незафиксированной резервной копии, а затем выборочно выбрать неповрежденные файлы из новых коммитов. Удачи!

Тим Лин
источник
1

Вот две функции, которые могут помочь, если ваша резервная копия повреждена или у вас есть несколько частично поврежденных резервных копий (это может произойти при резервном копировании поврежденных объектов).

Запустите оба в репо, которое вы пытаетесь восстановить.

Стандартное предупреждение: используйте только в том случае, если вы действительно в отчаянии и сделали резервную копию своего (поврежденного) репо. Это может ничего не решить, но, по крайней мере, должно подчеркнуть уровень коррупции.

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

а также

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
источник
0

Я решил эту проблему, чтобы снова добавить некоторые изменения, например git add -A и git commit.

Дмитрий С
источник