Encuentra y restaura un archivo eliminado en un repositorio Git

2802

Digamos que estoy en un repositorio Git. Elimino un archivo y confirmo ese cambio. Sigo trabajando y hago algunos compromisos más. Entonces, encuentro que necesito restaurar ese archivo.

Sé que puedo retirar un archivo usando git checkout HEAD^ foo.bar, pero realmente no sé cuándo se eliminó ese archivo.

  1. ¿Cuál sería la forma más rápida de encontrar la confirmación que eliminó un nombre de archivo dado?
  2. ¿Cuál sería la forma más fácil de recuperar ese archivo en mi copia de trabajo?

Espero no tener que buscar manualmente mis registros, pagar el proyecto completo para un SHA determinado y luego copiar manualmente ese archivo en mi pago original del proyecto.

avdgaag
fuente
39
tenga en cuenta que el comentario anterior responde a la pregunta en el título, no en el cuerpo, que incluye averiguar cuándo se eliminó el archivo.
avdgaag
8
Para encontrar el commit, se eliminó un archivo en:git log --diff-filter=D -- path/to/file
titaniumdecoy
54
@hhh git checkout deletedFilese recuperará deletedFilesi se ha eliminado pero esa eliminación aún no se ha realizado o confirmado . Esa no es la pregunta que se hace aquí; Esta pregunta trata sobre cómo restaurar un archivo cuya eliminación se confirmó hace muchos commits.
Mark Amery el

Respuestas:

3150

Encuentra el último commit que afectó la ruta dada. Como el archivo no está en la confirmación HEAD, esta confirmación debe haberlo eliminado.

git rev-list -n 1 HEAD -- <file_path>

Luego revise la versión en el commit antes, usando el cursor (^ símbolo ):

git checkout <deleting_commit>^ -- <file_path>

O en un comando, si $filees el archivo en cuestión.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Si está utilizando zsh y tiene habilitada la opción EXTENDED_GLOB, el símbolo de intercalación no funcionará. Puedes usar ~1en su lugar.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"
CB Bailey
fuente
94
El truco es verificar el commit ANTES, usando el sufijo ^. Gracias.
Christian Oudard
44
Por alguna razón, esto no funcionará en zsh. ± git checkout $(git rev-list -n 1 HEAD "spec/Sporkfile_example.rb")^ -- "spec/Sporkfile_example.rb" zsh: no matches found: b71c152d8f38dcd23ad7600a93f261a7252c59e9^ Cambié a bash y funcionó bien sin embargo.
zoras
20
Desde la línea de comandos de Windows recibí un error. error: pathspec <filename> did not match any file(s) known to git.. La solución fue usar git bash.
donturner
56
@zoras zsh tiene su propia expansión en '^' Creo, pero puede usar la sintaxis alternativa de '~ 1': git checkout <deleting-commit>~1 -- <file-path> ~ X le permite especificar X commits antes del commit especificado, entonces ~ 1 es el commit antes, ~ 2 es dos commits antes, etc.
Nils Luxton
22
En el indicador de cmd de Windows, ¡el ^personaje es el carácter de escape! Por lo tanto, en cmd, debe escribir ^^para indicarle a cmd que desea un único literal ^ y que no escapará de otra cosa después de él. Lo que le sucede a muchas personas es que ^le sigue un espacio. Entonces cmd cree que estás escapando del espacio, lo que simplemente produce un personaje espacial. Por lo tanto, cuando git obtiene los argumentos cli, ve SHA1y no SHA1^ . Es muy molesto. ~no es un personaje de escape, por eso todavía funciona. (PD: si crees que los googlers querrán esta información, por favor vota este comentario)
Alexander Bird
875
  1. Utilizar git log --diff-filter=D --summary para obtener todos los commits que han eliminado archivos y los archivos eliminados;
  2. Use git checkout $commit~1 path/to/file.extpara restaurar el archivo eliminado.

