libra add
Command reference for `libra add`
Stage file contents for the next commit.
Synopsis
libra add [OPTIONS] [PATHSPEC...]
libra add -A
libra add -u [PATHSPEC...]
libra add --refresh [PATHSPEC...]
Description
libra add stages file changes from the working tree into the index, preparing them
for the next libra commit. It supports pathspecs, glob patterns, --dry-run preview,
and --refresh to re-stat already tracked entries without staging new content.
The command resolves pathspecs relative to the current working directory, validates them
against the repository root, and respects .libraignore rules. Files tracked by LFS are
automatically staged as pointer files. The -A flag stages all changes (adds, modifies,
removes) across the entire working tree, while -u updates only tracked files without
adding new ones.
--sparse (Libra 0.23.25, plan-20260918 SW-06) lets add update skip-worktree entries: without it a pathspec matching only such entries prints the sparse diagnostic and exits 1 (data.sparse_paths in JSON); with it a modified sparse file is staged with the bit preserved, while a deleted one keeps the ordinary not-matched error.
Staged file modes honor core.filemode (plan issues/470 FM-03/FM-04): with false, re-staging keeps an existing entry's mode and a new path is recorded 100644; with true (Unix default) a mode-only change to a tracked regular file is staged even when its content is unchanged; --chmod still applies explicitly.
Options
[PATHSPEC...]
One or more files or directories to stage. Paths are resolved relative to the current
directory. Required unless -A, -u, or --refresh is specified.
libra add file.txt
libra add src/ tests/
libra add .
-A, --all
Update the index to match the entire working tree. Stages new files, modifications, and
deletions. When no pathspec is given, all files in the working tree are updated. Mutually
exclusive with -u and --refresh.
libra add -A
-u, --update
Update the index only where it already has entries matching the pathspec. Stages
modifications and deletions of tracked files but does not add new (untracked) files.
Mutually exclusive with -A and --refresh.
libra add -u
libra add -u src/
--refresh
Refresh index entries for all files currently in the index. Updates only metadata
(timestamps, file size) of existing index entries to match the working tree, without
adding new files or removing entries. Mutually exclusive with -A and -u.
libra add --refresh
-f, --force
Allow adding files that are otherwise ignored by .libraignore.
libra add -f ignored_file.log
-n, --dry-run
Preview what would be staged without actually modifying the index. Output shows which
files would be added, modified, or removed. -n matches Git; -d is also accepted
as a Libra-compatible short alias.
libra add -n file.txt
libra add --dry-run .
-v, --verbose
Produce more detailed output, showing per-file actions during staging.
libra add -v src/
--ignore-errors
Continue staging remaining files when individual paths fail. Failed paths are reported in the output but do not cause the command to exit with an error.
libra add --ignore-errors src/
--pathspec-from-file <file>
Read pathspecs from <file> (one per line); the command line must then carry no
pathspec arguments. Entries use the same shared pathspec matcher and magic forms as
positional pathspecs. A value of - reads the list from stdin instead of opening a
file (a worktree file literally named - is never read). Newline mode splits on \n
and strips one trailing \r per line, so CRLF lists work; empty lines are ignored.
Pair with --pathspec-file-nul when the list is NUL-separated (e.g. produced by
another tool's -z output) — NUL mode keeps every byte, including a CR.
A payload that is not valid UTF-8, or a file that cannot be read, is a fatal
LBR-IO-001 error (exit 128) with zero writes. An empty list is a usage error
(nothing specified, nothing added, exit 129); Git accepts an empty list as a
no-op, an intentional difference.
In newline mode a line that starts with " is decoded as one Git C-style quoted
string (\n, \t, \", \\, octal escapes, …), so paths containing spaces
or quotes survive; an unquoted line is used verbatim. Malformed quoting
(unterminated, trailing bytes after the closing quote, or an unknown escape) is a
fatal LBR-IO-001 error (exit 128) with zero writes. NUL mode never decodes.
--pathspec-from-file cannot be combined with -p/--patch, --edit,
--interactive, or command-line pathspec arguments (Git's
cannot be used together contract): each combination is a usage error
(LBR-CLI-002, exit 129) that writes nothing. (Git refuses the same combinations
with exit 128; Libra's 129 is its usage-error code — an intentional difference.
--interactive keeps its own declined-flag refusal, exit 128 +
LBR-UNSUPPORTED-001.)
libra add --pathspec-from-file paths.txt
printf 'a.txt\nb.txt\n' | libra add --pathspec-from-file=-
libra add --pathspec-from-file paths.bin --pathspec-file-nul
--pathspec-file-nul
Treat the --pathspec-from-file input as NUL-separated rather than newline-separated.
Requires --pathspec-from-file; using it alone is a usage error.
--chmod=(+|-)x
Force the executable bit recorded in the index for the matched paths: +x records
mode 100755, -x records 100644. The blob content is unchanged. A path whose
recorded mode actually changes is reported as modified, even when its content did
not change.
Only regular files carry an executable bit. A matched symlink (120000) or
gitlink (160000) is refused: the entry is left unchanged, an
error: cannot chmod +x '<path>' line is printed to stderr for each refusal, and
add exits 1 after the remaining paths have been processed normally. In --json
mode the refusals appear as chmod_rejected: [{"path", "flip"}] on the envelope
instead, with the same exit 1. (Git exits 255 here; Libra uses the process-level
exit 1 model shared with the ignored-path report.)
An invalid value (anything other than +x / -x) is a usage error.
With no pathspec (and none of -A, -u, --refresh, --renormalize,
--resolved), --chmod is a successful no-op: add exits 0 without touching
the index or object store — there is nothing to apply the mode to.
libra add --chmod=+x scripts/build.sh
libra add --chmod=-x notes.txt
--renormalize
Re-stage tracked files from scratch, rewriting their blobs even when the content is
unchanged. Implies -u: only tracked files are processed (never untracked ones), and
a tracked file removed from the working tree has its deletion staged.
libra add --renormalize
libra add --renormalize src/
--ignore-missing
Under --dry-run, a pathspec that matches no add candidate is classified against
the configured ignore rules (.libraignore, .gitignore): an ignored pattern is
reported like other ignored paths and makes add exit 1 (unless it is the only
pathspec, which keeps the LBR-ADD-001 / exit 128 contract); a path that is not
ignored is skipped with a warning on stderr. Mirrors Git: --ignore-missing
requires --dry-run.
libra add --dry-run --ignore-missing maybe-missing.txt other.txt
Common Commands
libra add file.txt
libra add src/
libra add .
libra add -n file.txt
libra add --refresh
libra add --ignore-errors src/
libra add --pathspec-from-file paths.txt
libra add --chmod=+x scripts/build.sh
libra add --renormalize
Human Output
Default human mode writes the staging summary to stdout.
Single file:
add 'src/main.rs' (new file)
Multiple files:
add 'src/main.rs' (new file)
add 'src/lib.rs' (modified)
add 'old.txt' (deleted)
Dry-run:
add 'src/main.rs' (new file)
add 'src/lib.rs' (modified)
(dry run, no files were staged)
Ignored files produce a warning on stderr:
warning: the following paths are ignored by configured ignore rules:
ignored.log
Hint: use -f if you really want to add them.
When some paths were staged (or reported by a dry-run) and other explicit
pathspecs were ignored, add finishes the whole operation — staging, output,
warnings and automation events included — and then exits 1, like Git.
When every path was ignored and nothing else was staged, add fails with
LBR-ADD-001 and exits 128 instead (intentional difference from Git's 1).
--json keeps the regular data envelope on stdout (with the ignored paths in
data.ignored) and exits 1; the exit code 1 also wins over
--exit-code-on-warning's 9.
--quiet suppresses all stdout output but preserves stderr warnings.
Structured Output
libra add supports the global --json and --machine flags.
--jsonwrites one success envelope tostdout--machinewrites the same schema as compact single-line JSONstderrstays clean on success
Example:
{
"ok": true,
"command": "add",
"data": {
"added": ["src/main.rs"],
"modified": ["src/lib.rs"],
"removed": ["old.txt"],
"refreshed": [],
"ignored": [],
"failed": [],
"dry_run": false
}
}
Dry-run:
{
"ok": true,
"command": "add",
"data": {
"added": ["src/main.rs"],
"modified": [],
"removed": [],
"refreshed": [],
"ignored": [],
"failed": [],
"dry_run": true
}
}
Partial failure with --ignore-errors:
{
"ok": true,
"command": "add",
"data": {
"added": ["good.txt"],
"modified": [],
"removed": [],
"refreshed": [],
"ignored": [],
"failed": [
{"path": "bad.bin", "message": "file too large"}
],
"dry_run": false
}
}
Schema Notes
added/modified/removedcorrespond to new, changed, and deleted files stagedrefreshedis populated only when--refreshis usedignoredlists paths skipped by.libraignorefailedlists paths that failed to stage, each withpathandmessagedry_runistruewhen-n/--dry-runis passed; no files are actually staged
Design Rationale
No --intent-to-add / -N
Git's --intent-to-add (-N) records an empty blob for untracked files so that they
appear in git diff output without actually staging their content. This is a workflow
convenience for reviewing new files before staging them. Libra omits this flag because
libra status already shows untracked files clearly, and libra diff is designed to
work with the full working tree state. The two-step "intent then stage" workflow adds
cognitive overhead without meaningfully improving the review experience. Users who want
to review new files before committing can use libra add --dry-run followed by
libra diff --staged after staging.
No --patch / -p interactive staging
Git's --patch mode provides an interactive hunk-by-hunk staging interface within the
terminal. Libra's add command does not implement that interface. Use libra add --dry-run
to preview a selection, then stage explicit paths; use libra diff --staged to review
the resulting index. The command remains non-interactive for human, scripted, and
external-agent workflows.
--refresh as explicit flag
In Git, git add --refresh silently updates stat information for tracked files. Libra
surfaces this as a first-class mode that is mutually exclusive with -A and -u (enforced
by clap argument groups). This makes the intent explicit: --refresh never stages new
content, only updates metadata. The mutual exclusivity prevents confusing combinations like
-A --refresh where the user's intent would be ambiguous.
.libraignore instead of .gitignore
Libra uses .libraignore files for its ignore policy rather than .gitignore. This avoids
conflicts when a Libra repository coexists with or is converted from a Git repository, and
makes it clear which VCS owns the ignore rules. The ignore file format is compatible with
Git's pattern syntax (globs, negation with !, directory-only patterns with trailing /).
libra init creates a root .libraignore in non-bare repositories, and Git imports or
non-bare clones copy existing .gitignore files to matching .libraignore files.
Parameter Comparison: Libra vs Git vs jj
| Parameter / Flag | Git | jj | Libra |
|---|---|---|---|
| Stage a file | git add file.txt |
N/A (jj auto-tracks) | libra add file.txt |
| Stage everything | git add . or git add -A |
N/A (automatic) | libra add . or libra add -A |
| Update tracked only | git add -u |
N/A | libra add -u |
| Dry-run preview | git add -n / --dry-run |
N/A | libra add -n / --dry-run |
| Force add ignored | git add -f |
N/A | libra add -f |
| Refresh stat info | git add --refresh |
N/A | libra add --refresh |
| Verbose output | git add -v |
N/A | libra add -v |
| Ignore errors | git add --ignore-errors |
N/A | libra add --ignore-errors |
| Intent to add | git add -N / --intent-to-add |
N/A | N/A (not implemented) |
| Interactive patch | git add -p / --patch |
N/A | N/A (not implemented; preview with --dry-run, then stage explicit paths) |
| Interactive select | git add -i / --interactive |
N/A | N/A (not implemented; stage explicit paths) |
| Edit diff before staging | git add -e / --edit |
N/A | N/A |
| Chmod only | git add --chmod=+x |
libra add --chmod=+x (non-regular index entries are refused with exit 1) |
N/A |
| Sparse checkout paths | git add --sparse |
N/A | N/A |
| Ignore file | .gitignore |
N/A (jj uses .gitignore) |
.libraignore |
| Structured JSON output | N/A | N/A | --json / --machine |
| Error hints | Minimal | N/A | Every error type has an actionable hint |
Error Handling
Every AddError variant maps to an explicit StableErrorCode.
| Scenario | Error Code | Exit | Hint |
|---|---|---|---|
| Not inside a repository | LBR-REPO-001 |
128 | "run 'libra init' to create a repository" |
| Pathspec matched nothing | LBR-CLI-003 |
129 | "check the spelling and use 'libra status' to see what changed" |
| Path outside repository root | LBR-CLI-003 |
129 | "only files within the repository root can be staged" |
| Invalid path encoding | LBR-CLI-003 |
129 | "path contains invalid UTF-8 characters" |
| Index file corrupted | LBR-REPO-002 |
128 | "the index file may be corrupted; try 'libra status' to verify" |
| Failed to save index | LBR-IO-002 |
128 | "check disk space and file permissions" |
| Refresh failed | LBR-IO-001 |
128 | -- |
| Entry creation failed | LBR-IO-002 |
128 | -- |
| Object or durable index-marker write failed | LBR-IO-002 |
128 | Check storage permissions and retry; the error is returned without a panic and the staging area is unchanged: the message says the object payloads were stored safely, no paths were staged, and a direct retry reuses the already-stored payloads without any lock-file cleanup. When the failure is a lock timeout, the message names the holder (its pid and purpose, e.g. marker_publication, queued_update, replay, deletion_fence) or says the holder could not be determined; wait for that process to finish and retry. Waiting uses a Git-style quadratic backoff and gives up after 10 seconds. Never delete the lock files under .libra/object-index-repair-locks: they exist only to arbitrate concurrent writers, do not block anything by themselves, and are released automatically when their owner exits. Read-only commands that replay pending cloud-index repair markers skip the replay (silently, without a warning) when the lock is busy and retry on the next command |
| Working directory error | LBR-REPO-001 |
128 | "cannot determine the working tree" |
| Status computation failed | LBR-REPO-002 |
128 | -- |
| All paths ignored (nothing staged) | LBR-ADD-001 |
128 | "use -f if you really want to add them" |
| No pathspec and no mode flag | LBR-CLI-001 |
129 | "maybe you wanted to say 'libra add .'?" |
Compatibility Notes
- jj does not have an
addcommand; it automatically tracks all working tree changes - Libra's
addis required beforecommit, matching Git's explicit staging model .libraignoreuses the same pattern syntax as.gitignorebut is a separate file; imports and non-bare clones copy.gitignorerules instead of deleting or renaming the originals- An un-ignored missing pathspec still prints Libra's
--ignore-missingskip warning (Git is silent there) - With a directory pathspec, an already-existing ignored parent directory is not additionally reported (Git also lists it)
- C-quoted
--pathspec-from-filelines accept one to three octal digits (Git requires exactly three) and reject trailing bytes after the closing quote (Git ignores them) - LFS-tracked files are automatically converted to pointer files during staging
Issue #477 notes
remaining unsupported interactive options fail with LBR-UNSUPPORTED-001
Stage this hunk [y,n,q,a,d