วิธีการกู้คืนวัตถุ 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โฟลเดอร์แน่นอน) ลงใน repo ที่โคลนใหม่ ... แล้วทำgit statusใน repo ใหม่ ... git ตรวจพบการเปลี่ยนแปลงที่ได้รับผลกระทบทั้งหมดในไฟล์ของฉันอย่างถูกต้องและฉันสามารถเริ่มงานได้อีกครั้ง
Rosdi Kasim

คำตอบ:


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-filegit commit-tree

แน่นอนฉันจะสำรองสำเนาการทำงานเดิมของคุณก่อนที่จะเริ่มกระบวนการนี้

นอกจากนี้จะดูที่วิธีการกู้คืนความเสียหายหยดวัตถุ


1
ขอบคุณที่ช่วยฉัน! ฉันจะโพสต์ขั้นตอนที่แน่นอนของฉันเป็นคำตอบแยกต่างหาก
คริสเตียน

เพียงการแก้ไข: คำสั่งสำหรับลงท้ายด้วย "เสร็จสิ้น" ไม่ใช่ "สิ้นสุด"
Felipe

ฉันกำลังพยายามทำสิ่งนี้ แต่.git/objects/pack/ว่างเปล่า
kirill_igum

สำหรับฉัน; หายไปหลังจาก git unpack-objects -r <$ i
mithrandir

@mithrandir: ถ้าคุณใส่ 'done' ในบรรทัดก่อนหน้า: ใช่คุณต้องมีอัฒภาค ถ้าคุณพิมพ์ตรงตามที่ฉันเขียนคุณจะไม่ทำ
Daniel Fanjul

38

Banenguskทำให้ฉันมาถูกทาง สำหรับการอ้างอิงเพิ่มเติมฉันต้องการโพสต์ขั้นตอนที่ฉันดำเนินการเพื่อแก้ไขความเสียหายที่เก็บของฉัน ฉันโชคดีพอที่จะพบอ็อบเจ็กต์ที่จำเป็นทั้งหมดทั้งในแพ็คเก่าหรือในการสำรองข้อมูล

# 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' เช่นเดียวกับในคำตอบของ Daniel
Emil Styrke

