Guía9 min

git format-patch para coding agents: mailbox, no un diff suelto

Resumen

git format-patch arma un mbox por commit no-merge. -1 SHA = ese commit; SHA solo = todo lo posterior. -o dir o --stdout. Cero --root/--signoff/--cover-letter. Git 2.50.1.

GitHub
Un commit se convierte en mailbox; HEAD no se mueve, aparecen archivos 0001-*.patch

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 format-patch prepara cada commit no-merge como un mensaje tipo mailbox. El man (git-format-patch(1); git-scm.com/docs/git-format-patch HTTP 200, last-modified 2026-08-31, last updated in 2.55.0; pie del man local Git 2.54.0, 2026-04-19; binario Git 2.50.1 / Apple Git-155): Prepare each non-merge commit with its "patch" in one "message" per commit, formatted to resemble a UNIX mailbox. El output es para correo o para git am. SEE ALSO: git-am(1), git-send-email(1).

No es diff. Diff imprime hunks; este comando añade From, Date, Subject y un -- al final. Tampoco apply: apply pega. format-patch escribe archivos (o stdout). HEAD no se mueve. El reverso para aplicar y committear es am.

Contrato: -1 para un SHA, -o o --stdout para no ensuciar el cwd, cero --root / --signoff / --cover-letter autónomo.

Qué hace (y qué no)

Un mensaje tiene tres partes (man): cabecera From <commit> con fecha fija Mon Sep 17 00:00:00 2001 (para que file(1) lo reconozca), cuerpo del log, y el parche separado por ---. Autor y título salen del commit. El SHA del From es el original; am hará un SHA nuevo.

Dos formas de elegir commits (man):

  1. Un solo <since>: commits de la punta actual que no están en la historia de ese since.
  2. Un <revision-range>. Un SHA suelto entra por la regla 1. Para “solo ese commit”: git format-patch -1 <commit>. Para “desde el origen hasta <commit>”: --root.

Verificado 2026-09-04 (Git 2.50.1 / Apple Git-155), repo feat: afeat: change bfeat: change c:

  • Sin argumentos: 0, cero archivos.
  • git format-patch -1: 0, 0001-feat-change-c.patch en el cwd, untracked. Log intacto.
  • --stdout -1: 0, el mbox en stdout, cero archivos.
  • -o dir -1: 0, el archivo vive en dir. El cwd no gana otro .patch.
  • SHA de feat: a sin -1: 0, 0001-feat-change-b.patch y 0002-feat-change-c.patch. No emite a.
  • -1 del mismo SHA: 0, solo 0001-feat-a.patch.
  • git format-patch main en topic (un commit extra): 0, un parche. Equivale a “lo que topic tiene y main no”.
  • main..topic: solo el commit de topic. main...topic (simétrica): los dos lados (feat: g y feat: h).
  • Merge --no-ff en HEAD: -1 salta el merge y emite el no-merge previo (feat: h). -5 no crea archivo del merge.
  • Working tree sucio: 0, igual escribe el parche. El comando no mira el índice.
  • Fuera de un repo: fatal: not a git repository, 128.
  • --signoff: 0, trailer Signed-off-by: t <t@t> (identidad del committer ahora).
  • --cover-letter -2: 0, 0000-cover-letter.patch extra.
  • --rfc: Subject [RFC PATCH] ….
  • -k: Subject sin [PATCH].
  • -n -1: Subject [PATCH 1/1].
  • --numbered-files -2: archivos llamados 1 y 2, sin .patch.
  • --root HEAD: un archivo por commit de la historia (4 en el fixture).
  • --allow-empty reciente: -1 emitió un mailbox corto (8 líneas, sin diff) sin --always.
  • Binario: el parche lleva GIT binary patch / literal.

-1 es un commit; SHA suelto es “todo lo posterior”

Lo que el agente sí / no corre