¿Dónde $commitestá el valor de la confirmación que has encontrado en el paso 1, por ejemplo,e4cf499627

Robert Munteanu
fuente
10
curioso, ¿a qué se refiere el ~ 1?
tommy chheng
77
@tommy: la especificación tilde te dará el enésimo nieto del commit nombrado. Ver book.git-scm.com/4_git_treeishes.html para más detalles.
Robert Munteanu
55
Este es, con mucho, el enfoque más fácil e intuitivo. git log -- *PartOfMyFileName*. Gracias por el$commit~1
bgs
3
la git checkout $commit~1 filenamesintaxis funciona perfecta para archivos individuales y también para directorios completos. es decir: para restaurar todas las imágenes borradas en ./images del sha 12345: git checkout 12345~1 images. gracias por esta respuesta!
noinput
34
@Alexar $commit~1significa que debe agregar el nombre de la confirmación. Algo así como 1d0c9ef6eb4e39488490543570c31c2ff594426cdonde $commitestá.
Eugene
319

Para restaurar todos esos archivos eliminados en una carpeta, ingrese el siguiente comando.

git ls-files -d | xargs git checkout --
Manu
fuente
1
¿A dónde se canalizan los archivos? No veo cambio
William Grand
21
Este es probablemente el método más fácil. Es pervertido lo difícil que githa hecho incluso la tarea más simple.
jww
git checkout - [archivo] revertirá los cambios en el [archivo]. La tubería reemplazará [archivo] con el nombre de los archivos eliminados.
Manu
66
El ls-filessubcomando es útil, pero no parece funcionar para archivos que se han eliminado, git rmes decir, almacenados , y mucho menos confirmados, que es lo que solicitó el OP.
MarkHu
Esto funcionó para restaurar los archivos eliminados, pero ¿cómo puedo actualizar los archivos que cambiaron y aparecer como M myChangedFiledespués git checkout?
libby
124

Llegué a esta pregunta buscando restaurar un archivo que acabo de eliminar pero aún no había confirmado el cambio. En caso de que se encuentre en esta situación, todo lo que necesita hacer es lo siguiente:

git checkout HEAD -- path/to/file.ext

Brett
fuente
93

Si estás loco, úsalo git-bisect. Esto es lo que debes hacer:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Ahora es el momento de ejecutar la prueba automatizada. El comando de shell '[ -e foo.bar ]'devolverá 0 si foo.barexiste, y 1 de lo contrario. El comando "ejecutar" git-bisectutilizará la búsqueda binaria para encontrar automáticamente la primera confirmación donde falla la prueba. Comienza a la mitad del rango dado (de bueno a malo) y lo reduce a la mitad según el resultado de la prueba especificada.

git bisect run '[ -e foo.bar ]'

Ahora estás en el commit que lo eliminó. Desde aquí, puede volver al futuro y usar git-revertpara deshacer el cambio,

git bisect reset
git revert <the offending commit>

