¿Cómo "culpo" a una línea eliminada?

506

git blamees ideal para líneas modificadas y agregadas, pero ¿cómo puedo encontrar cuando una línea que existía en una confirmación previa específica finalmente se eliminó? Estoy pensando bisect, pero esperaba algo más útil.

(Antes de preguntar: en este caso, acabo de hacer una git log -pbúsqueda de la línea de código y (a) un idiota acababa de eliminar la línea vital en el commit anterior y (b) yo era ese idiota).

Malvolio
fuente
44
Hay un seguimiento con una respuesta que aclara que git log -S<string> /path/to/filequiere una -co -ccasí para mostrar el traslado durante la fusión (conflictos)
TPI
3
Debería ser -cy --cc. @Steen: Correcto, ¡gracias por señalarlo! Estúpido descuido. Ojalá pudiera editar el comentario. Agregar uno nuevo, luego eliminar el mío, luego eliminar el tuyo es demasiado engorroso, supongo :)
cfi
44
Desearía git blametener una opción para mostrar líneas eliminadas (con quizás tachado o texto rojo) con la revisión en la que se eliminaron.
Craig McQueen
¿Sería difícil de escribir? No sé mucho sobre los aspectos internos de Git.
Malvolio

Respuestas:

636

Si conoce el contenido de la línea, este es un caso de uso ideal para:

git log -S <string> path/to/file

que muestra confirmaciones que introducen o eliminan una instancia de esa cadena. ¡También está el -G<regex>que hace lo mismo con las expresiones regulares! Consulte man git-logy busque las opciones -Gy -S, o piqueta (el nombre descriptivo de estas funciones) para obtener más información.

La -Sopción también se menciona en el encabezado de la página de git-blamemanual, en la sección de descripción, donde da un ejemplo usando git log -S....

Cascabel
fuente
Brillante ... justo lo que necesitaba en este trabajo de portabilidad en el que estoy trabajando en +1
jkp
37
Después de usar Git por más de 1 año, todavía me sorprende ver que Git siempre tiene un comando / opción en algún lugar para abordar casi cualquier escenario de uso que tenga. Gracias por compartir este, ¡es exactamente lo que necesito ahora!
Pascal Bourque
22
Este método me funcionó antes, pero justo ahora vi un caso en el que no encontré el commit donde se eliminó la línea. Resultó que la línea en cuestión se eliminó en una confirmación de fusión: ¿eso explicaría el error? (Sin git blame --reverseembargo, el método lo encontró.)
antinome
99
@antinome Para mostrar las confirmaciones de fusión, use la -copción adicionalmente.
Yunzen
2
Hice un ctrl + f en "-s" en la página de manual y no encontré nada. ¿Dónde en la página lo ves? Estoy usando git 1.8.5.2
temporary_user_name
135

Creo que lo que realmente quieres es

git blame --reverse START..END filename

Desde la página del manual :

Camina la historia hacia adelante en lugar de hacia atrás. En lugar de mostrar la revisión en la que apareció una línea, muestra la última revisión en la que ha existido una línea. Esto requiere una serie de revisiones como START..END donde el camino hacia la culpa existe en START.

Con git blame reverse, puede encontrar el último commit en el que apareció la línea. Todavía necesita obtener el commit que viene después.

Puede usar el siguiente comando para mostrar un registro git invertido. La primera confirmación mostrada será la última vez que aparezca esa línea, y la próxima confirmación será cuando se cambie o elimine.

git log --reverse --ancestry-path COMMIT^..master
Crónica
fuente
14
Si hay múltiples fusiones desde la rama donde se agregó la línea a la rama donde falta la línea (o cualquier otro caso en el que haya múltiples rutas en el linaje de INICIO a FIN), git blame --reversemostrará la revisión antes de la fusión que fue cronológicamente Por último, no la revisión antes de la fusión inicial donde se tomó la decisión de no tomar la línea. ¿Hay alguna forma de encontrar la primera revisión donde la línea dejó de existir en lugar de la más reciente?
rakslice
2
@rakslice, para eso puedes usar la culpa --reverse --first-parent, es un poco mejor.
max630
17

Solo para completar la respuesta de Cascabel :

git log --full-history -S <string> path/to/file

Tuve el mismo problema que se menciona aquí, pero resultó que faltaba la línea, porque una confirmación de fusión de una rama se revirtió y luego se fusionó nuevamente con ella, eliminando efectivamente la línea en cuestión. La --full-historybandera evita saltear esas confirmaciones.

estani
fuente
9

git culpa --reverse puede conseguir que cerca de donde se elimina la línea. Pero en realidad no apunta a la revisión donde se elimina la línea. Apunta a la última revisión donde la línea estaba presente . Entonces, si la siguiente revisión es una confirmación simple, tienes suerte y obtuviste la revisión de eliminación. OTOH, si la siguiente revisión es una confirmación de fusión , entonces las cosas pueden ponerse un poco locas.

Como parte del esfuerzo para crear difflame , abordé este mismo problema, por lo que si ya tiene Python instalado en su caja y está dispuesto a probarlo, no espere más y dígame cómo funciona.

https://github.com/eantoranz/difflame

eftshift0
fuente
0

Para cambios ocultos en commits de fusión

Las confirmaciones de fusión tienen sus cambios ocultos automáticamente en la salida del registro de Git. Tanto el pico como la culpa inversa no encontraron el cambio. Así que la línea que quería había sido agregada y luego eliminada y quería encontrar la fusión que la eliminó. El git log -p -- path/filehistorial del archivo solo mostró que se estaba agregando. Aquí está la mejor manera que encontré para encontrarlo:

git log -p -U9999 -- path/file

Busque el cambio, luego busque "^ commit" hacia atrás: el primer "^ commit" es el commit donde el archivo tuvo esa última línea. El segundo "^ commit" es después de que desapareció. El segundo commit podría ser el que lo eliminó. los-U9999 está destinado a mostrar el contenido completo del archivo (después de cada vez que el archivo se ha cambiado), suponiendo que sus archivos están todos max 9999 líneas.

Encuentra cualquier fusión relacionada a través de la fuerza bruta (diferencie cada posible compromiso de fusión con su primer padre, ejecute contra toneladas de confirmaciones)

git log --merges --pretty=format:"git diff %h^...%h | grep target_text" HEAD ^$(git merge-base A B) | sh -v 2>&1 | less

(Intenté restringir más el filtro de revisión, pero me encontré con problemas y no recomiendo esto. Los cambios de agregar / quitar que estaba buscando estaban en diferentes ramas que se fusionaron en diferentes momentos y A ... B no incluyó cuando los cambios se fusionaron en la línea principal).

Mostrar un árbol Git con estas dos confirmaciones (y gran parte del complejo historial de Git eliminado):

git log --graph --oneline A B ^$(git merge-base A B) (A es la primera confirmación anterior, B es la segunda confirmación anterior)

Muestra el historial de A y el historial de B menos el historial de A y B.

Versión alternativa (parece mostrar la ruta de forma más lineal en lugar del árbol de historial de Git normal; sin embargo, prefiero el árbol de historial de Git normal):

git log --graph --oneline A...B

Tres, no dos puntos: tres puntos significa "r1 r2 --no $ (git merge-base --all r1 r2). Es el conjunto de commits a los que se puede acceder desde cualquiera de r1 (lado izquierdo) o r2 (derecha lado), pero no de ambos ". - fuente: "hombre gitrevisions"

Curtis Yallop
fuente