Guía9 min

git interpret-trailers para coding agents: parse, no reescribir el mensaje

Resumen

git interpret-trailers lee o añade trailers tipo RFC 822 al final del mensaje. --parse es el contrato de lectura. Cero --in-place autónomo, cero trailer.*.cmd y cero dump del parche. Distinto de commit --trailer, format-patch y am. Git 2.50.1.

GitHub
Un coding agent extrae Signed-off-by con --parse; el mensaje original no se reescribe

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 interpret-trailers añade o parsea trailers al final de un mensaje de commit. El man (git-interpret-trailers(1); git-scm.com/docs/git-interpret-trailers 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): Add or parse structured information in commit messages. DESCRIPTION: líneas que look similar to RFC 822 e-mail headers al final de la parte libre. SYNOPSIS: git interpret-trailers [--in-place] [--trim-empty] [(--trailer (<key>|<key-alias>)[(=|:)<value>])...] [--parse] [<file>...].

Contrato para un coding agent: lee con --parse. Es stdout. No toca el índice, ni HEAD, ni el working tree. Ante un mensaje, extrae Signed-off-by / Reviewed-by / Co-authored-by y para. No --in-place. No trailer.*.cmd. No vuelques el parche que viene después de ---. No reescribas el cuerpo.

No es commit: git commit --trailer crea un commit y usa esta herramienta por debajo. Aquí no hay objeto nuevo. No es format-patch: format-patch emite mbox; interpret-trailers puede operar sobre ese mbox sin tocar el diff. No es am: am aplica el mbox. Aquí el parche sale igual, salvo que pidas --no-divider.

Qué hace (y qué no)

Lee de <file>... o de stdin si no hay archivo. Sin --parse, aplica trailer.* del config y los --trailer de la línea de comando; el resultado va a stdout. Con --parse (alias de --only-trailers --only-input --unfold) ignora config y flags de escritura: solo imprime los trailers del input, unfolded.

Verificado 2026-09-06:

InputComandoSalidaExit
subject + body, sin trailers--parsevacío0
body + Signed-off-by: Alice <[email protected]>--parseesa línea0
body, sin trailers--trailer 'Reviewed-by: Bob <[email protected]>'mensaje + blank + trailer key: value0
SOB de Alice--trailer el mismo parel original, sin duplicar0
SOB de Alice--if-exists replace --trailer 'Signed-off-by: Bob <…>'Bob sustituye0
SOB de Alice--if-exists doNothing --trailer 'Signed-off-by: Bob <…>'Alice intacta0
SOB de Alice--if-exists addIfDifferent --trailer BobAlice y Bob0
Fixes: y Cc: vacíos--trim-emptysubject solo0
mensaje + --- + diff --git--trailer 'Reviewed-by: Bob'trailer antes del divider; el diff intacto0
Co-authored-by: Alice <[email protected]>--parseesa línea0
valor folded en dos líneas--parseuna sola línea0
stdin--in-placefatal: no input file given for in-place editing128
path inexistente--trailer 'Foo: bar' /no/such/filecould not read input file128

El default de --if-exists es addIfDifferentNeighbor (man + git-config): no duplica el mismo (key, value) al lado. Por eso el mismo SOB de Alice no se reescribe. replace borra el trailer más cercano con esa key. doNothing no añade si la key ya existe, aunque el value sea otro.

--in-place exige archivo. Stdin → 128. No hay “editar el commit actual”: esto no mueve HEAD. Si el humano pide un trailer en un commit ya hecho, el camino es un commit nuevo o git commit --trailer antes de crear el objeto — no --in-place sobre .git/COMMIT_EDITMSG a ciegas.

Cómo extrae el bloque

El man: un grupo de una o más líneas que (i) son todas trailers, o (ii) contienen al menos un trailer generado por Git o configurado por el usuario y al menos 25 % trailers. El grupo va precedido de una o más líneas vacías. Está al final del input o es lo último no-blanco antes de una línea que empieza por --- (espacio o fin de línea).

Eso importa con format-patch: el mbox trae mensaje, divider --- y parche. Sin --no-divider, el comando no modifica divider ni patch. Verificado: el diff --git sale igual; el trailer nuevo se inserta en el mensaje.

Dentro de un trailer no hay whitespace en la <key>. Entre key y separador sí. El valor puede plegarse (línea siguiente con whitespace, estilo RFC 822). --unfold / --parse lo aplastan a una línea. No asumas encoding RFC 822: el man dice explícitamente que no siguen esas reglas.

