Làm thế nào để khôi phục các đối tượng Git bị hỏng do hỏng đĩa cứng?


92

Tôi đã gặp sự cố đĩa cứng dẫn đến một số tệp của kho lưu trữ Git bị hỏng. Khi chạy git fsck --fulltôi nhận được kết quả sau:

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

Tôi có các bản sao lưu của kho lưu trữ, nhưng bản sao lưu duy nhất bao gồm tệp gói đã bị hỏng. Vì vậy, tôi nghĩ rằng tôi phải tìm ra cách để lấy các đối tượng đơn lẻ từ các bản sao lưu khác nhau và bằng cách nào đó hướng dẫn Git tạo một gói mới chỉ có các đối tượng chính xác.

Bạn có thể vui lòng cho tôi gợi ý về cách sửa chữa kho lưu trữ của tôi không?


2
Nó vừa mới xảy ra với tôi. Tôi không muốn gây rối với các đối tượng git ... vì vậy hãy nhân bản lại dự án từ kho lưu trữ từ xa vào một thư mục mới, sau đó chỉ cần sao chép tất cả các tệp từ các kho lưu trữ có vấn đề của tôi ( .gittất nhiên là ngoại trừ thư mục) vào kho mới được nhân bản ... và sau đó thực hiện git statustrong repo mới ... git phát hiện chính xác tất cả các thay đổi bị ảnh hưởng đối với tệp của tôi và tôi có thể bắt đầu lại công việc của mình.
Rosdi Kasim

Câu trả lời:


82

Trong một số bản sao lưu trước đó, các đối tượng xấu của bạn có thể đã được đóng gói trong các tệp khác nhau hoặc có thể là các đối tượng lỏng lẻo. Vì vậy, các đối tượng của bạn có thể được phục hồi.

Có vẻ như có một vài đối tượng xấu trong cơ sở dữ liệu của bạn. Vì vậy, bạn có thể làm điều đó theo cách thủ công.

Bởi vì git hash-object, git mktreegit commit-treekhông viết các đối tượng vì chúng được tìm thấy trong gói, sau đó bắt đầu làm điều này:

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

(Các gói của bạn được chuyển ra khỏi kho và giải nén lại trong đó; chỉ những đối tượng tốt hiện có trong cơ sở dữ liệu)

Bạn có thể làm:

git cat-file -t 6c8cae4994b5ec7891ccb1527d30634997a978ee

và kiểm tra loại đối tượng.

