Encuentra cuándo se eliminó un archivo en Git

1035

Tengo un repositorio Git con n commits.

Tengo un archivo que necesito, y que solía estar en el repositorio, y que de repente busco y pienso "¡Oh! ¿A dónde fue ese archivo?"

¿Hay una (serie de) comando (s) de Git que me dirá que "file realmente_needed.txt fue eliminado en commit n-13"?

En otras palabras, sin mirar cada confirmación individual y sabiendo que mi repositorio de Git tiene cada cambio de cada archivo, ¿puedo encontrar rápidamente la última confirmación que TIENE ese archivo, para poder recuperarlo?

JohnMetta
fuente
2
El enlace compartido por Pedro tenía la respuesta a mi pregunta: cómo encontrar un archivo eliminado cuando no recuerda la ruta.
Gordon Bean

Respuestas:

1127

git log --full-history -- [file path] muestra los cambios de un archivo, funciona incluso si el archivo fue eliminado.

Ejemplo:

git log --full-history  -- myfile

Si desea ver solo la última confirmación, que eliminó un archivo, use -1 además, por ejemplo, git log --full-history -1 -- [file path]

Ver qué confirmación eliminó un archivo

vogella
fuente
16
¿Es posible buscar patrones? Olvidé el nombre completo del archivo = (¿tal vez es posible obtener un registro de todas las eliminaciones?
wutzebaer
66
lo encontré aquí: stackoverflow.com/questions/6017987/…
wutzebaer
66
Tenga en cuenta que si está utilizando PowerShell, los guiones deben escaparse: git log '-' [ruta del archivo]. Esperemos que esto le pueda dar a alguien más el crujir de dientes.
A. Wilson
68
Pude buscar usando git log -- */<<filename>>.<<file extension>>sin conocer la ruta completa del archivo.
Tom Howard
2
@MERose corchetes están allí como un marcador de posición para la ruta real del archivo.
Emile Bergeron
229

Respuesta corta:

git log --full-history -- your_file

le mostrará todas las confirmaciones en la historia de su repositorio, incluidas las confirmaciones de fusión, que se tocaron your_file. El último (superior) es el que eliminó el archivo.

Alguna explicación:

La --full-historybandera aquí es importante. Sin él, Git realiza una "simplificación del historial" cuando le solicita el registro de un archivo. Los documentos son ligeros sobre los detalles sobre cómo funciona esto exactamente y me falta el valor y el coraje necesarios para tratar de resolverlo a partir del código fuente, pero los documentos de git-log tienen mucho que decir:

Modo por defecto

Simplifica la historia a la historia más simple explicando el estado final del árbol. Más simple porque poda algunas ramas laterales si el resultado final es el mismo (es decir, fusionando ramas con el mismo contenido)

Esto es obviamente preocupante cuando se elimina el archivo cuyo historial queremos , ya que el historial más simple que explica el estado final de un archivo eliminado es sin historial . ¿Existe el riesgo de que git logsin --full-historysimplemente afirme que el archivo nunca fue creado? Por desgracia sí. Aquí hay una demostración:

mark@lunchbox:~/example$ git init
Initialised empty Git repository in /home/mark/example/.git/
mark@lunchbox:~/example$ touch foo && git add foo && git commit -m "Added foo"
[master (root-commit) ddff7a7] Added foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
mark@lunchbox:~/example$ git checkout -b newbranch
Switched to a new branch 'newbranch'
mark@lunchbox:~/example$ touch bar && git add bar && git commit -m "Added bar"
[newbranch 7f9299a] Added bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git rm foo && git commit -m "Deleted foo"
rm 'foo'
[master 7740344] Deleted foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 foo
mark@lunchbox:~/example$ git checkout newbranch
Switched to branch 'newbranch'
mark@lunchbox:~/example$ git rm bar && git commit -m "Deleted bar"
rm 'bar'
[newbranch 873ed35] Deleted bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git merge newbranch
Already up-to-date!
Merge made by the 'recursive' strategy.
mark@lunchbox:~/example$ git log -- foo
commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log -- bar
mark@lunchbox:~/example$ git log --full-history -- foo
commit 2463e56a21e8ee529a59b63f2c6fcc9914a2b37c
Merge: 7740344 873ed35
Author: Mark Amery 
Date:   Tue Jan 12 22:51:36 2016 +0000

    Merge branch 'newbranch'

commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log --full-history -- bar
commit 873ed352c5e0f296b26d1582b3b0b2d99e40d37c
Author: Mark Amery 
Date:   Tue Jan 12 22:51:29 2016 +0000

    Deleted bar

commit 7f9299a80cc9114bf9f415e1e9a849f5d02f94ec
Author: Mark Amery 
Date:   Tue Jan 12 22:50:38 2016 +0000

    Added bar

Observe cómo git log -- baren el volcado de la terminal anterior resultó literalmente sin salida; Git está "simplificando" la historia en una ficción donde barnunca existió. git log --full-history -- bar, por otro lado, nos da el commit que creó bary el commit que lo eliminó.

Para ser claros: este problema no es meramente teórico. Solo busqué en los documentos y descubrí la --full-historybandera porque me git log -- some_fileestaba fallando en un repositorio real donde estaba tratando de rastrear un archivo eliminado. Simplificar la historia a veces puede ser útil cuando usted está tratando de entender cómo una existente en la actualidad archivo llegó a estar en su estado actual, pero cuando se trata de localizar a un archivo borrado es más probable que el tornillo que sobre ocultando la de comprometerse usted se preocupa por . Utilice siempre la --full-historybandera para este caso de uso.

Mark Amery
fuente
44
Tenga en cuenta que esto solo busca el historial relevante para la rama actual (no 'historial de repositorio completo') ... es decir, si el archivo aún no se ha eliminado en la rama actual pero ha estado en otra rama, no encontrará el compromiso de eliminación. Debes estar en cualquier rama donde el archivo ya haya sido eliminado . Tal vez es obvio cuando lo pienso, pero al principio me sorprendió.
Anentropic
1
Esta respuesta funciona. Pero a partir de la git logsalida en sí, no es del todo obvio que la última confirmación haya eliminado el archivo. También intenté git log --name-status --full-history -- file_namey git log -p --stat --full-history -- file_name, pero ninguno indica explícitamente que el archivo se eliminó en la última confirmación. Esto parece un error.
Martin_W
@Martin_ATS mkdir somedir && cd somedir && git init && touch foo && git add foo && git commit -m "Added foo" && git checkout -b newbranch && touch bar && git add bar && git commit -m "Added bar" && git checkout master && git rm foo && git commit -m "Deleted foo" && git checkout newbranch && git rm bar && git commit -m "Deleted bar" && git checkout master && git merge newbranch && git log --name-status --full-history -- barincluye D bary A barpara mí en la salida del registro con Git 2.12.2. ¿No ves esas líneas en la salida? ¿Qué versión tienes?
Mark Amery
git version 2.15.1Sí, su secuencia de comando informa D bary A bar. Quizás mi problema es particular al historial de mi archivo. Estaba rastreando el historial de un .htaccessarchivo que fue eliminado y eliminado. Finalmente lo descubrí y volví a agregar el archivo. Cuando lo incluyo --name-statusen el git logcomando, veo dos A .htaccessentradas (ya que lo agregué en el último commit) pero no D .htaccess. Por lo tanto, parece que en algunos casos, aunque se haya eliminado un archivo del repositorio, git logno se mostrará una D file_nameentrada explícita .
Martin_W
@Martin_ATS Curioso. Me pregunto si tal vez .htaccessse agregó en un commit X pero luego no se incluyó en el commit de fusión que trajo X al maestro. Eso es lo único en lo que puedo pensar que podría argumentar que debería parecer un archivo que se ha agregado y nunca eliminado y que aún no está presente. Sería interesante tratar de descubrir un MCVE, luego determinar si es un error de Git y, de no ser así, si es posible modificar mi respuesta para manejar su caso.
Mark Amery
84

Git log pero necesita prefijar la ruta con --

P.ej:

dan-mac:test dani$ git log file1.txt
fatal: ambiguous argument 'file1.txt': unknown revision or path not in the working tree.

dan-mac:test dani$ git log -- file1.txt
 commit 0f7c4e1c36e0b39225d10b26f3dea40ad128b976
 Author: Daniel Palacio <[email protected]>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo
daniel
fuente
31

Acabo de agregar una solución aquí (¿hay alguna manera en git para enumerar todos los archivos eliminados en el repositorio?) Para encontrar las confirmaciones de los archivos eliminados mediante el uso de una expresión regular:

git log --diff-filter=D --summary | sed -n '/^commit/h;/\/some_dir\//{G;s/\ncommit \(.*\)/ \1/gp}'

Esto devuelve todo lo eliminado dentro de un directorio llamado some_dir(en cascada). Cualquier sed regexp allí donde \/some_dir\/está será suficiente.

OSX (gracias a @triplee y @keif)

git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
estani
fuente
1
Agradable. Algunos desajustes en bash en OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Brent Faust
@BrentFoust Es una pena que no pueda probar eso ... intente agregar un espacio al final (después de las llaves pero antes de la comilla simple), la página del manual en línea no tiene claro eso ...
estani
Buena sugerencia Pero agregar un espacio antes de la comilla simple no ayudó. Tampoco lo hizo un espacio antes de la llave de cierre.
Brent Faust
1
BSD / OSX sedaparentemente no siempre es bueno con punto y coma como separadores de comandos. Intente cambiarlos a nuevas líneas o cambie ased -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
tripleee
1
Lo probé y git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }trabajé para mí en OSX.
keif
22

Puede encontrar la última confirmación que eliminó el archivo de la siguiente manera:

git rev-list -n 1 HEAD -- [file_path]

Más información disponible aquí

Akif
fuente
11
La solución principal votada no funcionó para mí, pero esta sí.
Nick Heiner el
0

Tratar:

git log --stat | grep file
Eric Woodruff
fuente