o podría retroceder un commit e inspeccionar manualmente el daño:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .
Josh Lee
fuente
2
¿Podrías dar más detalles git bisect run '[ -e foo.bar ]'?
avdgaag
También puede usar lo bueno y lo malo manualmente, si es algo que no se puede verificar automáticamente. Vea la página del manual de bisectos.
Josh Lee
1
@avdgaag git bisect runle dice a Git que automatice la bisección ejecutando el comando después de la palabra 'ejecutar' donde el comando debe regresar 0para una goodversión (ver git help bisectpara más detalles). Esta '[ -e foo.bar ]'es una expresión estándar para probar si el archivo foo.barexiste (la implementación generalmente está en un archivo al /usr/bin/[que normalmente está vinculado /usr/bin/test) y las comillas simples se usan para poner todo eso como un argumento de línea de comando único.
Mikko Rantalainen
Gran idea. Intenté este enfoque e identificó una confirmación antes de la eliminación, pero no la confirmación que realmente eliminó el archivo. Y en otra prueba identificó 2 confirmaciones antes de la eliminación.
Michael Osofsky
¿Insano? Tal vez. Pero bisect es una excelente manera de ayudar a encontrar dónde se introdujo un error, por lo que es una habilidad valiosa para aprender de todos modos. Entonces, aunque tal vez no sea la forma 'correcta' o más 'correcta' aquí, ¡sigue siendo una buena idea y definitivamente vale la pena un +1!
Pryftan
77

Mi nuevo alias favorito, basado en bonyiii 's respuesta (upvoted), y mi propia respuesta acerca de ' pasar un argumento a un comando de alias Git ':

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

¿Perdí un archivo, lo borré por error hace algunos commits?
Rápido:

git restore my_deleted_file

Crisis evitada.

Advertencia, con Git 2.23 (Q3 2019) viene el comando experimental llamado git restore(!).
Así que cambie el nombre de este alias (como se muestra a continuación).


Robert Dailey propone en los comentarios el siguiente alias:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

Y jegan agrega en los comentarios :

Para configurar el alias desde la línea de comando, utilicé este comando:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 
VonC
fuente
77
Esto restaura todo el commit, no solo el archivo solicitado.
Daniel Bang
55
Aquí está mi alias, funciona maravillosamente:restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"
void.pointer
1
@RobertDailey ¡Eso se ve genial! He incluido su alias en la respuesta para mayor visibilidad.
VonC
1
Para configurar el alias desde la línea de comando, utilicé este comando:git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\""
jegan
2
Expansion of alias 'restore' failed; '!git' is not a git command
Karl Morrison
55

Si conoce el nombre de archivo, esta es una manera fácil con comandos básicos:

Enumere todas las confirmaciones para ese archivo.

git log -- path/to/file

La última confirmación (la más alta) es la que eliminó el archivo. Por lo tanto, debe restaurar la penúltima confirmación.

git checkout {second to last commit} -- path/to/file
Wisbucky
fuente
Acabo de usar esta solución y no hubo confirmación para la eliminación. Sin embargo, pude restaurar el archivo usando la identificación de confirmación más reciente.
Adam
¿La última hasta la última confirmación (confirmación previa para la eliminación) no contendría la versión más reciente del archivo eliminado? El penúltimo (el compromiso antes del compromiso anterior para la eliminación) podría quedar irremediablemente desactualizado.
Suncat2000
1
Esta es la primera solución que he visto que es lo suficientemente simple como para que no tenga que volver aquí para encontrarla la próxima vez. Tal vez.
Eloff
@ Suncat2000 "penúltimo" significa "confirmación previa para la eliminación", igual que "penúltimo". en.wiktionary.org/wiki/penultimate#Synonyms
wisbucky
¡Gracias un millón de veces por esta respuesta!
Rakesh Bk
29

Para restaurar un archivo borrado y comprometido:

git reset HEAD some/path
git checkout -- some/path

Fue probado en Git versión 1.7.5.4.

Fedir RYKHTIK
fuente
1
Eso no funcionó para mí. Después del pago, me error: pathspec 'foo' did not match any file(s) known to git.aseguré de que el nombre del archivo fuera correcto. Git versión 2.7.0
wisbucky
-1; esto está mal. Estos comandos deshacen una eliminación que aún no se ha confirmado (el primero elimina la eliminación del escenario, si está organizado, y el segundo descarta los cambios no almacenados en el archivo), pero aquí está afirmando que restablecerá un comprometido eliminación del archivo, que simplemente no es cierto y fallará con un error como ese en el comentario de @ wisbucky anterior.
Mark Amery
@MarkAmery De hecho, creo que este comando funcionó bien para aquellos desarrolladores, que no realizaron una puesta en escena explícita para comprometerse con los archivos eliminados git add -A, pero el archivo restaurado todavía estaba en una etapa no comprometida.
Fedir RYKHTIK
25

Si solo realizó cambios y eliminó un archivo, pero no lo confirmó, y ahora rompió con sus cambios

git checkout -- .

pero sus archivos borrados no regresaron, simplemente ejecuta el siguiente comando:

git checkout <file_path>

Y listo, tu archivo está de vuelta.

Paulo Linhares - Packapps
fuente
24

Tengo esta solución .

  1. Obtenga la identificación de la confirmación donde se eliminó el archivo utilizando una de las siguientes formas.

    • git log --grep=*word*
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # recomendado si apenas recuerda algo
  2. Deberías obtener algo como:

commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Autor: Alexander Orlov Fecha: jue 12 de mayo 23:44:27 2011 +0200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Autor: Alexander Orlov Fecha: jue 12 de mayo 22:10:22 2011 +0200

3 . Ahora, usando el commit id bfe68bd117e1091c96d2976c99b3bcc8310bebe7 do:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Como el ID de confirmación hace referencia a la confirmación donde ya se eliminó el archivo, debe hacer referencia a la confirmación justo antes de bfe68b, lo que puede hacer agregando ^1. Esto significa: dame el commit justo antes de bfe68b.

Alex
fuente
Este es el mismo enfoque que la respuesta aceptada, pero con algunas formas más de encontrar la confirmación de eliminación. Todavía me gusta el enfoque adoptado en la respuesta aceptada, pero estas son buenas alternativas. ¡Gracias!
avdgaag
Supongo que verificar primero el archivo eliminado y luego (sin cambiarlo) confirmarlo no crea una copia del archivo. ¿Derecha? (Necesito hacer esto con imágenes, y una copia
agrandaría
15
git checkout /path/to/deleted.file
usuario1528493
fuente
Esta para mi situación (eliminada involuntariamente) fue la solución más sencilla.
Paulo Oliveira
Una explicación estaría en orden.
Peter Mortensen
12

git undelete path/to/file.ext

  1. Ponga esto en su .bash_profile(u otro archivo relevante que se carga cuando abre un shell de comandos):

    git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'
    
  2. Luego use:

    git undelete path/to/file.ext
    

Este alias primero comprueba para encontrar la última confirmación donde existió este archivo, y luego realiza un pago Git de esa ruta de archivo desde la última confirmación donde existió este archivo. Fuente .

Beau Smith
fuente
11

En muchos casos, puede ser útil usar coreutils (grep, sed, etc.) junto con Git. Ya conozco estas herramientas bastante bien, pero Git no tanto. Si quisiera hacer una búsqueda de un archivo eliminado, haría lo siguiente:

git log --raw | grep -B 30 $'D\t.*deleted_file.c'

Cuando encuentro la revisión / commit:

git checkout <rev>^ -- path/to/refound/deleted_file.c

Al igual que otros han dicho antes que yo.

El archivo ahora se restaurará al estado que tenía antes de eliminarlo. Recuerde volver a confirmarlo en el árbol de trabajo si desea conservarlo.

Thomas E
fuente
7

Tuve que restaurar un montón de archivos eliminados de una confirmación específica, y lo logré con dos comandos:

git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git checkout <rev>^ -- 
git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git reset HEAD 

(Tenga en cuenta el espacio final al final de cada comando).

Los archivos se agregaron al archivo .gitignore y luego se borraron con git rm. Necesitaba restaurar los archivos, pero luego quitarlos del escenario. Tenía cientos de archivos para restaurar, y escribir cosas manualmente para cada archivo como en los otros ejemplos iba a ser demasiado lento.

kzar
fuente
7

En realidad, esta pregunta es directamente sobre Git, pero alguien como yo trabaja con herramientas de GUI como WebStorm VCS además de conocer los comandos de Git CLI.

Hago clic derecho en la ruta que contiene el archivo eliminado, y luego voy a Git y luego hago clic en Mostrar historial .

Ingrese la descripción de la imagen aquí

Las herramientas de VCS muestran el tren de todas las revisiones y puedo ver todos los compromisos y cambios de cada uno de ellos.

Ingrese la descripción de la imagen aquí

Luego selecciono las confirmaciones de que mi amigo elimine el PostAd.jsarchivo. ahora mira abajo:

Ingrese la descripción de la imagen aquí

Y ahora, puedo ver mi archivo de deseo eliminado. Simplemente hago doble clic en el nombre del archivo y se recupera.

Ingrese la descripción de la imagen aquí

Sé que mi respuesta no son comandos Git, pero es rápido, confiable y fácil para desarrolladores principiantes y profesionales. Las herramientas de WebStorm VCS son increíbles y perfectas para trabajar con Git y no necesita ningún otro complemento o herramientas.

AmerllicA
fuente
1
¡Esto es asombroso! Gracias. Definitivamente una solución más fácil para aquellos que usan cualquiera de los IDE de JetBrains.
Fabiano Arruda
¿Cómo restauramos el archivo si es imagen?
Nodirabegimxonoyim
Estimado @FabianoArruda, los IDE de JetBrains son herramientas poderosas para el desarrollo. Gracias por tu encantador comentario.
AmerllicA
Gracias a ti querido @PeterMortensen por la edición.
AmerllicA
6

Tenía la misma pregunta. Sin saberlo, había creado una confirmación pendiente .

Lista de confirmaciones pendientes

git fsck --lost-found

Inspeccione cada confirmación pendiente

git reset --hard <commit id>

Mis archivos reaparecieron cuando me mudé a la confirmación pendiente.

git status por la razón:

“HEAD detached from <commit id where it detached>”

rustyMagnet
fuente
2
Muchas gracias. Me ayudaste a restaurar miles de líneas de códigos.
Rubén
5
user@bsd:~/work/git$ rm slides.tex
user@bsd:~/work/git$ git pull 
Already up-to-date.
user@bsd:~/work/git$ ls slides.tex
ls: slides.tex: No such file or directory

Restaurar el archivo eliminado:

user@bsd:~/work/git$ git checkout
D       .slides.tex.swp
D       slides.tex
user@bsd:~/work/git$ git checkout slides.tex 
user@bsd:~/work/git$ ls slides.tex
slides.tex
usuario1797498
fuente
2
La pregunta era sobre la restauración de un archivo después de que se haya eliminado y se haya confirmado el cambio. Esta respuesta se trata de restaurar un archivo que se eliminó solo en el directorio de trabajo.
akaihola
Eso es cierto, y eso era lo que estaba buscando.
Hola Soy Edu Feliz Navidad
4

Si conoce la confirmación que eliminó los archivos, ejecute este comando donde <SHA1_deletion>está la confirmación que eliminó el archivo:

git diff --diff-filter=D --name-only <SHA1_deletion>~1 <SHA1_deletion> | xargs git checkout <SHA1_deletion>~1 --

La parte anterior a la tubería enumera todos los archivos que se eliminaron en la confirmación; todos son de pago de la confirmación anterior para restaurarlos.

Tony Wickham
fuente
4

Encuentra el commit que eliminó tu archivo:

git log --diff-filter=D --oneline -- path/to/file | cut -f -d ' '

Salida de muestra:

4711174

A partir de Git 2.23 en realidad hay un restorecomando. Todavía es experimental, pero para restaurar algo que eliminó en una confirmación (4711174 en este caso), puede escribir:

git restore --source=4711174^ path/to/file

Tenga en cuenta la ^ después de la identificación de confirmación, ya que queremos restaurar algo de la confirmación antes de la que eliminó el archivo.

El --sourceargumento le dice al restorecomando dónde buscar los archivos para restaurar y puede ser cualquier confirmación e incluso el índice.

Ver: git-restore doc para git 2.23.0

cb2
fuente
4

En nuestro caso, eliminamos accidentalmente archivos en una confirmación y algunas confirmaciones más tarde nos dimos cuenta de nuestro error y queríamos recuperar todos los archivos que se eliminaron, pero no los que se modificaron.

Basado en la excelente respuesta de Charles Bailey, aquí está mi frase:

git co $(git rev-list -n 1 HEAD -- <file_path>)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- <file_path>)~1 head | grep '^D' | cut -f 2)
bonyiii
fuente
2

