LibraLibra
LibraLibra
DocsBlogLibra
Getting Started
Architecture
Design
Commands
libra addlibra agentlibra alternateslibra applylibra archivelibra authlibra automationlibra bisectlibra blamelibra branchlibra bundlelibra cachelibra cat-filelibra check-attrlibra check-ignorelibra check-mailmaplibra checkoutlibra cherry-picklibra cleanlibra clonelibra cloudlibra code-controllibra codelibra commit-treelibra commitlibra completionslibra configlibra credentiallibra depslibra describelibra diff-fileslibra diff-indexlibra diff-treelibra difflibra dirtylibra fast-exportlibra fast-importlibra fetchlibra filelibra for-each-reflibra format-patchlibra fscklibra gclibra graphlibra greplibra hash-objectlibra hookslibra hydratelibra index-packlibra initlibra investigatelibra layerlibra lfslibra loglibra logfilelibra loginlibra logoutlibra ls-fileslibra ls-remotelibra ls-treelibra maintenancelibra medialibra merge-baselibra merge-filelibra mergelibra metadatalibra mvlibra noteslibra oplibra openlibra pack-objectslibra packagelibra prunelibra publishlibra pulllibra pushlibra read-treelibra rebaselibra refloglibra remotelibra repacklibra replacelibra rererelibra resetlibra restorelibra rev-listlibra rev-parselibra revertlibra reviewlibra revisionlibra rmlibra sandboxlibra servicelibra shortloglibra show-reflibra showlibra sparse-viewlibra stashlibra statslibra statuslibra switchlibra symbolic-reflibra taglibra update-indexlibra update-reflibra usagelibra verify-packlibra whoamilibra worktreelibra write-tree
API Reference
Policy
Commands

libra describe

Command reference for `libra describe`

Find the nearest reachable tag for a commit and format it as a human-readable version description.

Alias: desc

Synopsis

libra describe [OPTIONS] [COMMIT]

Description

libra describe walks the commit ancestry graph (BFS) from the given commit (default HEAD) to find the closest tag. The output follows Git's describe format:

  • Exact match: v1.2.3
  • Reachable tag with distance: v1.2.3-4-gabc1234
  • Fallback (--always): abc1234

By default only annotated tags are considered. Pass --tags to also match lightweight tags. When multiple tags are reachable at the same distance, annotated tags are preferred; ties are broken lexicographically.

When no tag can be found and --always is absent, the command fails with an actionable hint suggesting --tags or --always.

--exact-match restricts the command to tags that point directly at the target commit. If no exact tag exists, it fails even when --always is also present.

--long forces Git's long format when a tag describes the target. Exact matches therefore print v1.2.3-0-gabc1234 instead of v1.2.3. Like Git, --long --abbrev=0 is rejected because the long form requires a hash suffix.

--dirty[=<mark>] appends a suffix when tracked content differs from HEAD. The default suffix is -dirty; custom marks are accepted with --dirty=<mark>. Untracked files are ignored, matching Git's dirty check for this command.

Options

FlagDescriptionDefault
<COMMIT>The commit-ish to describe. Accepts HEAD, branch names, tag names, raw SHA-1, HEAD~N.HEAD
--tagsInclude lightweight tags in the search (not just annotated tags).Off
--allConsider any ref (local branches, remote-tracking branches, and tags, including lightweight ones), not just tags. Names are shown with their heads/, remotes/, or tags/ prefix; at a shared commit tags win, then heads, then remotes.Off
--abbrev <N>Number of hex digits for the abbreviated commit hash in the output.7
--alwaysWhen no tag can describe the target, fall back to the abbreviated commit hash instead of failing.Off
--exact-matchOnly succeed when the target commit exactly matches a tag.Off
--candidates <N>N=0 requires an exact tag match (equivalent to --exact-match); N>=1 keeps Libra's deterministic nearest-tag search (the positive bound is not enforced).—
--longForce tag-N-gHASH output when a tag describes the target, including tag-0-gHASH for exact matches.Off
--dirty[=<mark>]Append a dirty mark when tracked content differs from HEAD.Off, default mark -dirty when enabled
--first-parentFollow only the first parent of merge commits when walking history.Off
--match <pattern>Only consider tags whose name matches the glob (repeatable; OR semantics, wax globs ≤256 chars).None
--exclude <pattern>Exclude tags whose name matches the glob (repeatable; takes precedence over --match).None
--containsInverse containment query (git name-rev): name the target by its nearest descendant tag, printing <tag>, <tag>~<n>, or <tag>~<n>^<m>~<k>. Implies lightweight tags; equal-weight ties resolve by tag name; fails when no tag descends from the target.Off

