Bagaimana cara memperbaiki repositori git yang rusak?

104

Saya mencoba mengkloning repositori saya yang saya simpan di satu folder Ubuntu saya ke mesin baru dan saya mendapatkan ini:

christopher@christopher-laptop:~/source/personal$ git clone ~/Ubuntu\ One\ Side\ Work/projects.git/
Cloning into 'projects'...
done.
fatal: unable to read tree 29a422c19251aeaeb907175e9b3219a9bed6c616
christopher@christopher-laptop:~/source/personal$ 

Jadi saya mencoba melihat banyak pertanyaan lain seperti ini yang telah ditanyakan di sini dan kebanyakan dari mereka mengatakan untuk lari git fsck --fulldan kemudian saya mengerti ketika saya mencobanya.

christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (447/447), done.
broken link from  commit 235ae1f48701d577d71ebd430344a159e5ba4881
              to  commit 984c11abfc9c2839b386f29c574d9e03383fa589
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    blob dd4e97e22e159a585b20e21028f964827d5afa4e
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    tree 29a422c19251aeaeb907175e9b3219a9bed6c616
broken link from    tree 632a9cf0ef9fccea08438b574e2f1c954f4ff08b
              to    tree 8084e8e04d510cc28321f30a9646477cc50c235c
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob e9052d35bfb6d30065b206fc43f4200a04d5281b
broken link from    tree 774b5b4157b4caae1c6cad96c8eaf5d4eba2c628
              to    blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
broken link from    tree 4aa336dc1a5838e8918e03b85580069d83f4ad09
              to    tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
broken link from    tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
              to    blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
broken link from    tree e5674a91a53e15575a1f3bf5786bc5cc719fb483
              to    blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
broken link from    tree 252ab84542264e1589576b6ee51e7a31e580a0e2
              to    tree 2069041cd5950e529e2991d37b7290ec021d90d4
broken link from    tree 2d4964aa4d4f5d8c7228518ce72ef6a63f820c6d
              to    blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
broken link from    tree c7192e82fc581bd6448bda1a25e8729bdac5f4ff
              to    blob 30d54d47ae82add1917ca173d42e58b396df580b
broken link from    tree 7c66306901fc71389623286936cef172d4ffe408
              to    blob bc7e05d705401273b1df4e939de0f540597c0931
broken link from    tree 0940f5fd227d4c84d6e6749d872db50a4522ae3a
              to    tree 923767594ac22023e824948d65622fe5b407d1a1
broken link from    tree 8eadcd2a971e8357d24f0d80f993d2963452209f
              to    blob 2598bde3dc8cb80ee49510b8159344004b88645f
broken link from    tree ffa302dd0d969172ef23caeefe856ab2f57a4e4d
              to    blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
broken link from    tree 7045b8870a49ce30a2027537a96d73d162bda773
              to    blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
broken link from    tree 37e4705d34bd440ce681ae32ae9a180a13256d72
              to    tree 246f564d4cee53339b8a4244f3173b61caa518eb
