Come riparare un repository git danneggiato?


104

Ho provato a clonare il mio repository che tengo nella mia cartella Ubuntu one su una nuova macchina e ho ottenuto questo:

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$ 

Quindi ho provato a guardare le molte altre domande come questa che sono state poste qui e la maggior parte di loro dice di correre git fsck --fulle poi ottengo questo quando lo provo.

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

Sembra davvero brutto. Quando lo faccio git log | headottengo questo

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 <christopher@christopher.christopher>
Date:   Wed Aug 7 15:51:42 2013 -0400

    finishing chapter 7

Altre domande qui hanno detto di guardare ./git/refs/heads/master. È un semplice repo ed refs/heads/esiste ma refs/heads/masternon lo è. ref: refs/heads/masterTuttavia, dice HEAD nel repo nudo

packed-refs lo dice però

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

Ancora altre domande hanno suggerito l'esecuzione git refloge nessun output viene visualizzato quando lo eseguo.

Quindi non ho davvero idea di cosa fare qui. Quale strategia dovrebbe essere presa? È possibile ripristinare la testa a quest'ultimo commit il 7 agosto

MODIFICARE:

Fare un log git e andare in fondo all'output dello schermo mostra questo:

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

Questo sembra impedire il funzionamento di Git Prune


3
Questo non è di grande aiuto ma: non memorizzare i repository Git in Dropbox o altri servizi di sincronizzazione. Git non è costruito per gestire un altro programma che blocca e riscrive i file in modo casuale mentre sta facendo qualcos'altro.
millimoose


2
Quasi tutte le risposte presumono che si possa semplicemente ri-clonare da un'origine remota incorruttibile. Ecco il problema ... cosa succede se si è l'origine, e il gioco è corrotto? Destra. Quindi, ecco: git-repairè un programma che verrà eseguito git fscke proverà piuttosto duramente a risolvere eventuali problemi che incontra. git-repair.branchable.com Sembra abbastanza capace, e anche se potresti finire per dover copiare (se puoi!) oggetti da un backup (hai un backup, giusto?), dovrebbe farti risparmiare molto tempo salvando tutto ciò che può e lasciandoti il ​​vero lavoro, non molte attività automatizzabili. Nessuna affiliazione, ecc.
underscore_d

Risposte:


111

