¿Cómo reparar el repositorio de git dañado?

104

Intenté clonar mi repositorio que guardo en mi carpeta Ubuntu One en una nueva máquina y obtuve esto:

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$ 

Así que intenté mirar las muchas otras preguntas como esta que se han hecho aquí y la mayoría de ellas dicen que corra git fsck --fully luego obtengo esto cuando intento eso.

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

Eso se ve muy mal. Cuando hago una git log | head, obtengo esto

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

Aquí se han dicho otras preguntas para mirar ./git/refs/heads/master. Es un repositorio simple y refs/heads/existe, pero refs/heads/masterno es así. HEAD en el repositorio desnudo dice ref: refs/heads/mastersin embargo

packed-refs aunque dice esto

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

Aún otras preguntas han sugerido ejecutar git reflogy no aparece ningún resultado cuando ejecuto eso.

Así que realmente no tengo idea de qué hacer aquí. ¿Qué estrategia se debe tomar? ¿Es posible restablecer la cabeza a esta última confirmación el 7 de agosto?

EDITAR:

Hacer un git log e ir a la parte inferior de la salida de la pantalla muestra esto:

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

Eso parece estar impidiendo que git prune funcione

Pequeño saltamontes
fuente
3
Esto no es de mucha ayuda, pero no almacene repositorios de Git en Dropbox ni en ningún otro servicio de sincronización. Git no está diseñado para manejar otro programa que bloquea y reescribe archivos aleatoriamente mientras hace otra cosa.
millimoose
Relacionado: stackoverflow.com/q/18550073/1301972
Todd A. Jacobs
2
Casi todas las respuestas asumen que uno puede simplemente volver a clonar desde algún origen remoto incorruptible. Aquí está el problema ... ¿Qué pasa si usted es el origen y está corrupto? Correcto. Entonces, aquí: git-repaires un programa que se ejecutará git fscky se esforzará bastante para solucionar cualquier problema que encuentre. git-repair.branchable.com Parece bastante capaz, y aunque podría terminar teniendo que copiar (¡si puede!) objetos de una copia de seguridad (tiene una copia de seguridad, ¿verdad?), debería ahorrarle mucho tiempo al rescatar todo lo que pueda y dejarle el trabajo real, no muchas tareas automatizables. Sin afiliación, etc.
underscore_d

Respuestas:

110

Como alternativa a la última opción de CodeGnome, si solo el repositorio local está dañado y conoce la URL del control remoto, puede usar esto para restablecer su configuración .gitpara que coincida con la del control remoto (reemplazándola ${url}por la URL remota):

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

Esto deja intacto su árbol de trabajo y solo afecta la contabilidad de git.
También hice recientemente un script bash para este mismo propósito (Apéndice A), que envuelve un poco de seguridad en esta operación.

Nota:

Si su repositorio tiene submódulos, este proceso los estropeará de alguna manera, y la única solución que he encontrado hasta ahora es eliminarlos y luego usarlos git submodule update --init(o volver a clonar el repositorio, pero eso parece demasiado drástico).

Apéndice A - Guión 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
Zoey Hewll
fuente
3
Espectacular, hice una copia de seguridad de mi proyecto y probé tu solución. Git se corrompió bastante de alguna manera. Probé tu solución y funcionó perfectamente, muchas gracias.
kequc
2
no usé el script ya que estoy en Windows, pero esos comandos simplemente guardaron mi carpeta .git que mostraba un nuevo repositorio por cada guardado realizado en cualquier archivo rastreado (así que terminé con 50 repositorios maestros antes de probar esta solución )
ciclomotor
1
Pude restaurar mi .gitcarpeta después de hacer esto. Encontré que git reset origin/master --hardera más útil que --mixed.
Felipe Alvarez
1
Excelente. En caso de que haya submódulos que no contengan cambios, elimínelos primero y luego ejecute el submódulo init para obtenerlos nuevamente.
herm
1
@ w33haa Desafortunadamente, esta solución solo es aplicable para el caso en el que tenga un repositorio remoto válido y accesible. Algunas de las otras respuestas abordan el caso de un control remoto inaccesible o corrupto.
Zoey Hewll
53

