Guía10 min

git pickaxe: encontrar el commit que introdujo una línea con git log -S y -G

Resumen

Guía práctica del pickaxe de git para coding agents: cómo usar git log -S<string> para encontrar dónde cambió el número de ocurrencias de un texto, y git log -G<regex> para buscar commits cuyo parche toca líneas que encajan con un patrón. Incluye el ejemplo canónico frotz de la documentación oficial, comparativa lado a lado, checklist de depuración y errores típicos al buscar el origen de una línea en un repo ajeno.

GitHub
Un coding agent examinando los commits de un repositorio con git log -S y -G para encontrar el origen de una línea de código

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

El pickaxe de git son las opciones -S y -G de git log, y buscan en todo el historial los commits que tocaron una cadena o un patrón concreto. Para un coding agent que llega a un repo ajeno, el pickaxe responde la pregunta "¿en qué commit apareció o desapareció esto?" sin recorrer cientos de commits a mano, y es complementario a git log para conjuntos de commits y al hub de comparativas y decisiones si el archivo existe en el working tree.

La diferencia entre -S y -G no es cosmética: uno cuenta ocurrencias y el otro mira el texto del parche. Elegir mal te hace perder commits que sí te interesan, o te obliga a revisar diffs irrelevantes.

Qué detecta cada uno: ocurrencias vs texto del parche

git log -S"<string>" lista los commits donde el número de ocurrencias de esa cadena cambio (subió, bajó o llegó a cero), considerando las versiones del archivo antes y después del commit. git log -G"<regex>" lista los commits cuyo patch contiene líneas añadidas o eliminadas que encajan con la expresión regular.

git log -S"<string>"git log -G"<regex>"
Qué buscacambios en el número de ocurrencias de la cadenalíneas añadidas o eliminadas que encajan con la regex
Argumento por defectocadena literalexpresión regular extendida POSIX
¿Regex?solo con --pickaxe-regexsiempre
Detecta que movieron la línea sin cambiar la cuentano
Caso de uso documentadoencontrar el origen de una cadenaencontrar commits que tocaron un patrón

El ejemplo canónico de la documentación oficial de git: un commit modifica una llamada y el número de apariciones no cambia.

- hit = frotz(nitfol, mf2.ptr, 1, 0);
+ return frotz(nitfol, two->ptr, 1, 0);

git log -G"frotz\(nitfol" muestra ese commit. git log -S"frotz\(nitfol" --pickaxe-regex no lo muestra, porque la cadena frotz(nitfol aparece una vez antes y una vez después: su cuenta es la misma. Esa es la distinción que debes memorizar: -S solo reacciona cuando la cantidad de veces que aparece el texto cambia.

Comparación entre git log -S, que cuenta ocurrencias de una cadena, y git log -G, que examina el texto del parche

Cuándo usar -S

Usa -S cuando quieras el commit que introdujo o eliminó una cadena concreta, no un patrón:

  • localizar dónde nació un nombre de función, una ruta o un endpoint
  • encontrar el commit que dejó de usar una función (git log -S"funcionDeprecada" --oneline)
  • detectar cuándo un valor mágico (una API key de prueba, un id de entorno) entró al historial
git log -S"framework.startApp()" --oneline -- src/

Agrega --pickaxe-all cuando el commit modificó varios archivos y quieres ver el diff completo, no solo el archivo donde está la cadena. Para el caso de "buscamos el origen de un texto", -S es exactamente la herramienta que describe la documentación.

Cuándo usar -G

Usa -G cuando la línea pudo cambiar de forma sin cambiar de cuenta:

  • renombrado de variable o extracción a constante
  • cambio de argumento en una llamada (el caso frotz)
  • cualquier búsqueda por patrón, por ejemplo todos los commits que tocaron llamadas a fetch en archivos TypeScript
git log -G"\.push\(" -p -- '*.ts'

-G es regex por defecto, sin necesidad de --pickaxe-regex. Si además quieres el diff del commit candidato, combínalo con -p y confirma con git show <sha> antes de afirmar nada en el reporte.

Un coding agent reduciendo el historial con git log -G hasta el commit que tocó un patrón

Combinar con el resto del flujo git

El pickaxe escanea blobs, así que en repos grandes conviene acotar antes de ejecutar:

  • ruta: git log -S... -- src/services/ limita el escaneo a esa subárbol
  • rango: --since, --until, y -n 10 para no inundar la salida
  • todas las ramas: --all cuando el commit pudo venir de una rama ya fusionada
  • tipo de cambio: --diff-filter=A para commits que solo añadieron, -D para borrados
  • binarios: sin un filtro textconv, los binarios se ignoran; con --text también se escanean

No confundas el pickaxe con git log --grep, que busca en los mensajes de commit, ni con git blame, que atribuye las líneas actuales a una versión concreta. El pickaxe trabaja sobre el contenido del diff a lo largo de la historia.

Checklist para depurar con pickaxe

  • ¿Cadena literal o patrón? -S vs -G (o -S + --pickaxe-regex)
  • ¿La cuenta pudo no cambiar? Entonces es -G, no -S
  • ¿Acoté por ruta y por rango de fechas antes de correr?
  • ¿Lo confirmé con git show <sha> y vi el parche real?
  • ¿Necesito todo el changeset? --pickaxe-all
  • ¿El resultado está vacío? Prueba --all o quita el filtro de ruta
  • ¿Texto binario? Añade --text o un filtro textconv

FAQ

¿Por qué git log -S"frotz\(nitfol" --pickaxe-regex no muestra el commit que modifica esa línea? Porque -S solo reacciona a cambios en el número de ocurrencias; si la cadena sigue apareciendo la misma cantidad de veces, el commit no califica. Para "el parche toca líneas que encajan con un patrón" usa -G, que no necesita --pickaxe-regex.

¿-S es siempre literal? Sí, salvo que pases --pickaxe-regex, que trata el argumento de -S como una expresión regular extendida POSIX.

¿Pickaxe y git log --grep son lo mismo? No. --grep filtra por el mensaje del commit; el pickaxe analiza el contenido del diff.

¿Detecta renombrados? -G sí, porque mira líneas del parche. -S detecta el cambio de cuenta, no la renombrada en sí: si el símbolo aparece igual cantidad de veces, no lo reporta.

¿El pickaxe tarda mucho? En repos grandes conviene acotar por ruta y rango; git log -S... -- src/ es notablemente más rápido que sin límite. Si el historial es enorme, revisa git commit-graph para acelerar el recorrido.

Resumen

-S responde "¿cuándo cambió la cuenta de esta cadena?" y -G responde "¿qué commits tocaron líneas que encajan con este patrón?". Acota por ruta y rango, confirma con git show, y usa --pickaxe-all para contexto completo. Con eso, encontrar el origen de una línea en un repo ajeno pasa de caracolear en git log a una consulta con respuesta en segundos. Si trabajas con repos compartidos, recuerda también el flujo seguro de worktrees para no contaminar tu rama mientras investigas.