Nếu loại là blob: truy xuất nội dung của tệp từ các bản sao lưu trước đó (với git showhoặc git cat-filehoặc git unpack-file; thì bạn có thể git hash-object -wviết lại đối tượng trong kho lưu trữ hiện tại của mình.

Nếu loại là cây: bạn có thể sử dụng git ls-treeđể khôi phục cây từ các bản sao lưu trước đó; sau đó git mktreeviết lại nó trong kho lưu trữ hiện tại của bạn.

Nếu kiểu là commit: giống với git show, git cat-filegit commit-tree.

Tất nhiên, tôi sẽ sao lưu bản sao làm việc ban đầu của bạn trước khi bắt đầu quá trình này.

Ngoài ra, hãy xem Cách khôi phục đối tượng Blob bị hỏng .


1
Cảm ơn bạn, điều đó đã cứu tôi! Tôi sẽ đăng các bước chính xác của tôi như một câu trả lời riêng biệt.
Christian

Chỉ là một sửa chữa: lệnh for kết thúc bằng "done" chứ không phải "end".
Felipe

tôi đang cố gắng làm điều này nhưng .git/objects/pack/trống rỗng
kirill_igum

cho tôi một; bị mất tích sau khi git unpack-objects -r <$ i
mithrandir

@mithrandir: nếu bạn đặt 'done' ở dòng trước: vâng, bạn cần dấu chấm phẩy. Nếu bạn gõ chính xác những gì tôi đã viết, bạn không.
Daniel Fanjul

38

Banengusk đã đưa tôi đi đúng hướng. Để tham khảo thêm, tôi muốn đăng các bước tôi đã thực hiện để sửa lỗi kho lưu trữ của mình. Tôi đủ may mắn để tìm thấy tất cả các đối tượng cần thiết trong các gói cũ hơn hoặc trong các bản sao lưu kho lưu trữ.

# 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
Thêm vào điều này: Nếu bản sao lưu có các tệp bị thiếu trong một gói, cách thích hợp để lấy một đốm màu ra khỏi gói là 'git cat-file blob <SHA1>> file.dat' và đưa nó trở lại chỗ bị hỏng repo, làm 'git hash-object -w file.dat', như trong câu trả lời của Daniel.
Emil Styrke

Làm thế nào để bạn tìm thấy gói cuối cùng không bị hỏng? cảm ơn
Romain Ourg Xin lỗi,

18

Trước tiên, hãy thử các lệnh sau (chạy lại nếu cần):

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

Và sau đó bạn vẫn gặp sự cố, hãy thử có thể:

  • loại bỏ tất cả các đối tượng bị hỏng, ví dụ:

    fatal: loose object 91c5...51e5 (stored in .git/objects/06/91c5...51e5) is corrupt
    $ rm -v .git/objects/06/91c5...51e5
    
  • loại bỏ tất cả các đối tượng trống, ví dụ:

    error: object file .git/objects/06/91c5...51e5 is empty
    $ find .git/objects/ -size 0 -exec rm -vf "{}" \;
    
  • kiểm tra thông báo "liên kết bị hỏng" bằng cách:

    git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8
    

    Điều này sẽ cho bạn biết tệp bị hỏng đến từ đâu!

  • để khôi phục tệp, bạn có thể thực sự may mắn và nó có thể là phiên bản mà bạn đã kiểm tra trong cây làm việc của mình:

    git hash-object -w my-magic-file
    

    một lần nữa, và nếu nó xuất ra SHA1 bị thiếu (4b945 ..) thì bây giờ bạn đã hoàn tất!

  • giả sử rằng đó là một số phiên bản cũ hơn đã bị hỏng, cách dễ nhất để làm điều đó là:

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

    và điều đó sẽ hiển thị cho bạn toàn bộ nhật ký cho tệp đó (vui lòng nhận ra rằng cây bạn có có thể không phải là cây cấp cao nhất, vì vậy bạn cần phải tự mình tìm ra thư mục con nào của nó), sau đó bạn có thể tạo lại thiếu đối tượng với đối tượng băm một lần nữa.

  • để có được danh sách tất cả các giới thiệu bị thiếu cam kết, cây hoặc đốm màu:

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

    Có thể không xóa được một số ref đó bằng cách sử dụng các lệnh branch -d hoặc tag -d thông thường, vì chúng sẽ chết nếu git nhận thấy lỗi. Vì vậy, hãy sử dụng lệnh ống nước git update-ref -d $ ref để thay thế. Lưu ý rằng trong trường hợp các nhánh cục bộ, lệnh này có thể để lại cấu hình nhánh cũ trong .git / config. Nó có thể được xóa theo cách thủ công (tìm phần [nhánh "$ ref"]).

  • Sau khi tất cả các ref đã sạch, vẫn có thể có các cam kết bị hỏng trong reflog. Bạn có thể xóa tất cả các nhật ký bằng git reflog expire --expire = now --all. Nếu bạn không muốn mất tất cả các bản trích dẫn của mình, bạn có thể tìm kiếm các bản giới thiệu riêng lẻ để tìm các bản ghi bị hỏng:

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

    (Lưu ý rằng tùy chọn -g được thêm vào danh sách git rev.) Sau đó, sử dụng git reflog expire --expire = now $ ref trên mỗi cái đó. Khi tất cả refs và reflog bị hỏng biến mất, hãy chạy git fsck --full để kiểm tra xem kho lưu trữ có sạch không. Đối tượng nguy hiểm là Ok.


Dưới đây, bạn có thể tìm thấy cách sử dụng nâng cao của các lệnh có khả năng gây mất dữ liệu trong kho lưu trữ git của bạn nếu không được sử dụng một cách khôn ngoan, vì vậy hãy sao lưu trước khi bạn vô tình làm hỏng thêm git của mình. Hãy tự mạo hiểm nếu bạn biết mình đang làm gì.


Để kéo nhánh hiện tại lên trên nhánh ngược dòng sau khi tìm nạp:

$ git pull --rebase

Bạn cũng có thể thử kiểm tra chi nhánh mới và xóa chi nhánh cũ:

$ git checkout -b new_master origin/master

Để tìm đối tượng bị hỏng trong git để xóa, hãy thử lệnh sau:

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

Đối với OSX, hãy sử dụng sed -Ethay vì sed -r.


Ý tưởng khác là giải nén tất cả các đối tượng từ các tệp gói để tạo lại tất cả các đối tượng bên trong .git / objects, vì vậy hãy thử chạy các lệnh sau trong kho lưu trữ của bạn:

$ 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

Nếu cách trên không hữu ích, bạn có thể thử rsync hoặc sao chép các đối tượng git từ một repo khác, ví dụ:

$ 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

Để khắc phục nhánh bị hỏng khi thử kiểm tra như sau:

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

Hãy thử xóa nó và thanh toán lại từ ngược dòng:

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

Trong trường hợp nếu git đưa bạn vào trạng thái tách rời, hãy kiểm tra mastervà hợp nhất vào nó nhánh tách rời.


Một ý tưởng khác là căn cứ lại đệ quy cái hiện có một cách đệ quy:

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

Xem thêm:


2

Dưới đây là các bước tôi đã làm theo để khôi phục từ một đối tượng đốm màu bị hỏng.

1) Xác định đốm màu bị hỏng

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

