docs: home the replay algorithm in REPLAYING.md; message directives live inside the flanks
commit e3554c656162133cc0ca78bc32cebead7064f316 · parent 61ee4d1792
· browse this tree
Ximianth, 2026-10-18 23:17:39 UTC
The replay steps describe what a tool does, not what the format is, so they move out of FORMAT.md into their own hacking doc. The message-no-final-newline declaration moves from the header area into the message block itself: directive lines sit directly after the opening '----', before any content. Directives-then-content mirrors how the header block precedes its content, and makes a strict rule: bare unprefixed lines are legal only in that position, and only if recognized. tip is now optional per the format rewrite; REPLAYING.md says what that means for verification. The tool does not conform to these three points yet; that lands next, on this branch.
diff --git a/docs/hacking/FORMAT.md b/docs/hacking/FORMAT.md index bae83d0..96950bd 100644 --- a/docs/hacking/FORMAT.md +++ b/docs/hacking/FORMAT.md @@ -165,6 +165,12 @@ In detail: (Git headers cannot contain an empty line, so this is unambiguous.) - **Commit message**: - Flanked by literal `----` lines. + - Directive lines may appear directly after the opening `----`, before any message content. + These are bare lines (no prefix); any unrecognized line in this position is an error. + Currently defined: + - `message-no-final-newline` -- declares that the message does not end with a newline character. + (Possible at the git object level, though porcelain tools essentially never produce it. + But lossless means lossless.) - Each line of body between the `----` boundary markers carries a prefix. (This ensures that commit messages containing a line of dashes cannot produce a fuss.) - Two prefix styles are supported: {`> `+line} and {`\t`+line}. diff --git a/docs/hacking/REPLAYING.md b/docs/hacking/REPLAYING.md new file mode 100644 index 0000000..ee5bc68 --- /dev/null +++ b/docs/hacking/REPLAYING.md @@ -0,0 +1,26 @@ +Replaying: rebuilding branches from patch files +=============================================== + +The FORMAT document describes what the files *are*; +this one describes how a tool turns them back into git commits, exactly. +Any implementation following these steps reproduces the stored branch SHA-exact. + +For each patch name in series order, maintaining a map from reference to replayed SHA +(seeded with `BASE` -> the STACK's base): + +1. Parse the patch file. Resolve each `parents` reference through the map; + an unknown reference is an error (series order guarantees parents were replayed first). +2. Read the first parent's tree into a scratch index (no worktree is required, ever). +3. If the diff section is non-empty, apply it to that index + (`git apply --cached --binary --whitespace=nowarn`; the explicit whitespace flag + keeps a target repo's own `apply.whitespace` from rewriting or refusing recorded bytes). + An apply failure is an error: the recorded base is the only supported target. +4. Write the resulting tree. +5. Reassemble the raw commit object: `tree` line, one `parent` line per resolved parent, + then the stored header block verbatim, a blank line, and the message bytes + (dropping the final newline if the `message-no-final-newline` directive was declared). + Hash and store it (`git hash-object -t commit -w`). Record name -> SHA in the map. + +The final SHA is the replayed branch tip. +When the STACK records a `tip`, verification compares against it; +a STACK without one can still be replayed, just not cross-checked.