Jak naprawić uszkodzone repozytorium Git?

104

Próbowałem sklonować moje repozytorium, które trzymam w jednym folderze Ubuntu na nowy komputer i otrzymałem to:

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$ 

Więc spróbowałem spojrzeć na wiele innych pytań, takich jak to, które zostały tutaj zadane i większość z nich mówi, git fsck --fullżebym uciekała, a potem otrzymuję to, kiedy próbuję tego.

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

To wygląda naprawdę źle. Kiedy to zrobię git log | head, dostaję to

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

Inne pytania tutaj mówiły, żeby się przyjrzeć ./git/refs/heads/master. Jest to nagie repozytorium i refs/heads/istnieje, ale refs/heads/mastertak nie jest. Głowa w gołym repozytorium mówi ref: refs/heads/masterjednak

packed-refs jednak tak mówi

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

Jeszcze inne pytania sugerowały uruchomienie git reflogi żadne wyjście nie pojawia się po uruchomieniu tego.

Więc naprawdę nie mam pojęcia, co tutaj robić. Jaką strategię przyjąć? Czy można zresetować głowę do ostatniego zatwierdzenia 7 sierpnia?

EDYTOWAĆ:

Wykonanie dziennika git i przejście do dołu ekranu pokazuje to:

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

Wydaje się, że to uniemożliwia działanie git prune

TinyGrasshopper
źródło
3
Nie jest to zbyt pomocne, ale: nie przechowuj repozytoriów Git w Dropbox ani innych usługach synchronizacji. Git nie jest zbudowany do obsługi innego programu, który losowo blokuje i przepisuje pliki, podczas gdy robi coś innego.
millimoose,
Związane z: stackoverflow.com/q/18550073/1301972
Todd A. Jacobs
2
Prawie wszystkie odpowiedzi zakładają, że można po prostu ponownie sklonować z jakiegoś niezniszczalnego odległego źródła. Oto problem ... Co jeśli to pochodzenie, a ty uszkodzony? Dobrze. A więc tutaj: git-repairto program, który będzie działał git fscki będzie się bardzo starał naprawić wszelkie napotkane problemy. git-repair.branchable.com Wydaje się to całkiem możliwe i chociaż może się zdarzyć, że będziesz musiał skopiować (jeśli możesz!) obiekty z kopii zapasowej (masz kopię zapasową, prawda?), Powinno to zaoszczędzić dużo czasu, ocalić wszystko, co się da i zostawić ci prawdziwą pracę, a nie wiele zautomatyzowanych zadań. Brak przynależności itp.
podkreślenie_d

Odpowiedzi:

111

Jako alternatywa dla ostatniej opcji CodeGnome, jeśli tylko lokalne repozytorium jest uszkodzone i znasz adres URL do pilota, możesz użyć tego, aby ponownie ustawić swój tak, .gitaby pasował do zdalnego (zastępując ${url}zdalnym adresem URL):

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

To pozostawia twoje drzewo robocze nietknięte i wpływa tylko na księgowość git.
Niedawno stworzyłem również skrypt basha właśnie w tym celu (Dodatek A), który zapewnia trochę bezpieczeństwa wokół tej operacji.

Uwaga:

Jeśli twoje repozytorium ma podmoduły, ten proces w jakiś sposób je zepsuje, a jedynym rozwiązaniem, które do tej pory znalazłem, jest ich usunięcie, a następnie użycie git submodule update --init(lub ponowne sklonowanie repozytorium, ale wydaje się to zbyt drastyczne).

Dodatek A - pełny skrypt

#!/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
źródło
3
Spektakularne, wykonałem kopię zapasową mojego projektu i wypróbowałem Twoje rozwiązanie. Git został w jakiś sposób dość mocno uszkodzony. Wypróbowałem twoje rozwiązanie i zadziałało idealnie, dziękuję bardzo.
kequc
2
nie użyłem skryptu, ponieważ jestem w systemie Windows, ale te polecenia po prostu zapisały mój folder .git, który pokazywał nowe repozytorium dla każdego zapisu wykonanego w jakimkolwiek śledzonym pliku (więc skończyło się na 50 głównych repozytoriach, zanim spróbowałem tej poprawki )
motorower
1
.gitPo wykonaniu tej czynności udało mi się przywrócić mój folder. Okazało się, że git reset origin/master --hardjest bardziej przydatne niż --mixed.
Felipe Alvarez
1
Wspaniały. W przypadku, gdy istnieją podudule, które nie zawierają zmian, najpierw je usuń, a następnie wykonaj init submodułu, aby uzyskać je ponownie.
herm
1
@ w33haa Niestety, to rozwiązanie ma zastosowanie tylko w przypadku, gdy masz ważne i dostępne zdalne repozytorium. Niektóre inne odpowiedzi dotyczą przypadku niedostępnego lub uszkodzonego pilota.
Zoey Hewll
54

