git patch-id para coding agents: huella del diff, no del SHA
Resumen
git patch-id lee un parche por stdin y emite una huella SHA-1 del diff (sin números de línea). Default --unstable. --stable ignora el orden de archivos. --verbatim no recorta whitespace. Cero autónomo como filtro de historia. Distinto de cherry, apply y diff. Git 2.50.1.

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.
git patch-id calcula un identificador del parche, no del commit. El man (git-patch-id(1); git-scm.com/docs/git-patch-id HTTP 200, last-modified 2026-08-31; pie del man local Git 2.50.1.428.g0e8243, 2025-07-22; binario Git 2.50.1 / Apple Git-155): Compute unique ID for a patch. SYNOPSIS: git patch-id [--stable | --unstable | --verbatim]. DESCRIPTION: lee un parche desde stdin y suma SHA-1 de los file diffs ignorando números de línea. Dos parches con el mismo patch ID are almost guaranteed to be the same thing. El usecase del man: look for likely duplicate commits.
Contrato para un coding agent: stdin de un diff, una línea de salida, parar. No es cherry: cherry recorre una rama y marca +/−. Aquí no hay upstream ni HEAD. No es apply ni am: no pega nada. No es diff: diff produce el texto; patch-id lo consume.
No toca índice, working tree ni refs. Funciona fuera de un repo: el objeto vive en el texto del parche.
Qué hace (y qué no)
La salida son dos hex de 40 caracteres. El primero es el patch ID. El segundo es el commit ID si el stdin trae el prefijo de objeto que diff-tree / git show / format-patch ponen delante. Si el stdin es un git diff puro, el segundo campo es cuarenta ceros.
Verificado 2026-09-06 (Git 2.50.1 / Apple Git-155) en un repo mínimo base → commit que añade line2 a a.txt:
- stdin vacío: 0, cero líneas.
git show HEAD | git patch-id: 0. Dos hex. El segundo = SHA de HEAD.git diff HEAD~1 HEAD | git patch-id: 0. Mismo primer hex. Segundo =0000…0.git format-patch -1 --stdout | git patch-id: 0. Igual queshow.git diff-tree -p HEAD | git patch-id: 0. Igual queshow(el man habla de esta forma).git diff-tree HEADsin-p(raw): 0, vacío. Sin hunks no hay huella.--foo: 129, unknown option `foo'.- Fuera de un repo (
/tmp): 0, misma huella. No exige.git. - Un extra posicional (
git patch-id HEADcon el parche en stdin): 0, ignora el argumento. No hashea HEAD. No leas un path.
Algoritmos (man):
| Flag | Default | Whitespace | Orden de file diffs |
|---|---|---|---|
--unstable | sí (salvo config) | se ignora | el orden cambia el ID |
--stable | si patchid.stable=true | se ignora | el orden no cambia el ID |
--verbatim | si patchid.verbatim=true | no se recorta | como el input |
Verificado en el mismo commit de un archivo: --stable y --unstable coinciden. --verbatim diverge. Trailing spaces en la línea añadida: default y --stable siguen iguales; --verbatim cambia. Commit de dos archivos, mismos hunks en orden inverso: default/--unstable divergen; --stable coincide.
El man avisa: --stable no es el valor de Git ≤ 1.9. Una base de datos vieja de patch-ids se vuelve inútil si mezclas algoritmos. Un agente no “elige el más moderno” a ciegas.

Lo que el agente sí / no corre
| Quiero | Comando | Trampa |
|---|---|---|
| Huella de un commit | git show <sha> | git patch-id | git patch-id <sha> (el SHA se ignora) |
| Huella de un diff de trabajo | git diff -- <path> | git patch-id | volcar el diff al LLM “para que adivine duplicados” |
| Comparar dos series | dos pipes, comparar el primer hex | tratar el segundo hex (commit) como la huella |
| Duplicados vs upstream | cherry -v <upstream> <head> | reinventar cherry a mano con patch-id en un loop |
| Aplicar el parche | apply --check primero | patch-id no pega |
| Orden de archivos irrelevante | --stable explícito | asumir que el default ya es estable |
Prohibido en autónomo:
- Invocarlo sin stdin de un diff concreto. Verificado: vacío → exit 0 y silencio. Un agente no interpreta “no imprimió nada” como “no hay duplicados”.
- Pasar un SHA o un path como argumento. Verificado: se ignora. El contrato es pipe, no argv.
--verbatimpara “ser más fiel”. Cambia la huella con whitespace trivial. El default del man sí ignora whitespace;--verbatimes la excepción.- Cambiar
patchid.stable/patchid.verbatimen config global. El flag va en esa invocación. - Mezclar
--stabley--unstableen la misma tabla de duplicados. Son IDs distintos para el mismo parche reordenado. - Meter la línea entera en el contexto del modelo “para decidir un rebase”. Compara hex en el harness. El LLM no es un
==. - Encadenar a cherry-pick porque “el ID coincidió”. Equivalencia de parche ≠ el ticket. Un duplicado puede ser WIP, secreto o un revert a medias.
- Usar diff-tree raw (sin
-p) como input. Verificado: vacío. - Dump de
git show -pde toda la rama a patch-id “por si acaso”. Un commit por pipe, o usa cherry.
Receta (60 segundos)
Solo en un worktree propio. Stdin explícito:
git show <sha> | git patch-id
git diff <tree-ish> <tree-ish> -- <path> | git patch-id --stable
Lee el primer campo. Si vas a comparar dos diffs cuyo orden de archivos puede cambiar (-O, paths distintos), usa --stable en ambos. Si comparas git show contra git diff del mismo cambio, el primer hex coincide y el segundo no: eso no es un bug.
Para “qué de topic ya está en main”, no armes este loop. Corre cherry. cherry ya usa esta huella.
Checklist
- El input es un parche (show / format-patch /
diff-tree -p/diff), no un SHA en argv. - Exit 0 con una línea, o 0 vacío (stdin vacío / raw sin hunks). 129 = flag inventado.
- Comparas el primer hex. El segundo es commit o ceros, no la huella.
- Mismo algoritmo a ambos lados (
--stableo default). Cero mezcla. - Cero
--verbatim“por precisión”. - Cero config global
patchid.*. - Cero apply / am / cherry-pick porque coincidió.
- Duplicados de rama → cherry, no este binario en un
for. - La línea no entra al prompt del modelo.
FAQ
¿Es lo mismo que git cherry? No. cherry lista commits de una rama contra upstream con +/−. patch-id es el motor de huella de un solo parche por stdin. SEE ALSO del man de cherry apunta aquí.
¿Por qué git diff \| git patch-id pone ceros a la derecha? Porque ese stdin no trae el nombre de objeto del commit. El man lo documenta para git diff-tree con prefijo. Ceros ≠ error.
¿--stable siempre? No. Es distinto de Git 1.9 / default --unstable. Úsalo cuando el orden de file diffs puede cambiar. No lo pongas default del repo.
¿Puedo hashear un archivo .patch con git patch-id file.patch? No hay argv de path. cat file.patch | git patch-id. Un posicional se ignora y, si stdin está vacío, sales 0 en silencio.
¿Sirve para saber si el working tree está dirty? No. Eso es git diff --quiet. patch-id no mira el disco salvo lo que tú le pipes.
Fuentes
- git-patch-id(1) — HTTP 200, last-modified 2026-08-31. Man local Git 2.50.1.428.g0e8243 (2025-07-22).
- git-cherry(1) — HTTP 200, last-modified 2026-08-31. Equivalencia de parche vs SHA.
- git-diff(1) — HTTP 200, last-modified 2026-08-31. Productor del texto que patch-id consume.
- git-diff-tree(1) — HTTP 200, last-modified 2026-08-31. Prefijo de commit +
-p. - GitHub Docs — About Git — HTTP 200, verificado 2026-09-06.
Hub: comparativas y decisiones. Curso: instalar un agente.

Lecturas relacionadas
Sigue explorando Coding Agents y otras piezas para builders.

git commit-graph para coding agents: índice de historia, no gc

git replace para coding agents: refs locales que mienten el SHA, no reescritura

git var para coding agents: una variable lógica, no el config