QuieroComandoTrampa
Un commitgit format-patch -1 -- "$sha"git format-patch "$sha" (rango)
Serie vs maingit format-patch main (estando en topic)--root
Un mbox-1 --stdoutvolcar el mbox al LLM
Archivos en carpeta-o "$dir"cwd lleno de 0001-*.patch
Pegar el parcheapply --checkcreer que format-patch ya aplicó
Aplicar el mbox a commitsamgit apply del mismo archivo
Traer el SHA del grafocherry-pickformat-patch + am “por si acaso”

Prohibido en autónomo:

  • SHA sin -1. El man: un solo commit es <since>, no “ese commit”. Verificado: emite los posteriores.
  • --root. Toda la historia a disco.
  • Sin -o ni --stdout. Ensucia el worktree con untracked 0001-*.patch. Verificado: status ?? 0001-….
  • --signoff / -s. Firma tu identidad. Un agente no pone DCO por Marco.
  • --cover-letter. Inventa 0000-cover-letter.patch para que un humano lo edite.
  • --rfc, -v / --reroll-count, --subject-prefix. Cambian el Subject que am va a committear.
  • --numbered-files. Nombres 1, 2 sin sufijo: fáciles de git add por error.
  • --thread / --attach / --in-reply-to / --to / --cc. Correo, no un agente.
  • --notes. Mete notas al mailbox.
  • --always “por si el empty no sale”. En 2.50.1 el empty ya salió.
  • A...B cuando el ticket es A..B. Verificado: la simétrica mete ambos lados.
  • Tratar el SHA del From: como “el objeto que vas a tener después de am”. am crea SHA nuevo.

El mbox se consume con am. Un agente que reescribe el From: / hunk a mano está un paso más allá de este comando.

Receta (60 segundos)

Solo en un worktree propio. Un SHA o un since explícito:

git status -sb
mkdir -p /tmp/patches
git format-patch -1 -o /tmp/patches -- "$sha"
ls /tmp/patches

Un mbox a stdout (sin archivos en el repo):

git format-patch -1 --stdout -- "$sha" > /tmp/one.mbox

Serie de la rama vs main:

git format-patch -o /tmp/patches -- main

Si el rango es dudoso: para. Cero --root. Cero SHA suelto “a ver qué sale”.

Cierre = entregar el mbox / los .patch, no add de esos archivos al repo. Cero push a main.

--stdout no toca el disco; sin -o el cwd gana untracked

format-patch vs diff vs apply

diff es hunks. format-patch es correo: From, Subject, --- y el parche. apply pega bytes y no committea. am parte el mailbox y committea.

Un agente que “exporta el PR” con git diff main no replica autor ni mensaje. Si el ticket es “arma un mbox para am”, este comando. Si el ticket es “muéstrame el delta”, diff --stat. Si el ticket es “trae el SHA abc”, cherry-pick.

El man (CAVEATS, heredado del flujo am): un diff sin indentar en el mensaje de commit puede aplicarse como parche. No “arreglar” el mbox a ciegas.

format-patch exige un repo. Diff de working tree no.

Checklist

  • Worktree propio. git status -sb. Destino -o o --stdout.
  • Un commit = -1 -- "$sha". Serie vs rama = git format-patch -- main.
  • Cero SHA suelto, cero --root, cero --signoff / --cover-letter / --rfc.
  • No add los .patch al índice.
  • Cierre = mbox fuera del repo. No push a main.

FAQ

¿git format-patch SHA es ese commit? No. Un SHA suelto es <since>. Verificado: emite los posteriores. El commit solo es -1.

¿HEAD se mueve? No. Verificado: el log no cambia; aparecen untracked.

¿Hace falta un repo? Sí. Verificado: 128 fuera de Git.

¿--signoff “por si el proyecto usa DCO”? No. El trailer es la identidad del proceso. Nunca en autónomo.

El curso instalar un agente cubre el loop local. Hub: comparativas y decisiones. format-patch no es apply: escribe el mailbox, no pega el árbol. El reverso es am.