libra merge
Command reference for `libra merge`
Merge one target into the current branch.
Synopsis
libra merge [--ff-only | --no-ff | --squash | --no-commit] [-m <msg>] [--no-edit] [--stat | -n | --no-stat] [--verify-signatures | --no-verify-signatures] [--no-rerere-autoupdate] [--no-gpg-sign] [--dry-run] [--autostash | --no-autostash] <branch>
libra merge --continue
libra merge --abort
libra merge --restartDescription
libra merge <branch> resolves a local branch, commit hash, or remote-tracking ref such as refs/remotes/origin/main.
If the current branch can be fast-forwarded, Libra moves the branch pointer to the target commit and restores the index and working tree. If the branches have diverged, Libra performs a single-head three-way merge using the merge base.
Clean three-way merges create a two-parent merge commit, update HEAD, rebuild the index, restore the working tree, and write a merge reflog entry. Conflicting three-way merges write line-level conflict markers to the working tree (matching Git — only the diverging hunks are enclosed between <<<<<<< HEAD / ======= / >>>>>>>, with shared context left outside; binary or modify/delete paths fall back to whole-file markers), write unmerged index stages, save Libra merge state, and return LBR-CONFLICT-002 with hints for libra merge --continue and libra merge --abort.
Conflict style (merge.conflictStyle)
The marker format follows the Git-compatible merge.conflictStyle config key (config-only — matching Git, merge has no CLI style flag):
libra config merge.conflictStyle diff3merge(default, or unset) — the two-marker style above.diff3— additionally emits the common-ancestor content between a||||||| basemarker and the=======separator, so you can see what both sides started from.- Any other value — including the unimplemented
zdiff3— is a hard error when a conflict must be rendered (exit 128), never a silent fall-back to the default style.
The config is honored by both libra merge and libra cherry-pick for line-level text conflicts. Binary and modify/delete conflicts keep their two-part whole-file presentation (Git also emits no base block there), and libra rebase currently renders whole-file markers without a base block regardless of this setting.
Libra still does not implement octopus merges, custom strategies, strategy options, or interactive message editing (--edit/launching an editor). Signature verification (--verify-signatures) is supported but limited to the local vault PGP key (no external GPG keyring).
Options
| Option | Description |
|---|---|
<branch> | Target branch, commit, or remote-tracking ref to merge. |
-m, --message <MSG> | Override the merge commit message (default Merge <branch> into <head>). |
--ff-only | Refuse to merge unless the current branch can be fast-forwarded. |
--no-ff | Always create a two-parent merge commit, even when a fast-forward is possible. |
--squash | Produce the merged index/working tree but create no commit and do not move HEAD; finish with a plain libra commit. |
--no-commit | Perform the merge and stage the result but stop before committing; finish with libra merge --continue. |
--no-edit | Accept the auto-generated merge message without launching an editor. Libra never opens an editor for merge, so this is a no-op accepted for Git parity. |
--stat | Show a diffstat of the merge result (the changes between the pre-merge HEAD and the new commit) after the merge completes. Git shows this by default; Libra defaults to no diffstat, so --stat opts in. Last-one-wins toggle with --no-stat/-n. Human output only. |
-n, --no-stat | Do not show a diffstat at the end of the merge (Libra's default). Last-one-wins toggle with --stat. |
--no-progress | Do not show a progress meter. No-op accepted for Git parity: Libra's merge never renders a progress meter. |
--verify-signatures | Verify the PGP signature on the tip commit of the merged branch and abort the merge if it is unsigned or the signature is bad. Like tag -v, only signatures made by this repository's vault PGP key can be validated (no external GPG keyring), so a commit signed elsewhere — or with an SSH signature — is treated as not verifiable. |
--no-verify-signatures | Do not verify the merged commit's signature (the default). The inverse of --verify-signatures; the last one wins. |
--no-rerere-autoupdate | Do not update the rerere index after the merge. No-op accepted for Git parity: Libra has no rerere. (Git's --rerere-autoupdate is not exposed.) |
--no-gpg-sign | Do not GPG-sign the merge commit. No-op accepted for Git parity: Libra's merge never signs. (Git's -S/--gpg-sign is not implemented.) |
--continue | Finish an in-progress merge after conflicts have been resolved and staged. |
--abort | Restore the pre-merge HEAD, index, and working tree (re-applies a held autostash). |
--autostash / --no-autostash | Stash local tracked changes (incl. staged) before the merge and re-apply them when it concludes — held (outside stash list) across a conflict until --continue/--abort; a conflicting re-apply is saved to the stash list with a notice, never lost. Config: merge.autostash (boolean; invalid value = hard error). Untracked files are not stashed (Git parity). --json adds autostash: applied|stashed|kept. |
--dry-run | Libra extension: preview the merge outcome writing nothing — reports fast-forward / already-up-to-date / clean three-way / would-conflict (with the paths). Exits 0 for a clean preview, 1 when the merge would conflict. Mutually exclusive with --continue/--abort/--restart/--squash/--no-commit. |
--restart | Libra extension (ports Lore's branch merge restart): abort the in-progress conflicted merge — discarding any resolution work, exactly like --abort — then immediately re-run the same merge against the recorded target commit, regenerating fresh conflict markers and state. Takes no branch and no merge options (the original -m/--no-ff/… are not replayed). Requires a conflicted merge: a staged --no-commit merge is refused (finish it with --continue or discard with --abort). |
--json | Emit a structured success envelope. |
--machine | Emit the same structured envelope as one compact JSON line. |
--quiet | Suppress human success output. |
Common Commands
libra merge feature-x
libra merge refs/remotes/origin/main
libra merge --continue
libra merge --abort
libra merge --dry-run feature-x
libra merge --restart
libra merge --json feature-xConflict Lifecycle
When a merge conflicts:
- Edit files containing conflict markers.
- Stage each resolved path with
libra add <path>. - Run
libra merge --continueto create the two-parent merge commit.
Run libra merge --abort before continuing to restore the branch, index, and working tree to the pre-merge commit. libra status shows the in-progress merge target and the continue/abort commands while merge state exists.
To throw away a botched resolution attempt and start over in one step, run libra merge --restart: it restores the pre-merge state exactly like --abort (any edits to conflicted files are discarded) and immediately re-runs the same merge against the recorded target commit — deterministic even if the branch has moved since — leaving fresh conflict markers and a fresh merge state. The re-run uses default merge options.
Dry Run
libra merge --dry-run <branch> (a Libra extension — Git has no true merge dry-run) reports what the merge would do without writing anything: no HEAD, index, working-tree, reflog, merge-state, or object-store mutation (auto-merged blobs are computed in memory only). Because it is read-only it also works on a dirty working tree — note the preview does not validate cleanliness, so a real merge may still refuse where the preview succeeded.
Outcomes and exit codes:
| Preview outcome | Human output | Exit |
|---|---|---|
| Fast-forward possible | Would fast-forward | 0 |
| Already up to date | Already up to date. | 0 |
| Clean three-way merge | Would merge cleanly by the 'three-way' strategy. | 0 |
| Would conflict | Would conflict in: <paths> | 1 |
The would-conflict exit of 1 is an outcome signal (like merge-file and diff --exit-code), deliberately distinct from the 128 a real conflicting merge exits with — the preview itself succeeded. With --json/--machine the summary carries "dry_run": true and, when conflicting, "would_conflict": true plus conflicted_paths; both keys are absent from every real merge's output (frozen schema).
Human Output
Fast-forward:
Fast-forwardClean three-way merge:
Merge made by the 'three-way' strategy.Already up to date:
Already up to date.After --continue:
Merge completed.After --abort:
Merge aborted.Conflict errors are printed through Libra's standard structured error envelope on stderr and include recovery hints.
JSON / Machine Output
Success output keeps the historical files_changed numeric field and adds merge-lifecycle fields only when relevant.
{
"ok": true,
"command": "merge",
"data": {
"strategy": "three-way",
"old_commit": "abc1234...",
"commit": "def5678...",
"files_changed": 2,
"up_to_date": false,
"parents": ["abc1234...", "fedcba9..."]
}
}Already-up-to-date merges use strategy: "already-up-to-date", commit: null, files_changed: 0, and up_to_date: true.
--abort sets aborted: true; --continue sets continued: true. Conflict failures return an error envelope on stderr with LBR-CONFLICT-002.
Parameter Comparison: Libra vs Git vs jj
| Parameter | Libra | Git | jj |
|---|---|---|---|
| Branch target | <branch> (single target) | <commit>... (one or more) | N/A (use jj new) |
| Fast-forward | Supported | Supported | N/A |
| Single-head three-way | Supported | Supported | N/A |
| Continue / abort | --continue, --abort | --continue, --abort | N/A |
| Octopus merge | Not supported | Supported | N/A |
| Fast-forward only | --ff-only | --ff-only | N/A |
| Force merge commit | --no-ff | --no-ff | N/A |
| Squash | --squash | --squash | N/A |
| No-commit | --no-commit | --no-commit | N/A |
| Commit message | -m <msg> | -m <msg> | N/A |
| No editor | --no-edit (no-op; never edits) | --no-edit | N/A |
| Post-merge diffstat | --stat (prints it); -n / --no-stat (default: omit) | --stat (default) / -n / --no-stat | N/A |
| No progress meter | --no-progress (no-op; never renders one) | --no-progress | N/A |
| Disable signature verification | --no-verify-signatures (default; disables --verify-signatures) | --no-verify-signatures | N/A |
| No rerere autoupdate | --no-rerere-autoupdate (no-op; no rerere) | --no-rerere-autoupdate | N/A |
| No GPG sign | --no-gpg-sign (no-op; never signs) | --no-gpg-sign | N/A |
| Custom strategy | Not supported | --strategy, -X | N/A |
| Verify signatures | --verify-signatures (vault-key PGP only) | --verify-signatures | N/A |
| JSON output | --json / --machine | Not supported | N/A |
Error Handling
| Scenario | StableErrorCode | Exit |
|---|---|---|
| Missing branch / action | LBR-CLI-001 | 129 |
| Target ref cannot be resolved | LBR-CLI-003 | 129 |
| Failed to load merge target/current commit/tree | LBR-REPO-002 | 128 |
| Unrelated histories | LBR-REPO-003 | 128 |
--verify-signatures: tip unsigned, signature invalid, or vault unavailable | LBR-REPO-003 | 128 |
| Merge conflicts | LBR-CONFLICT-002 | 128 |
| Dirty worktree or staged changes | LBR-CONFLICT-002 | 128 |
| Untracked file would be overwritten | LBR-CONFLICT-002 | 128 |
| Merge already in progress | LBR-CONFLICT-002 | 128 |
No merge in progress for --continue / --abort | LBR-REPO-003 | 128 |
Unsupported merge.conflictStyle value (e.g. zdiff3) when rendering a conflict | LBR-REPO-003 | 128 |
Unresolved conflict stages remain for --continue | LBR-CONFLICT-002 | 128 |
| Failed to read merge state or index | LBR-IO-001 | 128 |
| Failed to save state, index, tree, commit, HEAD, or worktree | LBR-IO-002 | 128 |