Tham nhũng blob là 241091723c324aed77b2d35f97a05e856b319efd

2) Di chuyển đốm màu bị hỏng đến một nơi an toàn (đề phòng)

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

3) Nhận cha mẹ của đốm màu bị hỏng

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

Băm mẹ là 0716831e1a6c8d3e6b2b541d21c4748cc0ce7180 .

4) Nhận tên tệp tương ứng với đốm màu bị hỏng

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

Tìm tệp cụ thể này trong bản sao lưu hoặc trong kho lưu trữ git ngược dòng (trong trường hợp của tôi là dump.tar.gz ). Sau đó sao chép nó ở đâu đó bên trong kho lưu trữ cục bộ của bạn.

5) Thêm tệp bị hỏng trước đó trong cơ sở dữ liệu đối tượng git

git hash-object -w dump.tar.gz

6) Ăn mừng!

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)

Điều này không hiệu quả với tôi. Bước 4 dẫn đến git ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: Could not read 19505205fd1f219993da9b75846fff3cf432152d, và tôi cũng đã thử nó trên một lần nữa mà không bước 2, và điều đó dẫn đếngit ls-tree 9504a07fb803edfdf0c1dd99c5d561274af87982 error: inflate: data stream error (invalid stored block lengths) fatal: failed to read object 19505205fd1f219993da9b75846fff3cf432152d: Invalid argument
Ryan

1

Git checkout thực sự có thể chọn các tệp riêng lẻ từ một bản sửa đổi. Chỉ cần cung cấp cho nó băm cam kết và tên tệp. Thông tin chi tiết hơn ở đây.

Tôi đoán cách dễ nhất để khắc phục điều này một cách an toàn là hoàn nguyên về bản sao lưu không giới hạn mới nhất và sau đó chọn lọc các tệp không bị gián đoạn từ các cam kết mới hơn. Chúc may mắn!


1

Đây là hai chức năng có thể hữu ích nếu bản sao lưu của bạn bị hỏng hoặc bạn cũng có một vài bản sao lưu bị hỏng một phần (điều này có thể xảy ra nếu bạn sao lưu các đối tượng bị hỏng).

Chạy cả hai trong repo mà bạn đang cố khôi phục.

Cảnh báo tiêu chuẩn: chỉ sử dụng nếu bạn thực sự tuyệt vọng và bạn đã sao lưu repo (bị hỏng) của mình. Điều này có thể không giải quyết được gì, nhưng ít nhất cũng phải làm nổi bật mức độ tham nhũng.

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

Tôi đã giải quyết vấn đề này để thêm một số thay đổi như git add -A và git commit lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.