Як відновити об'єкти 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/порожній
kirill_igum

для мене a; був відсутній після git unpack-objects -r <$ i
mithrandir

@mithrandir: якщо ви поставили "зроблено" у попередньому рядку: так, вам потрібна крапка з комою. Якщо ви вводите саме те, що я писав, ви цього не робите.
Даніель Фанджул

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
Додавання до цього: якщо в резервній копії відсутні файли в пакеті, правильним способом витягування крапки з пакета є 'git cat-file blob <SHA1>> file.dat' і повернення його назад у пошкоджений repo, зробіть 'git hash-object -w file.dat', як у відповіді Даніеля.
Emil Styrke

Як знайти останню непошкоджену пачку? спасибі
Ромен Нашгоррі

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 = тепер $ 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

Дивитися також:


2

Ось кроки, які я виконував для відновлення з пошкодженого об’єкта BLOB.

1) Визначте пошкоджену крапку

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

Пошкоджена крапка - це 241091723c324aed77b2d35f97a05e856b319efd

2) Перемістіть пошкоджену крапку в безпечне місце (про всяк випадок)

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) Отримати ім’я файлу, яке відповідає пошкодженій крапці

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

0

Я вирішив цю проблему, щоб додати деякі зміни, такі як git add -A та git commit знову.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.