Simple y preciso

En primer lugar, obtenga una última confirmación estable en la que tenga ese archivo:

git log 

Digamos que encuentra $ commitid 1234567 ..., entonces

git checkout <$commitid> $fileName

Esto restaurará la versión del archivo que estaba en esa confirmación.

Sudhanshu Jain
fuente
1

Para la mejor manera de hacerlo, pruébalo.


Primero, encuentre el id de confirmación del compromiso que eliminó su archivo. Le dará un resumen de las confirmaciones que eliminaron archivos.

git log --diff-filter = D --summary

git checkout 84sdhfddbdddf ~ 1

Nota: 84sdhfddbdddes tucommit id

A través de esto, puede recuperar fácilmente todos los archivos eliminados.

Ritesh Adulkar
fuente
1

Siempre podría git revertsu confirmación que eliminó el archivo. ( Esto supone que la eliminación fue el único cambio en la confirmación ) .

> git log
commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3
Author: Dave <[email protected]>
Date:   Thu May 9 11:11:06 2019 -0700

    deleted readme.md

Y si ha seguido trabajando y luego se dio cuenta de que no deseaba confirmar esa eliminación, puede revertirla usando:

> git revert 2994bd

Ahora git logmuestra:

> git log
Author: Dave <[email protected]>
Date:   Thu May 9 11:17:41 2019 -0700

    Revert "deleted readme"

    This reverts commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3.