TL; DR

Git tak naprawdę nie przechowuje historii tak, jak myślisz. To oblicza historii w czasie wykonywania w oparciu o łańcuch przodków. Jeśli w Twoim przodku brakuje obiektów blob, drzew lub zatwierdzeń, możesz nie być w stanie w pełni odzyskać swojej historii.

Przywróć brakujące obiekty z kopii zapasowych

Pierwszą rzeczą, jaką możesz spróbować, jest przywrócenie brakujących elementów z kopii zapasowej. Na przykład sprawdź, czy masz kopię zapasową zatwierdzenia zapisaną jako .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589. Jeśli tak, możesz go przywrócić.

Możesz również zajrzeć do git-verify-pack i git-unpack-objects w przypadku, gdy zatwierdzenie zostało już spakowane i chcesz zwrócić je do luźnego obiektu na potrzeby operacji repozytorium.

Resekcja chirurgiczna

Jeśli nie możesz zastąpić brakujących elementów z kopii zapasowej, możesz wyciąć brakującą historię. Na przykład, możesz przejrzeć swoją historię lub reflog, aby znaleźć przodka commit 984c11abfc9c2839b386f29c574d9e03383fa589. Jeśli znajdziesz jeden nienaruszony, to:

  1. Skopiuj katalog roboczy Git do katalogu tymczasowego.
  2. Wykonaj twardy reset nieuszkodzonego zatwierdzenia.
  3. Skopiuj swoje bieżące pliki z powrotem do drzewa roboczego Git, ale upewnij się, że nie kopiujesz z powrotem folderu .git!
  4. Zatwierdź bieżące drzewo robocze i zrób wszystko, co w twojej mocy, aby traktować je jako zgniecione zatwierdzenie całej brakującej historii.

Jeśli to zadziała, oczywiście stracisz historię, która za nim stoi. W tym momencie, jeśli masz działający dziennik historii, dobrym pomysłem jest usunięcie historii i reflogów wszystkich nieosiągalnych zatwierdzeń i obiektów.

Pełne przywracanie i ponowna inicjalizacja

Jeśli repozytorium nadal jest zepsute, miejmy nadzieję, że masz nieuszkodzoną kopię zapasową lub klon, z którego możesz przywrócić. Jeśli nie, ale twój bieżący katalog roboczy zawiera prawidłowe pliki, zawsze możesz ponownie zainicjować Git. Na przykład:

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

Jest to drastyczne, ale może być jedyną opcją, jeśli historia repozytorium jest naprawdę nie do odzyskania. YMMV.

Todd A. Jacobs
źródło
3
Właśnie natknąłem się na ten problem i chciałem na to zwrócić uwagę, ponieważ nie ma go w twojej odpowiedzi, ale mój problem był tylko prostym problemem z uprawnieniami. error: Could not read abcdeMoje repozytorium jest zarządzane przez GitLab i tworzyło pliki, których mój użytkownik nie mógł odczytać. sudo chownPóźniej i byłem dobry, aby przejść.
Aust
Full Restores and Re-Initializationuratowałem mi życie :) Uruchomiłem też git pull origin master --allow-unrelated-histories, żeby pobrać ostatnią wersję mojego kodu ze zdalnego repozytorium
Sami
6

Jeśli masz skonfigurowanego pilota i masz / nie obchodzi Cię utrata niezakupionego kodu, możesz zrobić:

git fetch && git reset --hard
user5169648
źródło
2
W niektórych przypadkach git nie pozwoli ci pobrać, jeśli pewne obiekty są uszkodzone, więc musisz najpierw ponownie zainicjować repozytorium.
Zoey Hewll
To nie zadziałało, kiedy to zrobiłem fatal: pack has 13 unresolved deltas.
Artem Russakovskii
5

Oto skrypt (bash) do zautomatyzowania pierwszego rozwiązania @CodeGnome w celu przywrócenia z kopii zapasowej (uruchom z najwyższego poziomu uszkodzonego repozytorium). Kopia zapasowa nie musi być kompletna, wystarczy, że zawiera brakujące obiekty.

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
źródło
4

Przed wypróbowaniem którejkolwiek z poprawek opisanych na tej stronie radziłbym wykonać kopię repozytorium i pracować tylko na tej kopii. Na koniec, jeśli możesz to naprawić, porównaj go z oryginałem, aby upewnić się, że nie utraciłeś żadnego pliku w procesie naprawy.

Inną alternatywą, która działała dla mnie, było zresetowanie głowy git i indeksu do poprzedniego stanu za pomocą:

git reset --keep