TL; DR

Git realmente no almacena el historial de la forma en que crees que lo hace. Se calcula historia en tiempo de ejecución basado en una cadena de ancestro. Si a su ascendencia le faltan blobs, árboles o confirmaciones, es posible que no pueda recuperar completamente su historial.

Restaurar objetos perdidos de copias de seguridad

Lo primero que puede intentar es restaurar los elementos que faltan de la copia de seguridad. Por ejemplo, vea si tiene una copia de seguridad de la confirmación almacenada como .git/objects/98/4c11abfc9c2839b386f29c574d9e03383fa589. Si es así, puede restaurarlo.

También es posible que desee examinar git-verify-pack y git-unpack-objects en caso de que la confirmación ya se haya empaquetado y desee devolverla a un objeto suelto para fines de cirugía de repositorio.

Resección quirúrgica

Si no puede reemplazar los elementos que faltan de una copia de seguridad, es posible que pueda eliminar el historial faltante. Por ejemplo, puede examinar su historial o volver a registrar para encontrar un antepasado de la confirmación 984c11abfc9c2839b386f29c574d9e03383fa589. Si encuentra uno intacto, entonces:

  1. Copie su directorio de trabajo de Git en un directorio temporal en algún lugar.
  2. Haga un restablecimiento completo a la confirmación no corrupta.
  3. Copie sus archivos actuales nuevamente en el árbol de trabajo de Git, ¡pero asegúrese de no copiar la carpeta .git nuevamente!
  4. Confirme el árbol de trabajo actual y haga todo lo posible para tratarlo como una confirmación aplastada de todo el historial que falta.

Si funciona, por supuesto perderá el historial intermedio. En este punto, si tiene un registro de historial de trabajo, entonces es una buena idea podar su historial y re-logs de todas las confirmaciones y objetos inalcanzables.

Restauraciones completas y reinicialización

Si su repositorio aún está roto, entonces es de esperar que tenga una copia de seguridad o un clon no dañado desde el que pueda restaurar. Si no es así, pero su directorio de trabajo actual contiene archivos válidos, siempre puede reinicializar Git. Por ejemplo:

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

Es drástico, pero puede ser su única opción si el historial de su repositorio es realmente irrecuperable. YMMV.

Todd A. Jacobs
fuente
3
Me encontré con este problema y quería señalarlo ya que no está en su respuesta, pero mi problema era solo un problema de permisos. error: Could not read abcdeMi repositorio es administrado por GitLab y estaba creando archivos que mi usuario no podía leer. Más sudo chowntarde y estaba listo para irme.
Agosto
6

Si tiene un control remoto configurado y no le importa perder algún código sin presionar, puede hacer:

git fetch && git reset --hard
usuario5169648
fuente
2
En algunos casos, git no le permitirá buscar si ciertos objetos están dañados, por lo que primero debe reinicializar su repositorio.
Zoey Hewll
Esto no funcionó para mí cuando lo hice fatal: pack has 13 unresolved deltas.
Artem Russakovskii
5

Aquí hay un script (bash) para automatizar la primera solución de @CodeGnome para restaurar desde una copia de seguridad (ejecutar desde el nivel superior del repositorio dañado). La copia de seguridad no necesita estar completa, solo necesita tener los objetos que faltan.

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
fuente
4

Antes de intentar cualquiera de las correcciones descritas en esta página, le aconsejo que haga una copia de su repositorio y trabaje solo en esta copia. Luego, al final, si puede arreglarlo, compárelo con el original para asegurarse de que no perdió ningún archivo en el proceso de reparación.

Otra alternativa que funcionó para mí fue restablecer el encabezado y el índice de git a su estado anterior usando:

git reset --keep

También puede hacer lo mismo manualmente abriendo la GUI de Git, seleccionando cada "Cambios por etapas" y haciendo clic en "Desinstalar el cambio". Cuando todo esté sin preparar, ahora debería poder comprimir su base de datos, verificar su base de datos y confirmar.