In alternativa all'ultima opzione di CodeGnome, se solo il repository locale è danneggiato e conosci l'URL del telecomando, puoi usarlo per reimpostare il tuo .gitin modo che corrisponda al telecomando (sostituendolo ${url}con l'URL remoto):

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

Questo lascia intatto il tuo albero di lavoro e influisce solo sulla contabilità di git.
Recentemente ho anche creato uno script bash proprio per questo scopo (Appendice A), che avvolge un po 'di sicurezza attorno a questa operazione.

Nota:

Se il tuo repository ha sottomoduli, questo processo li incasinerà in qualche modo, e l'unica soluzione che ho trovato finora è eliminarli e quindi utilizzare git submodule update --init(o ri-clonare il repository, ma sembra troppo drastico).

Appendice A - Script completo

#!/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

3
Spettacolare, ho fatto un backup del mio progetto e ho provato la tua soluzione. Git è stato corrotto abbastanza male in qualche modo. Ho provato la tua soluzione e ha funzionato perfettamente, grazie mille.
kequc

2
non ho usato lo script dato che sono su Windows, ma quei comandi hanno appena salvato la mia cartella .git che mostrava un nuovo repository per ogni salvataggio effettuato su qualsiasi file tracciato (quindi mi sono ritrovato con 50 repository master prima di provare questa correzione )
ciclomotore

1
Sono stato in grado di ripristinare la mia .gitcartella dopo averlo fatto. Ho trovato git reset origin/master --hardpiù utile di --mixed.
Felipe Alvarez

1
Grande. Nel caso in cui ci siano sottomudule che non contengono modifiche, rimuoverli prima e poi eseguire il sottomodulo init per recuperarli.
herm

1
@ w33haa Sfortunatamente, questa soluzione è applicabile solo nel caso in cui si disponga di un repository remoto valido e accessibile. Alcune delle altre risposte affrontano il caso di un telecomando inaccessibile o danneggiato.
Zoey Hewll

54

TL; DR

Git non memorizza la cronologia nel modo in cui pensi che lo faccia. Si calcola la storia a tempo di esecuzione in base a una catena di antenato. Se ai tuoi antenati mancano blob, alberi o commit, potresti non essere in grado di recuperare completamente la tua cronologia.

Ripristina oggetti mancanti dai backup

La prima cosa che puoi provare è ripristinare gli elementi mancanti dal backup. Ad esempio, controlla se hai un backup del commit archiviato come .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589. Se è così puoi ripristinarlo.

Potresti anche voler esaminare git-verify-pack e git-unpack-objects nel caso in cui il commit sia già stato impacchettato e vuoi restituirlo a un oggetto sciolto ai fini della chirurgia del repository.

Resezione chirurgica

Se non puoi sostituire gli elementi mancanti da un backup, potresti essere in grado di eliminare la cronologia mancante. Ad esempio, potresti esaminare la tua cronologia o reflog per trovare un antenato del commit 984c11abfc9c2839b386f29c574d9e03383fa589. Se ne trovi uno intatto, allora:

  1. Copia la tua directory di lavoro Git in una directory temporanea da qualche parte.
  2. Eseguire un hard reset al commit non danneggiato.
  3. Copia i tuoi file correnti nuovamente nell'albero di lavoro di Git, ma assicurati di non copiare nuovamente la cartella .git!
  4. Esegui il commit dell'albero di lavoro corrente e fai del tuo meglio per trattarlo come un commit schiacciato di tutta la cronologia mancante.

Se funziona, ovviamente perderai la cronologia intervenuta. A questo punto, se si dispone di un registro della cronologia funzionante, è una buona idea eliminare la cronologia e i reflog di tutti i commit e gli oggetti non raggiungibili.

Ripristini completi e reinizializzazione

Se il tuo repository è ancora danneggiato, si spera che tu abbia un backup o un clone non danneggiato da cui puoi eseguire il ripristino. In caso contrario, ma la tua directory di lavoro corrente contiene file validi, puoi sempre reinizializzare Git. Per esempio:

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

È drastico, ma potrebbe essere la tua unica opzione se la cronologia del tuo repository è davvero irrecuperabile. YMMV.


3
Mi sono appena imbattuto in questo problema e volevo farlo notare poiché non è nella tua risposta, ma il mio problema era solo un semplice problema di autorizzazioni. error: Could not read abcdeIl mio repository è gestito da GitLab e stava creando file che il mio utente non poteva leggere. Un sudo chowndopo ed ero a posto.
Agosto

Full Restores and Re-Initializationmi ha salvato la vita :) Ho anche eseguito un git pull origin master --allow-unrelated-historiesper estrarre l'ultima versione del mio codice dal repository remoto
Sami

6

Se hai un telecomando configurato e non ti interessa perdere del codice non premuto, puoi fare:

git fetch && git reset --hard

2
In alcuni casi, git non ti consente di recuperare se alcuni oggetti sono danneggiati, quindi devi prima reinizializzare il tuo repository.
Zoey Hewll

Questo non ha funzionato per me quando l'ho fatto fatal: pack has 13 unresolved deltas.
Artem Russakovskii

5

Ecco uno script (bash) per automatizzare la prima soluzione di @CodeGnome per il ripristino da un backup (eseguito dal livello superiore del repository danneggiato). Il backup non deve essere completo, deve solo contenere gli oggetti mancanti.

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

4

Prima di provare una qualsiasi delle correzioni descritte in questa pagina, consiglierei di fare una copia del tuo repository e lavorare solo su questa copia. Quindi alla fine, se riesci a risolverlo, confrontalo con l'originale per assicurarti di non aver perso alcun file durante il processo di riparazione.

Un'altra alternativa che ha funzionato per me è stata ripristinare la testina git e l'indice al suo stato precedente utilizzando:

git reset --keep