Y readme.mdha sido restaurado en el repositorio.

Dave Baghdanov
fuente
Dado que la pregunta supone que se realizaron varias confirmaciones después de que se eliminó el archivo y que no hay indicios de que las confirmaciones posteriores no sean deseadas, no parece probable que esto ayude al OP en la situación descrita.
Jonathan Leffler
1
¡Sí! Puede realizar confirmaciones posteriores y aún revertir la confirmación de eliminación. Entonces, si commit 111 elimina el archivo y commit 222, 333, 444, agrega / modifica cosas, aún puede revertir commit 111 para deshacer la eliminación, y se convertirá en commit 555
Dave Baghdanov
0

También tengo este problema al usar el siguiente código para recuperar un archivo anterior en un directorio local:

git checkout <file path with name>

El siguiente ejemplo está funcionando para mí:

git checkout resources/views/usaSchools.blade.php

Akbor
fuente
Mencione cuál es el problema
Akbor
La eliminación ya estaba comprometida. Debe especificar la confirmación de restauración desde en este caso.
sba
-1
$ git log --diff-filter=D --summary  | grep "delete" | sort
kujiy
fuente
Una explicación estaría en orden.
Peter Mortensen
-1

Si no se ha confirmado la eliminación, el siguiente comando restaurará el archivo eliminado en el árbol de trabajo.

$ git checkout -- <file>

Puede obtener una lista de todos los archivos eliminados en el árbol de trabajo utilizando el siguiente comando.

$ git ls-files --deleted

Si la eliminación se ha confirmado, busque la confirmación donde sucedió y luego recupere el archivo de esta confirmación.

$ git rev-list -n 1 HEAD -- <file>
$ git checkout <commit>^ -- <file>

En caso de que esté buscando la ruta del archivo para recuperar, el siguiente comando mostrará un resumen de todos los archivos eliminados.

$ git log --diff-filter=D --summary
Muhammad Soliman
fuente
-1

Para restaurar todos los archivos eliminados con Git, también puede hacer:

git checkout $(git ls-files --deleted)

Donde git ls-files --deletedenumera todos los archivos eliminados y git checkout $(git command)restaura la lista de archivos en un parámetro.

Charles Duporge
fuente