¿Cómo encontrar el commit de Git que introdujo una cadena en cualquier rama?

396

Quiero poder encontrar cierta cadena que se introdujo en cualquier confirmación en cualquier rama, ¿cómo puedo hacer eso? Encontré algo (que modifiqué para Win32), pero git whatchangedno parece estar buscando en las diferentes ramas (ignore el fragmento py3k, es solo una solución de alimentación de línea msys / win)

git whatchanged -- <file> | \
grep "^commit " | \
python -c "exec(\"import sys,msvcrt,os\nmsvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\nfor l in sys.stdin: print(l.split()[1])\")" | \
xargs -i% git show origin % -- <file>

Realmente no importa si su solución es lenta.

Jonas Byström
fuente

Respuestas:

685

Tu puedes hacer:

git log -S <whatever> --source --all

Para encontrar todas las confirmaciones que agregaron o eliminaron la cadena fija whatever . El --allparámetro significa comenzar desde cada rama y --sourcesignifica mostrar cuál de esas ramas condujo a encontrar esa confirmación. A menudo es útil agregar -ppara mostrar los parches que cada uno de esos commits también introduciría.

Las versiones de git desde 1.7.4 también tienen una -Gopción similar , que toma una expresión regular . Esto en realidad tiene una semántica diferente (y bastante obvia), explicada en esta publicación de blog de Junio ​​Hamano .

Como thameera señala en los comentarios, debe poner comillas alrededor del término de búsqueda si contiene espacios u otros caracteres especiales, por ejemplo:

git log -S 'hello world' --source --all
git log -S "dude, where's my car?" --source --all

Aquí hay un ejemplo usando -Gpara encontrar ocurrencias de function foo() {:

git log -G "^(\s)*function foo[(][)](\s)*{$" --source --all
Mark Longair
fuente
19
+1 por excelencia. Señalar a -S es una cosa, explicar cosas, mejor. Además, me gusta usar --decorate para ver de qué ramas provienen las cosas
sehe
77
@sehe: Gracias por tu bonito comentario. Supongo que vale la pena señalar que --decoratesolo agrega el nombre de la rama al commit en la punta de cada rama. En la práctica realmente no uso --sourceo --decorate, y en su lugar utilizar git branch -a --contains <commit-hash>para encontrar qué ramas contienen el commit que me interesa.
Marcos Longair
3
agregue -p para ver la diferencia en línea, también, FWIW
rogerdpack
1
@MarkLongair no muestra los cambios realizados en la fusión. ¿Alguna sugerencia para mostrarlos también?
Pahlevi Fikri Auliya
2
Para mí esto sólo funciona si se quita el espacio entre la S y el término de búsqueda, es decir, git log -S"dude, where's my car?" --source --all. @ribamar también escribió eso en una respuesta a continuación, pero podría pasar desapercibido fácilmente junto a esta respuesta principal.
bug313
69

--reverse también es útil ya que desea la primera confirmación que realizó el cambio:

git log --all -p --reverse --source -S 'needle'

De esta manera, los commits más antiguos aparecerán primero.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
20

La respuesta de Mark Longair es excelente, pero he encontrado que esta versión más simple funciona para mí.

git log -S whatever
Steven Penny
fuente
24
Solo para aclarar, eso funciona bien si la confirmación que está buscando está dentro HEAD, pero esta pregunta particular se hizo específicamente sobre buscar en todas las ramas en un repositorio.
Mark Longair
18

Jugando con las mismas respuestas:

$ git config --global alias.find '!git log --color -p -S '
  • ! es necesario porque de otra manera, git no pasa el argumento correctamente a -S. Ver esta respuesta
  • --color y -p ayudan a mostrar exactamente "lo que cambió"

Ahora puedes hacer

$ git find <whatever>

o

$ git find <whatever> --all
$ git find <whatever> master develop
albfan
fuente
6
git log -S"string_to_search" # options like --source --reverse --all etc

Preste atención para no usar espacios entre S y "string_to_search". En algunas configuraciones (git 1.7.1), obtendrá un error como:

fatal: ambiguous argument 'string_to_search': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions
ribamar
fuente
2

Si bien esto no responde directamente a su pregunta, creo que podría ser una buena solución para usted en el futuro. Vi una parte de mi código, que era malo. No sabía quién lo escribió ni cuándo. Pude ver todos los cambios del archivo, pero estaba claro que el código se había movido de otro archivo a este. Quería encontrar quién realmente lo agregó en primer lugar.

Para hacer esto, usé Git bisect , que rápidamente me permitió encontrar al pecador.

Corrí git bisect starty luego git bisect bad, porque la revisión revisada tenía el problema. Como yo no sé cuándo ocurrió el problema, me dirigidos el primero de confirmación para la "buena", git bisect good <initial sha>.

Luego seguí buscando en el repositorio el código incorrecto. Cuando lo encontré, me encontré git bisect bad, y cuando no estaba allí: git bisect good.

En ~ 11 pasos, cubrí ~ 1000 confirmaciones y encontré la confirmación exacta, donde se introdujo el problema. Bastante genial

Eldamir
fuente
2

No estoy seguro de por qué la respuesta aceptada no funciona en mi entorno, finalmente ejecuto el siguiente comando para obtener lo que necesito

git log --pretty=format:"%h - %an, %ar : %s"|grep "STRING"
BMW
fuente