También probé los siguientes comandos, pero no funcionaron para mí, pero podrían serlo para usted dependiendo del problema exacto que tenga:

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

Finalmente, para evitar que este problema de sincronización dañe su índice git (lo que puede suceder con DropBox, SpiderOak o cualquier otro disco en la nube), puede hacer lo siguiente:

  1. Convierta su .gitcarpeta en un solo archivo git "paquete" usando:, git bundle create my_repo.git --allentonces debería funcionar igual que antes, pero como todo está en un solo archivo, ya no correrá el riesgo de que la sincronización dañe su repositorio git.
  2. Desactivar la sincronización instantánea : SpiderOak te permite configurar la programación para verificar cambios en "automático" (lo que significa que es lo antes posible, estando monitoreando los cambios de archivos gracias a las notificaciones del SO). Esto es malo porque comenzará a cargar cambios tan pronto como esté haciendo un cambio, y luego descargará el cambio, por lo que podría borrar los últimos cambios que estaba haciendo. Una solución para solucionar este problema es establecer el retraso de supervisión de cambios en 5 minutos o más. Esto también soluciona problemas con las aplicaciones de toma de notas para guardar instantáneamente (como Notepad ++).
gaboroso
fuente
3

Si está desesperado, puede intentar esto:

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

Obtendrá sus datos, pero perderá el historial. Fui con prueba y error en mi repositorio y --depth=10trabajé, pero --depth=50me falló.

alberto56
fuente
3

Intenté alejar los archivos de objeto con 0 bytes y recuperarlos nuevamente desde el control remoto, y funcionó:

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

Recuperó los objetos faltantes del control remoto y me permitió continuar trabajando sin reinicializar todo el repositorio.

AlejandroVD
fuente
2

Estaba enfrentando el mismo problema, así que reemplacé la carpeta ".git" con una versión de respaldo y todavía no funcionaba porque el archivo .gitconfig estaba dañado. El BSOD de mi computadora portátil lo corrompió. Lo reemplacé con el siguiente código y sourcetree restauró todos mis repositorios.

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

No sé si esto ayudará a alguien, pero esta es solo otra solución que funcionó para mí.

Aspirante canadiense
fuente
2

Elimina el índice y reinicia

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

Experimenté problemas similares usando git versión 2.7.1 bajo Ubuntu 18.04.3 últimamente. Así es como lo hice:

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 mayor parte del tiempo el proceso de recuperación fue exitoso

Jonathan L
fuente
git-repair es de hecho una herramienta muy útil. Me ayudó a recuperar un repositorio. El problema con la mayoría de los otros métodos es que pierdes tus alijos, lo cual no era una opción para mí.
Jan Rychter
1

En mi caso, estaba creando el repositorio a partir del código fuente que ya estaba en mi PC y apareció ese error. Eliminé la carpeta .git e hice todo de nuevo y funcionó :)

Carolina
fuente
1

Quería agregar esto como un comentario debajo de la increíble respuesta de Zoey Hewil anterior, pero actualmente no tengo suficiente representante para hacerlo, así que tengo que agregarlo aquí y dar crédito por su trabajo: P

Si está usando Poshgit y se siente excepcionalmente perezoso, puede usar lo siguiente para extraer automáticamente su URL de su configuración de git y hacer un trabajo fácil aún más fácil. Se aplican advertencias estándar sobre probar esto en una copia / hacer una copia de seguridad de su repositorio local primero en caso de que explote en su cara.

$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
fuente
0

De manera rápida, si tiene un cambio en su proyecto actual y no quiere perderlo, mueva su proyecto actual a algún lugar, clone el proyecto de github a esta carpeta y haga algún cambio e intente confirmarlo nuevamente. O simplemente elimine el repositorio y vuelva a clonarlo, funcionó para mí.

certilremy
fuente
-2

Este comando funcionó para mí:

$ git reset --mixed 
Sin arrepentimientos
fuente