Examples

# Describe HEAD using annotated tags only
libra describe

# Include lightweight tags
libra describe --tags

# Always produce output, even without tags
libra describe --always

# Only succeed on an exact tag match
libra describe --exact-match

# Force tag-0-gHASH output on an exact tag match
libra describe --long

# Describe a specific commit
libra describe HEAD~5

# Use longer abbreviated hashes
libra describe --abbrev 12

# Append -dirty when tracked content differs from HEAD
libra describe --dirty

# Use a custom dirty mark
libra describe --dirty=-worktree

# Follow only the first parent of merge commits
libra describe --first-parent

# Only consider tags matching a glob
libra describe --match 'v1.*'

# Skip release-candidate tags
libra describe --exclude '*rc*'

# Name a commit by its nearest descendant tag (e.g. v1.0~2)
libra describe --contains HEAD~2

# JSON output for automation
libra describe --json

Common Commands

libra describe
libra describe --tags
libra describe --always
libra describe --exact-match
libra describe --long
libra describe --dirty
libra describe HEAD~1
libra describe --contains HEAD~2
libra describe --json
libra describe --tags --abbrev 10

Human Output

  • Exact tag match: v1.2.3
  • Exact tag match with --long: v1.2.3-0-gabc1234
  • Reachable tag: v1.2.3-4-gabc1234
  • --always fallback: abc1234
  • --dirty on tracked changes: v1.2.3-dirty
  • --dirty=-worktree on tracked changes: v1.2.3-worktree

--quiet suppresses stdout.

Structured Output (JSON examples)

--json / --machine returns:

Tag match (exact)

{
  "ok": true,
  "command": "describe",
  "data": {
    "input": "HEAD",
    "resolved_commit": "abc1234def5678901234567890abcdef12345678",
    "result": "v1.2.3",
    "tag": "v1.2.3",
    "distance": 0,
    "abbreviated_commit": null,
    "exact_match": true,
    "used_always": false,
    "long_format": false,
    "dirty": false,
    "dirty_mark": null
  }
}

Tag match (with distance)

{
  "ok": true,
  "command": "describe",
  "data": {
    "input": "HEAD",
    "resolved_commit": "abc1234def5678901234567890abcdef12345678",
    "result": "v1.2.3-4-gabc1234",
    "tag": "v1.2.3",
    "distance": 4,
    "abbreviated_commit": "abc1234",
    "exact_match": false,
    "used_always": false,
    "long_format": false,
    "dirty": false,
    "dirty_mark": null
  }
}

Fallback (--always, no tag found)

{
  "ok": true,
  "command": "describe",
  "data": {
    "input": "HEAD",
    "resolved_commit": "abc1234def5678901234567890abcdef12345678",
    "result": "abc1234",
    "tag": null,
    "distance": null,
    "abbreviated_commit": "abc1234",
    "exact_match": false,
    "used_always": true,
    "long_format": false,
    "dirty": false,
    "dirty_mark": null
  }
}

When --always is used and no tag matches, tag and distance are null and abbreviated_commit contains the emitted hash.

Dirty suffix

{
  "ok": true,
  "command": "describe",
  "data": {
    "input": "HEAD",
    "resolved_commit": "abc1234def5678901234567890abcdef12345678",
    "result": "v1.2.3-dirty",
    "tag": "v1.2.3",
    "distance": 0,
    "abbreviated_commit": null,
    "exact_match": true,
    "used_always": false,
    "long_format": false,
    "dirty": true,
    "dirty_mark": "-dirty"
  }
}

Design Rationale

--match, --exclude, --first-parent, and what is still missing

