¿Recuperar el registro de confirmación para una línea específica en un archivo?


503

¿Hay alguna forma de que git le proporcione un registro de confirmación solo para las confirmaciones que tocaron una línea particular en un archivo?

Me gusta git blame, pero git blamele mostrará la ÚLTIMA confirmación que tocó una línea en particular.

Realmente me gustaría obtener un registro similar, no la lista de confirmaciones en cualquier parte del archivo, sino solo las confirmaciones que tocaron una línea en particular.


2
Ver también:
Culpa de

Respuestas:


632

Ver también Git: descubre qué commits alguna vez tocaron un rango de líneas .


Desde Git 1.8.4 , git logtiene -Lque ver la evolución de una gama de líneas.

Por ejemplo, suponga que mira git blamela salida de. Aquí -L 150,+11significa "solo mirar las líneas 150 a 150 + 11":

$ git blame -L 150,+11 -- git-web--browse.sh
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &

Y quieres saber la historia de lo que ahora es la línea 155.

Entonces, use git log. Aquí, -L 155,155:git-web--browse.shsignifica "rastrear la evolución de las líneas 155 a 155 en el archivo llamado git-web--browse.sh".

$ git log --pretty=short -u -L 155,155:git-web--browse.sh
commit 81f42f11496b9117273939c98d270af273c8a463
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: support opera, seamonkey and elinks

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -143,1 +143,1 @@
-firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)

commit a180055a47c6793eaaba6289f623cff32644215b
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: coding style

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -142,1 +142,1 @@
-    firefox|iceweasel)
+firefox|iceweasel)

commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
Author: Christian Couder <chriscool@tuxfamily.org>

    Rename 'git-help--browse.sh' to 'git-web--browse.sh'.

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- /dev/null
+++ b/git-web--browse.sh
@@ -0,0 +127,1 @@
+    firefox|iceweasel)

2
'git log --topo-order --graph -u -L 155,155: git-web--browse.sh' - esto ha dado un error fatal: 'nombre de objeto inválido 155,155'. Versión de Git: 1.8.3.2. ¿Alguna sugerencia?
BairDev

12
Actualice a Git 1.8.4 o posterior.
Matt McClure

44
¿Qué pasa si quiero saber "el historial de lo que es la línea 155 en commitA" (en lugar de la línea 155 en la cabecera). ¿Puedo simplemente usar git log commitA-hash -L 155,155:file-name?
Ida

@Flimm, no tengo una preferencia fuerte.
Matt McClure

esto funciona bien, excepto si el archivo se ha movido / renombrado ... parece que a --follow no le gusta combinarlo con argumentos de rango de línea.
Mike Ellery

67

Puede obtener un conjunto de confirmaciones utilizando pick-ax.

git log -S'the line from your file' -- path/to/your/file.txt

Esto le dará todas las confirmaciones que afectaron ese texto en ese archivo. Si se cambió el nombre del archivo en algún momento, puede agregar --follow-parent.

Si desea inspeccionar las confirmaciones en cada una de estas ediciones, puede canalizar ese resultado a git show:

git log ... | xargs -n 1 git show

55
No estoy seguro de ver cómo esto ayuda. Si el texto se vio afectado, la línea ya no es la misma, por lo que el pico solo le mostrará el cambio más reciente. Entonces tendrías que hacer git log -S'the previous version of the line'y demás, exactamente como terminarías haciendo git blame -L. Y será mucho más lento que git blame, ya que tiene que buscar el texto en todas partes, no solo en el lugar dado.
Cascabel

Puede usar una expresión regular allí para que sea más aceptable. Actualmente no hay forma de "navegar a través de parches" en el tiempo sin algunas secuencias de comandos elaboradas. Espero que gitk obtenga esta funcionalidad en su vista de parche en el futuro.
Adam Dymitruk

3
¿Por qué esto se llama pick-axe ? :)
Ciprian Tomoiagă


14

Una manera extremadamente fácil de hacer esto es usando vim-fugitive . Simplemente abra el archivo en vim, seleccione las líneas que le interese usar V, luego ingrese

:Glog

Ahora puede usar :cnexty :cprevpara ver todas las revisiones del archivo donde se modifica esa línea. En cualquier momento, ingrese :Gblamepara ver la información de sha, autor y fecha.


13

Simplificando la respuesta de @ matt -

git blame -L14,15 -- <file_path>

Aquí tendrás la culpa de las líneas 14 to 15.

Como la -Lopción se espera Rangecomo un parámetro, no podemos obtener una Blamepara una sola línea usando la -Lopción ' .

Referencia


10

No creo que haya nada incorporado para esto. Se vuelve complicado por el hecho de que es raro que una sola línea cambie varias veces sin que el resto del archivo cambie sustancialmente también, por lo que tenderá a terminar con los números de línea cambiando mucho.

Si tiene la suerte de que la línea siempre tenga alguna característica de identificación, por ejemplo, una asignación a una variable cuyo nombre nunca cambió, puede usar la opción regex para git blame -L. Por ejemplo:

git blame -L '/variable_name *= */',+1

Pero esto solo encuentra la primera coincidencia para esa expresión regular, por lo que si no tiene una buena manera de hacer coincidir la línea, no es demasiado útil.

Podrías hackear algo, supongo. No tengo tiempo para escribir código en este momento, pero ... algo en este sentido. Ejecutar git blame -n -L $n,$n $file. El primer campo es el commit anterior tocado, y el segundo campo es el número de línea en ese commit, ya que podría haber cambiado. Agárrelos y ejecútelos git blame -n $n,$n $commit^ $file, es decir, lo mismo a partir de la confirmación antes de la última vez que se modificó el archivo.

(Tenga en cuenta que esto le fallará si la última confirmación que cambió la línea fue una confirmación de fusión. La forma principal en que esto podría suceder si la línea se cambia como parte de una resolución de conflicto de fusión).

Editar: Me encontré con esta publicación de la lista de correo de marzo de 2011 hoy, que menciona eso tigy git guitiene una función que lo ayudará a hacer esto. Parece que la característica se ha considerado, pero no está terminada, para git en sí.


6

Esto requerirá git blameque cada revisión significativa muestre una línea $LINEde archivo $FILE:

git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE

Como de costumbre, la culpa muestra el número de revisión al comienzo de cada línea. Puedes agregar

| sort | uniq -c

para obtener resultados agregados, algo así como una lista de confirmaciones que cambiaron esta línea. (No del todo, si el código solo se ha movido, esto podría mostrar la misma ID de confirmación dos veces para diferentes contenidos de la línea. Para un análisis más detallado, tendría que hacer una comparación retrasada de los git blameresultados de las confirmaciones adyacentes. ¿Alguien? )


Nuevamente, creo que esto no funciona, porque no rastrea la ubicación anterior de esa línea. Entonces, si se agregó una línea hace 2 commits, estaría mirando otra línea
Vic Seedoubleyew

6

Aquí hay una solución que define un alias git, por lo que podrá usarlo así:

git rblame -M -n -L '/REGEX/,+1' FILE

Ejemplo de salida:

00000000 18 (Not Committed Yet 2013-08-19 13:04:52 +0000 728) fooREGEXbar
15227b97 18 (User1 2013-07-11 18:51:26 +0000 728) fooREGEX
1748695d 23 (User2 2013-03-19 21:09:09 +0000 741) REGEXbar

Puede definir el alias en su .gitconfig o simplemente ejecutar el siguiente comando

git config alias.rblame !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); echo $line; done' dumb_param

Esta es una línea fea, así que aquí hay una función de bash equivalente ofuscada:

git-rblame () {
    local commit line
    while line=$(git blame "$@" $commit 2>/dev/null); do
        commit="${line:0:8}^"
        if [ "00000000^" == "$commit" ]; then
            commit=$(git rev-parse HEAD)
        fi
        echo $line
    done
}

La solución pickaxe ( git log --pickaxe-regex -S'REGEX ' ) solo le dará adiciones / eliminaciones de línea, no las otras alteraciones de la línea que contiene la expresión regular.

Una limitación de esta solución es que git blame solo devuelve la primera coincidencia REGEX, por lo que si existen varias coincidencias, la recursión puede "saltar" para seguir otra línea. Asegúrese de verificar la salida del historial completo para detectar esos "saltos" y luego arregle su REGEX para ignorar las líneas de parásitos.

Finalmente, aquí hay una versión alternativa que ejecuta git show en cada confirmación para obtener la diferencia completa:

git config alias.rblameshow !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); git show $commit; done' dumb_param

2

Puede mezclar git blamey git logcomandos para recuperar el resumen de cada confirmación en el comando git blame y agregarlos. Algo así como el siguiente script bash + awk. Agrega el resumen de confirmación como comentario de código en línea.

git blame FILE_NAME | awk -F" " \
'{
   commit = substr($0, 0, 8);
   if (!a[commit]) {
     query = "git log --oneline -n 1 " commit " --";
     (query | getline a[commit]);
   }
   print $0 "  // " substr(a[commit], 9);
 }'

En una linea:

git blame FILE_NAME | awk -F" " '{ commit = substr($0, 0, 8); if (!a[commit]) { query = "git log --oneline -n 1 " commit " --"; (query | getline a[commit]); } print $0 "  // " substr(a[commit], 9); }'

2

En mi caso, el número de línea había cambiado mucho con el tiempo. También estaba en git 1.8.3 que no admite expresiones regulares en "git blame -L". (RHEL7 todavía tiene 1.8.3)

myfile=haproxy.cfg
git rev-list HEAD -- $myfile | while read i
do
    git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"
done | grep "<sometext>"

Un trazador de líneas:

myfile=<myfile> ; git rev-list HEAD -- $myfile | while read i; do     git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"; done | grep "<sometext>"

Por supuesto, esto se puede convertir en un script o una función.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.