คุณจะพบแพ็คสุดท้ายที่ไม่เสียหายได้อย่างไร? ขอบคุณ
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
    

    และนั่นจะแสดงบันทึกทั้งหมดสำหรับไฟล์นั้น (โปรดทราบว่าทรีที่คุณมีอาจไม่ใช่ทรีระดับบนสุดดังนั้นคุณต้องหาไดเร็กทอรีย่อยที่อยู่ในไดเร็กทอรีด้วยตัวคุณเอง) จากนั้นคุณสามารถสร้าง ไม่มีวัตถุที่มีแฮชวัตถุอีกครั้ง

  • เพื่อรับรายชื่อการอ้างอิงทั้งหมดที่ขาดหายไปต้นไม้หรือ blobs:

    $ 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 ปกติเนื่องจากจะตายหากคอมไพล์สังเกตเห็นความเสียหาย ดังนั้นให้ใช้คำสั่ง plumbing git update-ref -d $ ref แทน โปรดทราบว่าในกรณีของสาขาภายในคำสั่งนี้อาจทิ้งการกำหนดค่าสาขาเก่าไว้ใน. git / config สามารถลบได้ด้วยตนเอง (ค้นหาส่วน [สาขา "$ ref"])

  • หลังจากการอ้างอิงทั้งหมดสะอาดแล้วอาจยังมีการคอมมิชที่เสียใน reflog คุณสามารถล้าง reflogs ทั้งหมดโดยใช้ git reflog expire --expire = now --all หากคุณไม่ต้องการสูญเสียการอ้างอิงทั้งหมดของคุณคุณสามารถค้นหาการอ้างอิงแต่ละรายการเพื่อหา reflogs ที่ไม่สมบูรณ์:

    $ (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 กับแต่ละรายการ เมื่อ refs และ reflogs ที่เสียทั้งหมดหายไปให้รัน git fsck --full เพื่อตรวจสอบว่าที่เก็บนั้นสะอาด การห้อยสิ่งของก็โอเค


ด้านล่างนี้คุณสามารถค้นหาการใช้คำสั่งขั้นสูงซึ่งอาจทำให้ข้อมูลของคุณสูญหายในที่เก็บ git ของคุณหากไม่ได้ใช้อย่างชาญฉลาดดังนั้นให้ทำการสำรองข้อมูลก่อนที่คุณจะสร้างความเสียหายต่อคอมไพล์ของคุณโดยไม่ได้ตั้งใจ ลองเสี่ยงเองถ้าคุณรู้ว่ากำลังทำอะไรอยู่


ในการดึงสาขาปัจจุบันที่ด้านบนของสาขาต้นน้ำหลังจากดึงข้อมูล:

$ git pull --rebase

คุณอาจลองชำระเงินสาขาใหม่และลบสาขาเก่า:

$ git checkout -b new_master origin/master

หากต้องการค้นหาวัตถุที่เสียหายในคอมไพล์สำหรับการลบให้ลองใช้คำสั่งต่อไปนี้:

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 / อ็อบเจ็กต์ดังนั้นลองรันคำสั่งต่อไปนี้ภายในที่เก็บของคุณ:

$ 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 จาก repo อื่นเช่น

$ 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

ในกรณีที่คอมไพล์ทำให้คุณเข้าสู่สถานะแยกออกให้ชำระเงินmasterและรวมเข้ากับสาขาแยก


อีกแนวคิดหนึ่งคือการสร้างฐานต้นแบบที่มีอยู่ซ้ำ:

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

ดูสิ่งนี้ด้วย:


2

นี่คือขั้นตอนที่ฉันทำตามเพื่อกู้คืนจากวัตถุที่เสียหาย

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 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180

4) รับชื่อไฟล์ที่สอดคล้องกับ blob ที่เสียหาย

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

ค้นหาไฟล์นี้ในการสำรองข้อมูลหรือในที่เก็บ git ต้นน้ำ (ในกรณีของฉันคือdump.tar.gz ) จากนั้นคัดลอกไว้ที่ใดที่หนึ่งในที่เก็บในเครื่องของคุณ

5) เพิ่มไฟล์ที่เสียหายก่อนหน้านี้ในฐานข้อมูลวัตถุคอมไพล์

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
Ryan

1

Git checkout สามารถเลือกไฟล์แต่ละไฟล์จากการแก้ไขได้ เพียงแค่ระบุแฮชคอมมิตและชื่อไฟล์ ข้อมูลรายละเอียดเพิ่มเติมที่นี่

ฉันเดาว่าวิธีที่ง่ายที่สุดในการแก้ไขปัญหานี้อย่างปลอดภัยคือการเปลี่ยนกลับเป็นการสำรองข้อมูลที่ไม่ได้รับคำแนะนำใหม่ล่าสุดจากนั้นเลือกไฟล์ที่ไม่เสียหายจากคอมมิตใหม่ โชคดี!


1

ต่อไปนี้เป็นฟังก์ชันสองอย่างที่อาจช่วยได้หากการสำรองข้อมูลของคุณเสียหายหรือคุณมีการสำรองข้อมูลที่เสียหายบางส่วนเช่นกัน (อาจเกิดขึ้นหากคุณสำรองข้อมูลวัตถุที่เสียหาย)

เรียกใช้ทั้งสองอย่างใน repo ที่คุณพยายามกู้คืน

คำเตือนมาตรฐาน: ใช้เฉพาะเมื่อคุณหมดหวังจริงๆและคุณได้สำรอง repo ของคุณ (เสียหาย) แล้ว สิ่งนี้อาจไม่สามารถแก้ไขอะไรได้ แต่อย่างน้อยควรเน้นระดับการทุจริต

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 คอมมิตอีกครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.