libra am
Command reference for `libra am`
Apply one or more plain-text format-patch mail files as commits. The files
are processed in the order given, preserving each mail's subject/body, author,
and Date: metadata while using the current Libra identity as committer.
Synopsis
libra am [-3] <patch|mbox|-> ...
libra am --continue
libra am --skip
libra am --abort
Behavior
A new series requires a local branch with an existing commit and no staged or tracked working-tree changes. Unrelated untracked files are preserved, but any existing non-index path that a mail would touch—including an ignored path—is rejected before sequencer state is saved. The aggregate mail input is limited to 64 MiB and 10,000 mails.
Each input may be a single mail or an mbox: a file (or stdin) whose first
line is an mbox From envelope line is split into its messages and
applied in order. The envelope test is ctime-shaped (an hh:mm:ss time
token followed by a 4-digit year, trailing timezone tokens allowed), so
commit-message prose like From my reading of RFC 9110 never splits a
mail, and body content — including >From quoting — is preserved
byte-for-byte like git's default (mboxo) reading. - reads one mail
or mbox from standard input (allowed at most once); the full mail content
is persisted in the sequencer state, so --continue / --skip /
--abort work for stdin-sourced series exactly like file-sourced ones.
Multi-message sources are position-labelled <source>#<n> in output and
state.
Beyond plain text diffs, mails may carry git extended sections: GIT binary patch payloads (both literal and delta, decoded from base85 +
zlib with bounded inflation), rename from/rename to (with or without
content hunks — the source deletion and the destination are staged in the
same commit), copy from/copy to, and mode-only old mode/new mode
changes (the executable bit is applied to the worktree and staged
directly). Every extended target passes the same path-safety checks.
The mail parser accepts UTF-8 messages with 7bit, 8bit, binary,
quoted-printable, or base64 transfer encoding — single-part text/plain,
or MIME multipart containers (multipart/mixed/alternative, nested up
to a bounded depth): parts are split on the declared boundary, every
supported text part (text/plain, text/x-patch, text/x-diff) is
decoded with its own transfer encoding and concatenated in order, HTML
alternatives and binary attachments are skipped, and a multipart mail
with no supported text part fails closed. format-patch --attach output
therefore applies directly. It reads From:,
Date:, and Subject:, removes a leading [PATCH ...] subject marker, honors
the standard in-body From: override, and extracts the text diff --git
section after the --- separator. UTF-8/US-ASCII RFC 2047 B and Q encoded
words are decoded.
Every target is checked against absolute paths, empty/./.. components, NUL
bytes, .libra/, and existing symlink path components. All files in one mail are test-applied before
the first write. File replacements use atomic rename and content patches retain
the existing permission bits.
Sequencer state is saved before worktree writes. Each successful commit moves
the branch, writes its reflog, and advances or clears the am position in one
SQLite transaction. Resume and skip reject a branch whose tip moved outside
the sequencer. If interruption occurs after state is saved but before the
current mail writes anything—including between two commits—--continue
retries that mail. --abort resets the original branch tip, index, and tracked
worktree, and also removes a new-file target left by an interruption before it
was staged.
Hooks
The applypatch hook family runs from .libra/hooks through the
sandboxed repository-hook runner (same contract as the commit hooks;
LIBRA_NO_HOOKS=1 bypasses):
applypatch-msg <msg-file>— before any worktree write. The proposed commit message is written to the worktree'sCOMMIT_EDITMSG(the one writable hook file); the hook may edit it in place, and a non-zero exit refuses the mail with the series left resumable.pre-applypatch— after the worktree write and staging, before the commit; a non-zero exit pauses the series with the changes staged. It also gates the resolved--continuecommit (Git's--resolvedsemantics);applypatch-msgdoes not re-run there.post-applypatch— after the commit; advisory (failures warn, never fail the applied mail).
Conflict recovery
With -3/--3way, a text patch that does not apply falls back to a
three-way merge: the base is the index header's old blob resolved from
the local object store (abbreviated ids resolve only when unambiguous),
theirs is the patch applied to that base, ours is the current content. A
clean merge applies silently; a conflicting one writes <<<<<<< markers
into the worktree and pauses the series (resolve, stage, --continue).
A base that is not locally present keeps the plain refusal — the
fallback never fabricates content. The -3 choice persists in the saved
series state, so resumes keep the same semantics.
Without -3, when a patch does not apply the command leaves the current
branch tip unchanged and keeps the series resumable:
- resolve the affected paths manually;
- stage only paths named by the current patch with
libra add; - run
libra am --continue.
Use --skip to discard the current patch and continue with the next one, or
--abort to discard the entire series and restore the pre-am state.
Options
| Option | Meaning |
|---|---|
--continue |
Commit the fully staged resolution and continue. Unstaged current-patch paths, unrelated tracked changes, unresolved index entries, an empty resolution, or staged unrelated paths are rejected. A pristine recovery state retries the current mail. |
--skip |
Reset the current patch and continue with the remaining mails. |
--abort |
Restore the original branch tip, index, and tracked worktree and clear the sequencer. |
--json / --machine |
Emit the action, applied source files/subjects/commit IDs, and optional restored HEAD in the standard envelope. |
Examples
# Generate and replay a series
libra format-patch -o outgoing origin/main..HEAD
libra switch target
libra am outgoing/0001-*.patch outgoing/0002-*.patch
# Pipe a whole series as one mbox
libra format-patch --stdout origin/main..HEAD | libra am -
# Resolve a stopped patch
$EDITOR src/lib.rs
libra add src/lib.rs
libra am --continue
# Cancel the complete series
libra am --abort
Current limitations
This is not yet full Git am parity. It does not expose Git's wider
flag set (--signoff, --keep, --scissors, and others). The shared parser is also
available as the standalone libra mailinfo command.