Separador por defecto: : (un colon y un espacio) en la salida. En --trailer acepta = o : por compatibilidad. trailer.separators puede ampliar el set (:# para Fix #42); no lo toques en --global.

Config que un agente no debe escribir

trailer.<keyAlias>.key acorta --trailer sign: Alice a Signed-off-by. El alias es prefijo de la key (case-insensitive). trailer.where / --where: end (default), start, after, before. trailer.ifexists / --if-exists: addIfDifferentNeighbor (default), addIfDifferent, add, replace, doNothing. trailer.ifmissing / --if-missing: add (default) o doNothing.

trailer.<keyAlias>.cmd (y el deprecado .command) lanza un shell. El man: se llama una vez para auto-añadir el trailer y otra por cada --trailer <alias>=<value>, con el value como primer argumento. Ejemplos oficiales corren git log y scripts en ~/bin. Un coding agent no configura .cmd. No es un parser: es RCE con disfraz de trailer.

Tampoco commit.template con hook commit-msg que reescribe $1 vía --in-place o mv sin que el humano lo pida. El ejemplo del man existe; no lo copies a un repo ajeno.

La lectura segura no necesita config: --parse no aplica trailer.*. Verificado: --only-trailers --only-input --unfold equivale a --parse y sale 0 con el SOB de Alice.

Co-authored-by no es un verbo de Git

GitHub Docs Creating a commit with multiple authors (HTTP 200 el 2026-09-06; URL canónica /en/pull-requests/how-tos/commit-changes/creating-a-commit-with-multiple-authors tras 301 desde la ruta vieja): Add one or more Co-authored-by trailers to a commit message. Title: Creating a commit with multiple authors or on behalf of an organization. El trailer es convención de producto. Git lo trata como cualquier otra key. --parse lo extrae; no valida el email noreply ni cuenta contribuciones.

Si el humano pide co-autor, imprime el trailer canónico Co-authored-by: Name <email> y que lo pegue. No inventes el users.noreply.github.com. No mezcles Signed-off-by (DCO / -s de commit) con co-autoría.

git commit --trailer (man git-commit(1), git-scm.com/docs/git-commit HTTP 200, last-modified 2026-08-31) es el porcelain: añade el trailer al crear el commit y respeta trailer.*. interpret-trailers es el filtro. Un agente autónomo no corre ninguno de los dos contra un mensaje vivo.

Checklist del agente

  1. Diagnóstico: git interpret-trailers --parse sobre el mensaje (stdin o archivo). Exit 0 aunque no haya trailers.
  2. ¿Hay que añadir? Primero stdout sin --in-place. Diff visual para el humano. Luego, si pide persistir un archivo, --in-place sobre ese path.
  3. Duplicados: deja el default. replace solo si el humano pide sustituir la key.
  4. Mbox de format-patch: no uses --no-divider salvo que el input sea solo el mensaje.
  5. Cero --in-place sobre stdin (128). Cero path inventado (128). Cero trailer.*.cmd. Cero --global en config.
  6. No vuelques el parche al contexto: --only-trailers o --parse.
  7. --trim-empty borra trailers cuyo value es solo whitespace. Útil en templates. No lo corras “por si acaso” sobre un mensaje con Fixes: 123 real.

El agente imprime solo las líneas key: value; el diff queda fuera

Errores que importan

--in-place sin archivo: fatal: no input file given for in-place editing, exit 128. Archivo ausente: fatal: could not read input file '…': No such file or directory, 128. Uso válido: 0, incluso cuando --parse no imprime nada.

No hay “tool-help” ni TTY. No abre GUI. No es interactivo. El riesgo no es FileMerge: es reescribir el mensaje o ejecutar .cmd.

git interpret-trailers -h lista --[no-]in-place, --trim-empty, --where, --if-exists, --if-missing, --only-trailers, --only-input, --unfold, --parse, --no-divider / --divider, --trailer. Verificado 2026-09-06, exit 0.

FAQ

¿Puedo usarlo para firmar un commit ya publicado? No. No mueve refs. El objeto commit es inmutable. Un trailer nuevo implica un commit nuevo (o un --amend que un agente no hace).

¿--parse respeta trailer.sign.key? No. El man: without influencing them with any command line options or configuration variables.

¿Es lo mismo que git commit -s? No. -s añade Signed-off-by del committer al crear. Esto filtra texto.

¿Puedo parsear git log -1 --format=%B? Sí, por stdin. No pases -p. El cuerpo basta.

¿Y un valor multilínea? --parse lo unfold. Verificado con el ejemplo del man (key: + continuación indentada) → una línea, exit 0.

Cuándo no usarlo

No lo uses para inventar DCO, para “arreglar” autoría ni para inyectar Reviewed-by en un PR ajeno. No lo uses como pretty-printer del parche. Si solo necesitas el SHA o el subject, commit ya cubre el porcelain y git log -1 --format=%s basta.

Siguiente lectura: el hub Comparativas y decisiones y el curso de instalar un agente.

Cadena mensaje → parse → trailers; el parche no entra al modelo