Puoi anche fare lo stesso manualmente aprendo la GUI di Git e selezionando ogni "Modifiche a fasi" e fai clic su "Rimuovi dallo stadio di modifica". Quando tutto è unstaged, ora dovresti essere in grado di comprimere il tuo database, controllare il tuo database ed eseguire il commit.

Ho anche provato i seguenti comandi ma non hanno funzionato per me, ma potrebbero per te a seconda del problema esatto che hai:

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

Infine, per evitare che questo problema di sincronizzazione danneggi il tuo indice git (cosa che può accadere con DropBox, SpiderOak o qualsiasi altro disco cloud), puoi fare quanto segue:

  1. Converti la tua .gitcartella in un singolo file git "bundle" utilizzando:git bundle create my_repo.git --all allora dovrebbe funzionare esattamente come prima, ma poiché tutto è in un singolo file non rischierai più che la sincronizzazione danneggi il tuo repository git.
  2. Disabilita sincronizzazione istantanea : SpiderOak permette di impostare la schedulazione per il controllo delle modifiche su "automatico" (il che significa che è il prima possibile, monitorando le modifiche ai file grazie alle notifiche del sistema operativo). Questo non va bene perché inizierà a caricare le modifiche non appena apporti una modifica, quindi scaricherà la modifica, quindi potrebbe cancellare le ultime modifiche che stavi facendo. Una soluzione per risolvere questo problema è impostare il ritardo del monitoraggio delle modifiche su 5 minuti o più. Questo risolve anche i problemi con il salvataggio istantaneo delle applicazioni che prendono appunti (come Notepad ++).

3

Se sei disperato puoi provare questo:

git clone ssh://me@my.git.server/path/to/project destination --depth=1

Otterrà i tuoi dati, ma perderai la cronologia. Sono andato con tentativi ed errori sul mio repository e ho --depth=10lavorato, ma --depth=50mi ha dato un fallimento.


3

Ho provato a spostare i file oggetto con 0 byte e a recuperarli di nuovo dal telecomando, e ha funzionato:

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

Ha recuperato gli oggetti mancanti dal telecomando e mi ha permesso di continuare a lavorare senza reinizializzare l'intero repository.


2

Stavo affrontando lo stesso problema, quindi ho sostituito la cartella ".git" con una versione di backup e ancora non funzionava perché il file .gitconfig era danneggiato. Il BSOD sul mio laptop lo ha danneggiato. L'ho sostituito con il seguente codice e sourcetree ha ripristinato tutti i miei repository.

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

Non so se questo aiuterà qualcuno, ma questa è solo un'altra soluzione che ha funzionato per me.


2

Rimuovere l'indice e ripristinare

rm -f .git/index
git reset

2

Ultimamente ho riscontrato problemi simili utilizzando la versione 2.7.1 di git con Ubuntu 18.04.3. Ecco come ho fatto:

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

La maggior parte delle volte il processo di ripristino ha avuto successo


git-repair è davvero uno strumento molto utile. Mi ha aiutato a recuperare un repository. Il problema con la maggior parte degli altri metodi è che perdi le tue scorte, il che non era un'opzione per me.
Jan Rychter,

1

Nel mio caso, stavo creando il repository dal codice sorgente già nel mio pc e quell'errore è apparso. Ho cancellato la cartella .git e ho fatto tutto di nuovo e ha funzionato :)


1

Volevo aggiungere questo come commento sotto la fantastica risposta di Zoey Hewil sopra, ma al momento non ho abbastanza rappresentante per farlo, quindi devo aggiungerlo qui e dare credito per il suo lavoro: P

Se stai usando Poshgit e ti senti eccezionalmente pigro, puoi usare quanto segue per estrarre automaticamente il tuo URL dalla tua configurazione git e rendere un lavoro ancora più facile. Si applicano avvertenze standard sul testare questo su una copia / eseguire il backup del repository locale prima nel caso in cui ti esploda in faccia.

$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

0

Modo rapido se hai modifiche al tuo progetto corrente e non vuoi perderlo, sposta il tuo progetto corrente da qualche parte, clona il progetto da GitHub a questa cartella e apporta alcune modifiche e prova a eseguire nuovamente il commit. O semplicemente elimina il repository e clonalo di nuovo, ha funzionato da me.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.