Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ batch document.
| merge | partial | fast-forward and single-head three-way merge supported; `-m <msg>`, `--ff-only`, `--no-ff`, `--squash`, `--no-commit`, `--no-edit` (accepted no-op; Libra never opens an editor for merge), `--stat`/`-n`/`--no-stat` (last-wins toggle; `--stat` prints a post-merge diffstat of the merge's changes, the default is no diffstat), `--no-progress` (accepted no-op; Libra's merge renders no progress meter), `--verify-signatures` (verify the merged tip's PGP signature against the local vault key — like `tag -v`, only signatures made by this repository's vault key can be validated; no external keyring) / `--no-verify-signatures` (the default; toggle pair, last wins), `--no-rerere-autoupdate` (accepted no-op — never auto-stages replayed resolutions; rerere IS auto-integrated when `rerere.enabled` is set, with staging following `rerere.autoUpdate` since merge does not expose the positive `--rerere-autoupdate`), and `--no-gpg-sign` (accepted no-op; Libra's merge never signs the merge commit) supported; the `merge.conflictStyle` config (`merge` default / `diff3` adds the `||||||| base` ancestor block to line-level text conflicts; honored by merge and cherry-pick; an unsupported value such as `zdiff3` is a hard error when a conflict must be rendered, exit 128) supported; Libra extensions: `--dry-run` (preview the outcome writing nothing — no HEAD/index/worktree/state/object write; exit 0 clean, exit 1 would-conflict with `would_conflict`+`conflicted_paths` in `--json`) and `--restart` (abort the in-progress conflicted merge — discarding resolution work like `--abort` — then re-run the same merge against the recorded target commit; original merge options not replayed); octopus/custom strategies, `--rerere-autoupdate`, and `-S`/`--gpg-sign` (signing the merge commit) deferred; `--autostash`/`--no-autostash` + `merge.autostash` config (lore.md 1.8, Git-faithful): a dirty tree (tracked changes incl. staged) is stashed into a HELD commit before the merge (not in `stash list` — the MERGE_AUTOSTASH model, sidecar `merge-autostash.json`), re-applied when the merge concludes (clean success, up-to-date, squash, start failure, `--continue`, `--abort`); a conflicted merge HOLDS it (also across `--restart` cycles); a conflicting re-apply promotes it into the stash list with a notice — changes are never lost; invalid `merge.autostash` is a hard error; JSON gains an additive `autostash: applied|stashed|kept` field lore.md 2.6: participates in the symmetric cross-op sequencer mutex — starting a `merge` is refused (`LBR-CONFLICT-002`) while any OTHER sequence is unresolved (and vice-versa). |
| merge-base | partial | Prints the best common ancestor of two commits — a true LCA (a common ancestor that is not a strict ancestor of another), shared with `diff A...B` via `internal/merge_base.rs`; `--all` prints every lowest common ancestor, `--is-ancestor` tests ancestry (exit 0/1), `--json`/`--machine` supported. Exit 0 (base found / ancestry holds) / 1 (no common ancestor / not an ancestor; no output, matching Git) / 128 (unresolvable commit or wrong arg count). More than two commits, `--octopus`/`--independent`/`--fork-point` not exposed. `rebase` now computes its merge base through this shared LCA (no longer a first-found walk), and `log A...B` excludes the reachable-set intersection (correct for multiple merge bases) — the consolidation is complete |
| merge-file | partial | File-level three-way merge of `<current> <base> <other>` reusing the same `diffy` merge as `merge` (markers labelled `ours`/`theirs`, `||||||| original` with `--diff3`); `-p`/`--stdout`, `--diff3`, `-q`/`--quiet`, `--json`/`--machine` supported; works outside a repository. Exit 0 (clean) / 1 (conflict, fixed at 1) / 128 (missing/unreadable/binary input). In-place writes back up the original under `.libra/merge-file-backup/` (kept on conflict). `-L <label>`, `--ours`/`--theirs`/`--union`, and `--marker-size` not exposed (deferred) |
| reset | partial | `--soft`/`--mixed`/`--hard` and pathspec un-staging supported, with index-rollback on failure. `--pathspec-from-file`/`--pathspec-file-nul` supported for bulk/stdin pathspec input, but paths are taken literally — Git's default-mode C-style quoted-path decoding is intentionally not performed (use `--pathspec-file-nul` for special characters). `--no-refresh` is accepted as a no-op (Libra's reset never refreshes the index, so there is no refresh to skip; no `--refresh`). `--merge`/`--keep` remain unsupported (see [docs/commands/reset.md](docs/commands/reset.md) "Why no --merge/--keep?") |
| reset | partial | `--soft`/`--mixed`/`--hard` and pathspec un-staging supported, with index-rollback on failure. Bare pathspecs follow Git disambiguation: `libra reset <path>` unstages from `HEAD`, `libra reset -- <path>` forces path interpretation for revision-like filenames, and a token that is both revision and filename is rejected as ambiguous (`LBR-CLI-002`). `--pathspec-from-file`/`--pathspec-file-nul` supported for bulk/stdin pathspec input, but paths are taken literally — Git's default-mode C-style quoted-path decoding is intentionally not performed (use `--pathspec-file-nul` for special characters). `--no-refresh` is accepted as a no-op (Libra's reset never refreshes the index, so there is no refresh to skip; no `--refresh`). `--merge`/`--keep` remain unsupported (see [docs/commands/reset.md](docs/commands/reset.md) "Why no --merge/--keep?") |
| rev-parse | partial | basic revision parsing, `--verify`, `--short[=<n>]`, `--abbrev-ref`, `--symbolic-full-name` (resolve a spec to its full ref name — `refs/heads/…`/`refs/tags/…`/`refs/remotes/…`, or `HEAD` when detached; a valid non-ref object prints nothing, an unresolvable name exits 128 — Libra reports it on stderr rather than echoing the spec to stdout), `--symbolic` (echo a resolvable ref/revision/object-id spec verbatim — `main` stays `main`, not `refs/heads/main`; unresolvable names exit 128 like `--symbolic-full-name`), `--show-toplevel`, `--show-prefix`, `--show-cdup`, `--is-inside-work-tree`, `--is-inside-git-dir`, `--is-bare-repository`, `--git-dir`, `--absolute-git-dir`, `--sq` (shell-quote the resolved object name), the output-filter modes `--flags`/`--no-flags`/`--revs-only`/`--no-revs` (classify each arg as flag/revision/path and print a filtered subset), and multiple `<SPEC>` arguments (each resolved on its own line) supported; a `--` separator splits revisions from paths in all of these. **Intentional divergence:** combining `--verify`/`--short` (single-revision modes) with any output-filter flag is rejected with a usage error (LBR-CLI-002 / exit 129) — Git's behavior in that corner is ill-defined. Remaining output-filter (`--abbrev=<n>`) and parseopt modes are incomplete |
| rev-list | partial | multi-revision reachability, `^` exclusions, `A..B`/`A...B` ranges, `--count`, `-n`/`--max-count`, `--skip`, `--since`/`--after`, `--until`/`--before`, parent-count filters and reset aliases, `--first-parent`, `--author`, `--committer`, `--grep`, path limitation after `--`, symmetric side filters (`--left-right`, `--left-only`, `--right-only`), cherry filters (`--cherry`, `--cherry-pick`, `--cherry-mark`), `--parents`, `--children`, `--timestamp`, and `--reverse` (limit-then-reverse output ordering), `--all` (seed the walk from every branch/remote/tag ref and HEAD), `--date-order` (no-op for Libra's default committer-date ordering; unlike Git, no topo constraint under date skew), and `--boundary` (append the frontier commits — parents of listed commits that are not themselves listed, whether excluded by a range or beyond a `--max-count`/`--skip` cut — each prefixed with `-` and carrying `--parents`/`--children`/`--timestamp` metadata, with Git-faithful merge handling under `--first-parent --parents` and `--children`, leading under `--reverse`, and counted by `--count`), and object-enumeration output (`--objects`, plus `--objects-edge`/`--objects-edge-aggressive` which also emit `-`-prefixed edge commits — the latter an alias, a documented narrowing) supported — objects are the deduplicated reachable trees/blobs printed after the commits as `<oid> <path>` (root tree empty path), byte-for-byte matching `git rev-list --objects`, with excluded-side objects treated as uninteresting (range/`^` closure), `-- <pathspec>` walk pruning, gitlinks skipped, and a hard error on a corrupt included tree |
| describe | partial | basic describe, `--tags`, `--always`, `--abbrev`, `--exact-match`, `--long`, `--dirty[=<mark>]`, `--first-parent`, `--match`/`--exclude` (wax globs, ≤256 chars; exclude wins over match), `--candidates <n>` (n=0 ⇒ exact-match), `--all` (use any ref — branches/remotes/tags — with `heads/`/`remotes/`/`tags/` prefixes), and `--contains` (git name-rev: name a commit relative to the nearest descendant tag — `<tag>`, `<tag>~<n>`, or `<tag>~<n>^<m>~<k>`) supported |
Expand Down
21 changes: 15 additions & 6 deletions docs/commands/reset.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Move `HEAD` and reset the index or working tree depending on the selected mode.

```
libra reset [<target>] [--soft | --mixed | --hard]
libra reset <pathspec>...
libra reset [<target>] [--] <pathspec>...
libra reset [<target>] --pathspec-from-file=<file> [--pathspec-file-nul]
```
Expand All @@ -18,7 +19,7 @@ libra reset [<target>] --pathspec-from-file=<file> [--pathspec-file-nul]
- **`--mixed`** (default): moves HEAD and resets the index. The working tree is untouched, so changes appear as unstaged modifications. Useful for un-staging files.
- **`--hard`**: moves HEAD, resets the index, and restores the working tree. All uncommitted changes are discarded. Useful for fully reverting to a known state.

When pathspecs are provided, the command performs a targeted mixed reset: only the named files are reset in the index to match the target commit, without moving HEAD. This is the primary way to un-stage specific files. Pathspecs are incompatible with `--soft` and `--hard`.
When pathspecs are provided, the command performs a targeted mixed reset: only the named files are reset in the index to match the target commit, without moving HEAD. This is the primary way to un-stage specific files. Like Git, a bare first positional that is a known path and not a revision is treated as a pathspec with target `HEAD`, so `libra reset src/lib.rs` is equivalent to `libra reset HEAD -- src/lib.rs`. If a token is both a revision and a filename, reset refuses it as ambiguous; use `libra reset <revision> -- <file>` for a target revision or `libra reset -- <file>` for a path. Pathspecs are incompatible with `--soft` and `--hard`.

The default target is `HEAD`, making `libra reset` (with no arguments) equivalent to un-staging everything.

Expand All @@ -30,7 +31,7 @@ The default target is `HEAD`, making `libra reset` (with no arguments) equivalen
| | `--soft` | | Move HEAD only; keep index and working tree |
| | `--mixed` | | Move HEAD and reset index; keep working tree (default) |
| | `--hard` | | Move HEAD, reset index, and restore working tree |
| | `<pathspec>...` | positional (after `--`) | Specific files to reset in the index |
| | `<pathspec>...` | positional, optionally after `--` | Specific files to reset in the index |
| | `--pathspec-from-file` | `<file>` | Read pathspecs from a file (`-` for stdin) instead of the command line. Mutually exclusive with command-line pathspecs |
| | `--pathspec-file-nul` | | Treat `--pathspec-from-file` input as NUL-separated rather than line-separated. No-op without `--pathspec-from-file` |
| | `--no-refresh` | | Accepted for Git compatibility; a no-op in Libra (see below) |
Expand Down Expand Up @@ -63,10 +64,16 @@ libra reset HEAD~2
libra reset --hard main

# Un-stage a specific file
libra reset src/lib.rs

# Un-stage a revision-like filename
libra reset -- HEAD

# Un-stage a specific file from an explicit target
libra reset HEAD -- src/lib.rs

# Un-stage multiple files
libra reset HEAD -- src/main.rs src/cli.rs
libra reset src/main.rs src/cli.rs

# Reset specific files to a prior commit
libra reset abc1234 -- path/to/file.rs
Expand All @@ -87,6 +94,7 @@ libra reset --json --hard HEAD~1
libra reset HEAD~1 # Move HEAD and reset index to the previous commit
libra reset --soft HEAD~2 # Move HEAD only, keep index and worktree
libra reset --hard main # Reset HEAD, index, and worktree to branch 'main'
libra reset src/lib.rs # Unstage a path back to HEAD
libra reset HEAD -- src/lib.rs # Unstage a path back to HEAD
libra reset --pathspec-from-file=paths.txt # Unstage paths read from a file ('-' for stdin)
libra reset --json --hard HEAD~1 # Structured JSON output for agents
Expand Down Expand Up @@ -159,7 +167,7 @@ Pathspec reset:

### Why reject pathspecs with --soft/--hard?

- **`--soft` + pathspecs**: `--soft` by definition only moves HEAD and touches nothing else. Resetting individual file index entries contradicts the "HEAD only" contract. If you want to un-stage specific files, use the default mixed mode: `libra reset HEAD -- file`.
- **`--soft` + pathspecs**: `--soft` by definition only moves HEAD and touches nothing else. Resetting individual file index entries contradicts the "HEAD only" contract. If you want to un-stage specific files, use the default mixed mode: `libra reset file` or `libra reset HEAD -- file`.
- **`--hard` + pathspecs**: `--hard` restores the entire working tree to match the target commit. Selectively restoring only some files while leaving others in a different state would create a confusing hybrid that is neither "fully reset" nor "index-only reset." For selective file restoration, use `libra restore --source <commit> -- file`.

This restriction makes the three modes unambiguous: soft touches HEAD, mixed touches HEAD + index, hard touches HEAD + index + worktree. Pathspecs operate orthogonally at the index level only.
Expand All @@ -185,15 +193,15 @@ Libra favors explicit, composable commands over implicit multi-step operations h
| Mixed reset (default) | `git reset <target>` | `libra reset <target>` | N/A (jj has no staging area) |
| Soft reset | `git reset --soft <target>` | `libra reset --soft <target>` | N/A |
| Hard reset | `git reset --hard <target>` | `libra reset --hard <target>` | `jj restore --from <rev>` |
| Un-stage files | `git reset HEAD -- <file>` | `libra reset HEAD -- <file>` | N/A (no staging area) |
| Un-stage files | `git reset <file>` / `git reset HEAD -- <file>` | `libra reset <file>` / `libra reset HEAD -- <file>` | N/A (no staging area) |
| Merge reset | `git reset --merge <target>` | Not supported | N/A |
| Keep reset | `git reset --keep <target>` | Not supported | N/A |
| Pathspec from file | `git reset --pathspec-from-file=<f>` | `libra reset --pathspec-from-file=<f>` (literal paths; no C-style quote decoding) | N/A |
| Pathspec file NUL | `git reset --pathspec-file-nul` | `libra reset --pathspec-file-nul` | N/A |
| Index refresh control | `git reset --[no-]refresh` | `--no-refresh` accepted as a no-op; no `--refresh` | N/A |
| Default target | HEAD | HEAD | N/A |
| Structured output | No | `--json` / `--machine` | `--template` |
| Pathspec + soft | Allowed (un-stages) | Rejected (`LBR-CLI-002`) | N/A |
| Pathspec + soft | Rejected | Rejected (`LBR-CLI-002`) | N/A |
| Pathspec + hard | Rejected | Rejected (`LBR-CLI-002`) | N/A |
| Pathspec from file + CLI pathspec | Rejected | Rejected (`LBR-CLI-002`) | N/A |
| Rollback on failure | No | Attempts index rollback | N/A (operation log undo) |
Expand All @@ -204,6 +212,7 @@ Libra favors explicit, composable commands over implicit multi-step operations h
|----------|-----------|------|
| Not a libra repository | `LBR-REPO-001` | "run 'libra init' to create a repository in the current directory." |
| Invalid revision | `LBR-CLI-003` | "check the revision name and try again." |
| Ambiguous revision/path token | `LBR-CLI-002` | "use '--' to separate paths from revisions, like 'libra reset <revision> -- <file>' or 'libra reset -- <file>'." |
| HEAD is unborn | `LBR-REPO-003` | "create a commit first before resetting HEAD." |
| Failed to resolve HEAD | `LBR-IO-001` | "check whether the repository database is readable." |
| HEAD reference corrupt | `LBR-REPO-002` | "the HEAD reference or branch metadata may be corrupted." |
Expand Down
Loading
Loading