missing blob d6925fa431be1ac585bf9a481e98f75107a6e6fb
missing blob ac033bf9dc846101320c96a5ce8aceb8c96ec098
missing tree 29a422c19251aeaeb907175e9b3219a9bed6c616
missing tree 8084e8e04d510cc28321f30a9646477cc50c235c
missing blob 30d54d47ae82add1917ca173d42e58b396df580b
missing tree 8cc55ec952dc192a233e062201d1e7e873ac3db0
missing blob e9052d35bfb6d30065b206fc43f4200a04d5281b
dangling tree 4b26e95db542c72ac4a22ec25abe38fb2de79752
missing blob d83690e1b9a6bdd8a08754b38231799acefcb2ab
missing blob 25a742dff0a403b2b3884f2ffddf63eb45721fac
missing tree 923767594ac22023e824948d65622fe5b407d1a1
missing blob 25688652dea26f61f576ca1b52b9d1a18fbfd01d
missing blob 2598bde3dc8cb80ee49510b8159344004b88645f
dangling tree 3a683869f1bb0c1634de75700c316b3b36570dbd
dangling blob 4098d30843380d798a811f1aa9a02994f0dbbb27
missing tree 2069041cd5950e529e2991d37b7290ec021d90d4
missing blob 4a994e1e7bb7ce28dcec98bad48b9a891d7dec51
missing blob 1a3a5e4dd2502ac121c22f743c4250e254a94eeb
missing blob a0daa0c1567b55d8de2b4d7a3bc010f58c047eab
dangling tree 6c7b5162aa7a303fa3fe8dc393c5da564e309521
missing commit 984c11abfc9c2839b386f29c574d9e03383fa589
missing blob bc7e05d705401273b1df4e939de0f540597c0931
missing blob dd4e97e22e159a585b20e21028f964827d5afa4e
missing tree 246f564d4cee53339b8a4244f3173b61caa518eb
dangling commit a01f5c1e5315dc837203d6dee00d3493be9c5db9

Itu terlihat sangat buruk. Ketika saya melakukan, git log | headsaya mengerti

christopher@christopher-laptop:~/Ubuntu One Side Work/projects.git$ git log | head
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881
commit 2fb0d2d0643b445440f01b164f11ee9ee71fca48
Author: christopher <[email protected]>
Date:   Wed Aug 7 15:51:42 2013 -0400

    finishing chapter 7

Pertanyaan lain di sini telah dikatakan untuk dilihat ./git/refs/heads/master. Ini adalah repo telanjang dan refs/heads/ada tetapi refs/heads/mastertidak. KEPALA di repo telanjang mengatakan ref: refs/heads/mastermeskipun

packed-refs memang mengatakan ini

# pack-refs with: peeled 
2fb0d2d0643b445440f01b164f11ee9ee71fca48 refs/heads/master

Masih ada pertanyaan lain yang menyarankan agar berjalan git reflogdan tidak ada output yang muncul saat saya menjalankannya.

Jadi saya benar-benar tidak tahu harus berbuat apa di sini. Strategi apa yang harus diambil? Apakah mungkin untuk mengatur ulang ke commit terakhir ini pada 7 Agustus

EDIT:

Melakukan git log dan pergi ke bagian bawah output layar menunjukkan ini:

commit 996e03b949aea176238e3c7a8452700bbb987ac9
Author: christopher <christopher@christopher>
Date:   Wed Jul 3 23:00:44 2013 -0400

    many many changes
error: Could not read 984c11abfc9c2839b386f29c574d9e03383fa589
fatal: Failed to traverse parents of commit 235ae1f48701d577d71ebd430344a159e5ba4881

Itu sepertinya mencegah git prune bekerja

TinyGrasshopper
sumber
3
Ini tidak banyak membantu tetapi: jangan simpan repo Git di Dropbox atau layanan sinkronisasi lainnya. Git tidak dibuat untuk menangani program lain yang mengunci dan menulis ulang file secara acak saat melakukan sesuatu yang lain.
millimoose
Terkait: stackoverflow.com/q/18550073/1301972
Todd A.Jacobs
2
Hampir semua jawaban berasumsi bahwa seseorang dapat dengan mudah mengkloning ulang dari sumber jarak jauh yang tidak dapat rusak. Inilah masalahnya ... Bagaimana jika Anda yang asli, dan Anda rusak? Baik. Jadi, di sini: git-repairadalah program yang akan dijalankangit fsck dan berusaha keras untuk memperbaiki masalah yang ditemuinya. git-repair.branchable.com Tampaknya cukup mampu, dan meskipun Anda mungkin harus menyalin (jika Anda bisa!) objek dari cadangan (Anda memiliki cadangan, bukan?), itu akan menghemat banyak waktu dengan menyelamatkan apa pun yang dia bisa dan meninggalkan Anda pekerjaan yang sebenarnya, bukan banyak tugas yang dapat dijalankan secara otomatis. Tidak ada afiliasi, dll.
underscore_d