Możesz również zrobić to samo ręcznie, otwierając GUI Git i wybierając każdą „Etapową zmianę” i klikając „Cofnij zmianę”. Kiedy wszystko jest niestabilne, powinieneś być w stanie skompresować bazę danych, sprawdzić bazę danych i zatwierdzić.

Wypróbowałem również następujące polecenia, ale nie działały one dla mnie, ale mogą one dla ciebie, w zależności od dokładnego problemu, który masz:

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

Wreszcie, aby uniknąć problemu synchronizacji uszkadzającego indeks git (co może się zdarzyć w przypadku DropBox, SpiderOak lub dowolnego innego dysku w chmurze), możesz wykonać następujące czynności:

  1. Przekonwertuj swój .gitfolder na pojedynczy plik git „bundle” przy użyciu git bundle create my_repo.git --all:, to powinno działać tak samo jak poprzednio, ale ponieważ wszystko jest w jednym pliku, nie ryzykujesz już, że synchronizacja uszkodzi twoje repozytorium git.
  2. Wyłącz natychmiastową synchronizację : SpiderOak umożliwia ustawienie harmonogramu sprawdzania zmian na „automatyczny” (czyli tak szybko, jak to możliwe, monitorując zmiany plików dzięki powiadomieniom systemu operacyjnego). To jest złe, ponieważ zacznie przesyłać zmiany, gdy tylko wprowadzisz zmianę, a następnie pobrać zmianę, więc może usunąć ostatnie zmiany, które właśnie robiłeś. Rozwiązaniem tego problemu jest ustawienie opóźnienia monitorowania zmian na 5 minut lub więcej. Rozwiązuje to również problemy z natychmiastowym zapisywaniem aplikacji do robienia notatek (takich jak Notepad ++).
wytworny
źródło
3

Jeśli jesteś zdesperowany, możesz spróbować tego:

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

Otrzymasz dane, ale stracisz historię. Poszedłem metodą prób i błędów na moim repozytorium i --depth=10działałem, ale --depth=50dało mi to porażkę.

alberto56
źródło
3

Próbowałem przenieść pliki obiektowe z bajtami 0 i pobrać je ponownie z pilota i zadziałało:

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

Pobrał brakujące obiekty z pilota i pozwolił mi kontynuować pracę bez ponownej inicjalizacji całego repozytorium.

AlejandroVD
źródło
2

Miałem ten sam problem, więc zastąpiłem folder „.git” wersją z kopii zapasowej i nadal nie działał, ponieważ plik .gitconfig był uszkodzony. BSOD na moim laptopie uszkodził go. Zastąpiłem go następującym kodem i sourcetree przywróciło wszystkie moje repozytoria.

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

Nie wiem, czy to komuś pomoże, ale to tylko kolejne rozwiązanie, które zadziałało.

AspiringCanadian
źródło
2

Usuń indeks i zresetuj

rm -f .git/index
git reset
Ashfaq
źródło
2

Ostatnio doświadczyłem podobnych problemów podczas używania git w wersji 2.7.1 pod Ubuntu 18.04.3. Oto jak to zrobiłem:

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

W większości przypadków proces odzyskiwania był udany

Jonathan L.
źródło
git-repair to rzeczywiście bardzo przydatne narzędzie. Pomogło mi to odzyskać repozytorium. Problem z większością innych metod polega na tym, że tracisz swoje skrytki, co nie było dla mnie opcją.
Jan Rychter
1

W moim przypadku tworzyłem repozytorium z kodu źródłowego już na moim komputerze i pojawił się ten błąd. Usunąłem folder .git i zrobiłem wszystko ponownie i zadziałało :)

Karolina
źródło
1

Chciałem to dodać jako komentarz pod wspaniałą odpowiedzią Zoey Hewil powyżej, ale obecnie nie mam wystarczającej liczby przedstawicieli, aby to zrobić, więc muszę to dodać tutaj i przyznać uznanie za jej pracę: P

Jeśli używasz Poshgit i czujesz się wyjątkowo leniwy, możesz użyć poniższego, aby automatycznie wyodrębnić swój adres URL z konfiguracji git i jeszcze bardziej ułatwić sobie zadanie. Obowiązują standardowe zastrzeżenia dotyczące testowania tego na kopii / tworzenia kopii zapasowej lokalnego repozytorium na wypadek, gdyby wybuchło ci w twarz.

$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
źródło
0

Szybki sposób, jeśli masz zmiany w bieżącym projekcie i nie chcesz ich stracić, przenieś bieżący projekt w inne miejsce, sklonuj projekt z github do tego folderu i wprowadź zmiany i spróbuj ponownie zatwierdzić. Lub po prostu usuń repozytorium i sklonuj je ponownie, zadziałało dla mnie.

certilremy
źródło
-2

To polecenie zadziałało dla mnie:

$ git reset --mixed 
Bez żalu
źródło