Libra exposes --match and --exclude (wax globs, capped at 256 chars, with exclude taking precedence over match) and --first-parent (follow only the first parent of merge commits during the BFS walk). --candidates <N> is exposed for its well-defined boundary: N=0 means "only exact matches" (equivalent to --exact-match), while N>=1 keeps Libra's predictable nearest-tag BFS (the candidate bound only affects which of several equally reachable tags Git would prefer, which Libra resolves deterministically). --all is supported: branches (heads/), remote-tracking branches (remotes/), and tags (tags/, including lightweight ones) are all folded into the candidate set for the same BFS, with tags taking precedence at a shared commit, then heads, then remotes. --contains is supported: it runs Git's reverse-walk containment algorithm (name-rev) — a Dijkstra-style walk backward from every tag commit where first-parent steps are cheap and other-parent steps are expensive, so the closest descendant tag's straightest path wins — and prints <tag>, <tag>~<n>, or <tag>~<n>^<m>~<k>. It implies lightweight-tag inclusion (like git name-rev --tags) and fails when no tag descends from the commit.

Why include both string and structured fields?

Human output follows Git's string format, including --long for exact matches. The JSON output also includes separate tag, distance, abbreviated_commit, exact_match, and long_format fields, so automation can avoid parsing the human string when it needs to distinguish exact matches from reachable tags.

Why BFS instead of Git's candidate algorithm?

Git's describe uses a more complex algorithm that considers multiple tag candidates and picks the one with the smallest distance, with heuristics to avoid walking the entire graph. Libra uses a simpler BFS from the target commit, which guarantees finding the closest tag (shortest path in the DAG). For the repository sizes Libra targets (monorepos with structured tagging), BFS is fast enough and its behavior is trivially predictable. The trade-off is that very deep histories with many tags could be slower than Git's pruned search, but this has not been a problem in practice.

Parameter Comparison: Libra vs Git vs jj

FeatureLibraGitjj
Default targetHEADHEADN/A (no built-in describe)
Annotated tags onlyDefault behaviorDefault behaviorN/A
Include lightweight tags--tags--tagsN/A
Abbreviated hash length--abbrev <N> (default 7)--abbrev=<N> (default dynamically chosen)N/A
Fallback to hash--always--alwaysN/A
Exact match only--exact-match--exact-matchN/A
Force long format--long--longN/A
Match tag pattern--match <glob> (wax, ≤256 chars, repeatable)--match <glob>N/A
Exclude tag pattern--exclude <glob> (exclude wins over match)--exclude <glob>N/A
Candidate count--candidates <N> (N=0 ⇒ exact-match; N≥1 ⇒ nearest-tag BFS)--candidates=<N> (default 10)N/A
First-parent only--first-parent--first-parentN/A
Consider all refs--all (heads/remotes/tags, prefixed)--allN/A
Find tags containing a commit--contains (name-rev, prints <tag>~<n>^<m>)--containsN/A
Dirty suffix--dirty[=<mark>]--dirty[=<mark>]N/A
JSON output--json with typed fieldsNoNo
AlgorithmBFS (shortest path)Heuristic multi-candidateN/A

Error Handling

ScenarioStableErrorCodeExit
Invalid revisionLBR-CLI-003129
HEAD has no commitLBR-REPO-003128
No tags can describe the target and --always is absentLBR-REPO-003128
--exact-match target has no exact tag (incl. --contains --exact-match resolving only to a relative ~N name)LBR-REPO-003128
--contains target has no descendant tagLBR-REPO-003128
--long --abbrev=0LBR-CLI-002129
Failed to read refs or objectsLBR-IO-001 / LBR-REPO-002128

libra deps

Command reference for `libra deps`

libra diff-files

Command reference for `libra diff-files`

On this page

SynopsisDescriptionOptionsExamplesCommon CommandsHuman OutputStructured Output (JSON examples)Tag match (exact)Tag match (with distance)Fallback (--always, no tag found)Dirty suffixDesign Rationale--match, --exclude, --first-parent, and what is still missingWhy include both string and structured fields?Why BFS instead of Git's candidate algorithm?Parameter Comparison: Libra vs Git vs jjError Handling