Jawaban:

111

Sebagai alternatif dari opsi terakhir CodeGnome, jika hanya repo lokal yang rusak, dan Anda mengetahui url ke remote, Anda dapat menggunakan ini untuk mengatur ulang .gitagar sesuai dengan remote (mengganti ${url}dengan url jarak jauh):

mv -v .git .git_old &&            # remove old git
git init &&                       # initialise new repo
git remote add origin "${url}" && # link to old repo
git fetch &&                      # get old history
git reset origin/master --mixed   # force update to old history

Ini membuat pohon kerja Anda tetap utuh, dan hanya memengaruhi pembukuan git.
Saya juga baru-baru ini membuat skrip bash untuk tujuan ini (Lampiran A), yang membungkus sedikit keamanan di sekitar operasi ini.

catatan:

Jika repo Anda memiliki submodul, proses ini akan mengacaukannya, dan satu-satunya solusi yang saya temukan sejauh ini adalah menghapusnya dan kemudian menggunakan git submodule update --init(atau mengkloning ulang repo, tetapi tampaknya terlalu drastis).

Lampiran A - Skrip lengkap

#!/bin/bash

# Author: Zoey Llewellyn "Zobean" Hewll
#
# Usage: fix-git [REMOTE-URL]
#   Must be run from the root directory of the repository.
#   If a remote is not supplied, it will be read from .git/config
# 
# For when you have a corrupted local repo, but a trusted remote.
# This script replaces all your history with that of the remote.
# If there is a .git, it is backed up as .git_old, removing the last backup.
# This does not affect your working tree.
#
# This does not currently work with submodules!
# This will abort if a suspected submodule is found.
# You will have to delete them first
# and re-clone them after (with `git submodule update --init`)
#
# Error codes:
# 1: If a url is not supplied, and one cannot be read from .git/config
# 4: If the url cannot be reached
# 5: If a git submodule is detected


if [[ "$(find -name .git -not -path ./.git | wc -l)" -gt 0 ]] ;
then
    echo "It looks like this repo uses submodules" >&2
    echo "You will need to remove them before this script can safely execute" >&2
    echo "Then use \`git submodule update --init\` to re-clone them" >&2
    exit 5
fi

if [[ $# -ge 1 ]] ;
then
    url="$1"
else
    if ! url="$(git config --local --get remote.origin.url)" ;
    then
        echo "Unable to find remote 'origin': missing in '.git/config'" >&2
        exit 1
    fi
fi
url_base="$(echo "${url}" | sed -E 's;^([^/]*://)?([^/]*)(/.*)?$;\2;')"
echo "Attempting to access ${url_base} before continuing"
if ! wget -p "${url_base}" -O /dev/null -q --dns-timeout=5 --connect-timeout=5 ;
then
    echo "Unable to reach ${url_base}: Aborting before any damage is done" >&2
    exit 4
fi

echo
echo "This operation will replace the local repo with the remote at:"
echo "${url}"
echo
echo "This will completely rewrite history,"
echo "but will leave your working tree intact"
echo -n "Are you sure? (y/N): "

read confirm
if ! [ -t 0 ] ; # i'm open in a pipe
then
    # print the piped input
    echo "${confirm}"
fi
if echo "${confirm}"|grep -Eq "[Yy]+[EeSs]*" ; # it looks like a yes
then
    if [[ -e .git ]] ;
    then
        # remove old backup
        rm -vrf .git_old | tail -n 1 &&
        # backup .git iff it exists
        mv -v .git .git_old
    fi &&
    git init &&
    git remote add origin "${url}" &&
    git config --local --get remote.origin.url | sed 's/^/Added remote origin at /' &&
    git fetch &&
    git reset origin/master --mixed
else
    echo "Aborting without doing anything"
fi
Zoey Hewll
sumber
3
Spektakuler, saya membuat cadangan proyek saya dan mencoba solusi Anda. Git rusak cukup parah. Saya mencoba solusi Anda dan berhasil dengan sempurna, terima kasih banyak.
kequc
2
tidak menggunakan skrip karena saya menggunakan Windows, tetapi perintah itu baru saja menyimpan folder .git saya yang menampilkan repo baru untuk setiap penyimpanan yang dilakukan pada file yang dilacak (jadi saya berakhir dengan 50 repositori master sebelum saya mencoba perbaikan ini )
moped
1
Saya dapat memulihkan .gitfolder saya setelah melakukan ini. Saya menemukan git reset origin/master --harditu lebih berguna daripada --mixed.
Felipe Alvarez
1
Bagus. Jika ada submudul yang tidak berisi perubahan, hapus dulu lalu lakukan submodul init untuk mendapatkannya lagi.
Herm
1
@ w33haa Sayangnya, solusi ini hanya berlaku untuk kasus di mana Anda memiliki repositori jarak jauh yang valid dan dapat diakses. Beberapa dari jawaban lain membahas kasus remote yang tidak dapat diakses atau rusak.
Zoey Hewll
54

TL; DR

Git tidak benar-benar menyimpan sejarah seperti yang Anda pikirkan. Ini menghitung sejarah pada waktu proses berdasarkan rantai leluhur. Jika leluhur Anda kehilangan gumpalan, pohon, atau commit, Anda mungkin tidak dapat memulihkan riwayat Anda sepenuhnya.

Kembalikan Objek Hilang dari Cadangan

Hal pertama yang dapat Anda coba adalah mengembalikan item yang hilang dari cadangan. Misalnya, lihat apakah Anda memiliki cadangan komit yang disimpan sebagai .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589. Jika demikian Anda dapat memulihkannya.

Anda mungkin juga ingin melihat git-verifikasi-pack dan git-unpack- seandainya komit telah dikemas dan Anda ingin mengembalikannya ke objek lepas untuk keperluan operasi repositori.

Reseksi Bedah

Jika Anda tidak dapat mengganti item yang hilang dari cadangan, Anda mungkin dapat menghilangkan riwayat yang hilang. Misalnya, Anda mungkin memeriksa riwayat atau reflog untuk menemukan leluhur dari commit 984c11abfc9c2839b386f29c574d9e03383fa589. Jika Anda menemukannya utuh, maka:

  1. Salin direktori kerja Git Anda ke direktori sementara di suatu tempat.
  2. Lakukan hard reset untuk komit yang tidak rusak.
  3. Salin file Anda saat ini kembali ke pohon kerja Git, tetapi pastikan Anda tidak menyalin kembali folder .git!
  4. Komit pohon kerja saat ini, dan lakukan yang terbaik untuk memperlakukannya sebagai komitmen terjepit dari semua riwayat yang hilang.

Jika berhasil, Anda tentu saja akan kehilangan riwayat yang mengintervensi. Pada titik ini, jika Anda memiliki log riwayat kerja, sebaiknya pangkas riwayat dan reflog Anda dari semua komit dan objek yang tidak dapat dijangkau.

Pemulihan Penuh dan Inisialisasi Ulang

Jika repositori Anda masih rusak, semoga Anda memiliki cadangan atau klon yang tidak rusak yang dapat Anda pulihkan. Jika tidak, tetapi direktori kerja Anda saat ini berisi file yang valid, maka Anda selalu dapat menginisialisasi ulang Git. Sebagai contoh:

rm -rf .git
git init
git add .
git commit -m 'Re-initialize repository without old history.'

Ini drastis, tetapi ini mungkin satu-satunya pilihan Anda jika riwayat repositori Anda benar-benar tidak dapat dipulihkan. YMMV.

Todd A. Jacobs
sumber
3
Saya baru saja mengalami masalah ini dan ingin menunjukkan hal ini karena ini bukan jawaban Anda, tetapi masalah saya hanyalah masalah izin sederhana. error: Could not read abcdeRepo saya dikelola oleh GitLab dan itu membuat file yang tidak dapat dibaca oleh pengguna saya. Beberapa saat sudo chownkemudian dan saya siap berangkat.
Aust
Full Restores and Re-Initializationmenyelamatkan hidup saya :) Saya juga menjalankan a git pull origin master --allow-unrelated-historiesuntuk menarik versi terakhir kode saya dari repo jarak jauh
Sami
6

Jika Anda memiliki konfigurasi jarak jauh dan Anda memiliki / tidak peduli kehilangan beberapa kode yang tidak dikunci, Anda dapat melakukan:

git fetch && git reset --hard
pengguna5169648
sumber
2
Dalam beberapa kasus, git tidak mengizinkan Anda mengambil jika objek tertentu rusak, jadi Anda perlu menginisialisasi ulang repo Anda terlebih dahulu.
Zoey Hewll
Ini tidak berhasil untuk saya ketika saya melakukannya fatal: pack has 13 unresolved deltas.
Artem Russakovskii
5

Berikut adalah skrip (bash) untuk mengotomatiskan solusi pertama oleh @CodeGnome untuk memulihkan dari cadangan (dijalankan dari tingkat atas repo yang rusak). Pencadangan tidak perlu lengkap, hanya perlu memiliki objek yang hilang.

git fsck 2>&1 | grep -e missing -e invalid | awk '{print $NF}' | sort -u |
    while read entry; do
        mkdir -p .git/objects/${entry:0:2}
        cp ${BACKUP}/objects/${entry:0:2}/${entry:2} .git/objects/${entry:0:2}/${entry:2}
    done
campkeith
sumber
4

Sebelum mencoba salah satu perbaikan yang dijelaskan di halaman ini, saya akan menyarankan untuk membuat salinan repo Anda dan mengerjakan salinan ini saja. Kemudian pada akhirnya jika Anda dapat memperbaikinya, bandingkan dengan aslinya untuk memastikan Anda tidak kehilangan file apa pun dalam proses perbaikan.

Alternatif lain yang berhasil bagi saya adalah mengatur ulang git head dan indeks ke keadaan sebelumnya menggunakan:

git reset --keep

Anda juga dapat melakukan hal yang sama secara manual dengan membuka GUI Git dan memilih setiap "Perubahan bertahap" dan klik "Hapus perubahan". Ketika semuanya tidak diatur, Anda sekarang dapat mengompresi database Anda, memeriksa database Anda dan melakukan.

Saya juga mencoba perintah berikut tetapi tidak berhasil untuk saya, tetapi mungkin untuk Anda tergantung pada masalah yang Anda alami:

git reset --mixed
git fsck --full
git gc --auto
git prune --expire now
git reflog --all

Terakhir, untuk menghindari masalah sinkronisasi yang merusak indeks git Anda (yang dapat terjadi dengan DropBox, SpiderOak, atau disk cloud lainnya), Anda dapat melakukan hal berikut:

  1. Ubah .gitfolder Anda menjadi satu file git "bundel" dengan menggunakan:git bundle create my_repo.git --all maka itu akan bekerja sama seperti sebelumnya, tetapi karena semuanya ada dalam satu file, Anda tidak akan mengambil risiko sinkronisasi merusak repo git Anda lagi.
  2. Nonaktifkan sinkronisasi instan : SpiderOak memungkinkan Anda menyetel penjadwalan untuk memeriksa perubahan ke "otomatis" (yang berarti secepat mungkin, memantau perubahan file berkat notifikasi OS). Ini buruk karena akan mulai mengupload perubahan segera setelah Anda melakukan perubahan, lalu mendownload perubahan tersebut, jadi ini mungkin menghapus perubahan terbaru yang baru saja Anda lakukan. Solusi untuk memperbaiki masalah ini adalah dengan mengatur penundaan pemantauan perubahan menjadi 5 menit atau lebih. Ini juga memperbaiki masalah dengan aplikasi pencatatan penyimpanan instan (seperti Notepad ++).
rajin
sumber
3

Jika Anda putus asa Anda dapat mencoba ini:

git clone ssh://[email protected]/path/to/project destination --depth=1

Ini akan mendapatkan data Anda, tetapi Anda akan kehilangan riwayatnya. Saya pergi dengan trial and error pada repo saya dan --depth=10bekerja, tetapi --depth=50memberi saya kegagalan.

alberto56
sumber
3

Saya mencoba memindahkan file objek dengan 0 byte dan mengambilnya lagi dari jarak jauh, dan itu berhasil:

find . -type f -size 0 -exec mv {} /tmp \;
git fetch

Ini mengambil objek yang hilang dari remote dan memungkinkan saya untuk terus bekerja tanpa menginisialisasi ulang seluruh repo.

AlejandroVD
sumber
2

Saya menghadapi masalah yang sama, jadi saya mengganti folder ".git" dengan versi cadangan dan masih tidak berfungsi karena file .gitconfig rusak. BSOD di laptop saya merusaknya. Saya menggantinya dengan kode berikut dan source mengembalikan semua repositori saya.

[user]
name = *your username*
email = *your email address*
[core]
autocrlf = true
excludesfile = C:\\Users\\*user name*\\Documents\\gitignore_global.txt

Saya tidak tahu apakah ini akan membantu siapa pun, tetapi ini hanyalah solusi lain yang berhasil untuk saya.

AspiringCanadian
sumber
2

Hapus indeks dan lakukan reset

rm -f .git/index
git reset
Ashfaq
sumber
2

Saya mengalami masalah serupa menggunakan git versi 2.7.1 di Ubuntu 18.04.3 belakangan ini. Inilah yang saya lakukan:

sudo apt install git-repair
git-repair  # fix a broken git repository
or
git-repair --force  # force repair, even if data is lost
git fsck  # to verify it was fixed

Sebagian besar waktu proses pemulihan berhasil

Jonathan L.
sumber
git-repair memang merupakan alat yang sangat berguna. Ini membantu saya memulihkan repositori. Masalah dengan sebagian besar metode lain adalah Anda kehilangan simpanan, yang bukan merupakan pilihan bagi saya.
Jan Rychter
1

Dalam kasus saya, saya sedang membuat repositori dari kode sumber yang sudah ada di pc saya dan kesalahan itu muncul. Saya menghapus folder .git dan melakukan semuanya lagi dan berhasil :)

Carolina
sumber
1

Saya ingin menambahkan ini sebagai komentar di bawah jawaban luar biasa Zoey Hewil di atas, tetapi saat ini saya tidak memiliki cukup perwakilan untuk melakukannya, jadi saya harus menambahkannya di sini dan memberikan penghargaan untuk karyanya: P

Jika Anda menggunakan Poshgit dan merasa sangat malas, Anda dapat menggunakan yang berikut ini untuk mengekstrak URL Anda secara otomatis dari konfigurasi git dan membuat pekerjaan mudah menjadi lebih mudah. Peringatan standar berlaku tentang pengujian ini pada salinan / pencadangan repo lokal Anda terlebih dahulu jika itu meledak di wajah Anda.

$config = get-content .git\config
$url = $config -match " url = (?<content>.*)"
$url = $url.trim().Substring(6)
$url

move-item -v .git .git_old;
git init;
git remote add origin "$url";
git fetch;
git reset origin/master --mixed
Gesthemene
sumber
0

Cara cepat jika Anda memiliki perubahan pada proyek Anda saat ini dan tidak ingin kehilangannya, pindahkan proyek Anda saat ini ke suatu tempat, klon proyek dari github ke folder ini dan buat beberapa perubahan dan coba lakukan lagi. Atau hapus saja repo dan kloning lagi, itu berfungsi dari saya.

certilremy
sumber
-2

Perintah ini berhasil untuk saya:

$ git reset --mixed 
NoRegrets
sumber