git describe --tags --dirty: Version Strings in CI
git describe --tags --dirty produces a version like v1.4.2-3-gabc1234-dirty: nearest tag, commits since, short SHA, plus a -dirty suffix if the tree has uncommitted changes.
Build pipelines lean on git describe to stamp artifacts with a readable version. The flags that matter in CI are --tags (count lightweight tags), --always (fall back to a SHA), and --dirty (flag uncommitted changes).
What it does
git describe finds the most recent tag reachable from HEAD and appends the number of commits since it and the abbreviated SHA. --tags also considers lightweight (non-annotated) tags. --dirty appends a marker if the working tree differs from HEAD. --always falls back to a bare SHA when no tag is reachable.
Common usage
git describe --tags --dirty
# always produce something, even with no tags
git describe --tags --always --dirty
# match only release tags
git describe --tags --match "v[0-9]*"
# just the long form (always tag-count-sha)
git describe --tags --longOptions
| Flag | What it does |
|---|---|
| --tags | Use lightweight tags too, not only annotated |
| --dirty[=<mark>] | Append -dirty (or <mark>) if the tree is modified |
| --always | Fall back to an abbreviated SHA if no tag is found |
| --long | Always show tag-count-gSHA, even on an exact tag |
| --match <pattern> | Only consider tags matching the glob |
| --abbrev=<n> | Number of hex digits in the short SHA |
In CI
Shallow clones usually omit tags, so git describe fails with "No names found". Fetch tags first: git fetch --tags --unshallow, or set fetch-depth: 0 in actions/checkout. Add --always so a tagless repo still yields a SHA instead of erroring the whole build.
Common errors in CI
"fatal: No names found, cannot describe anything." means no reachable tag, almost always a shallow clone without tags; fetch tags or add --always. "fatal: No annotated tags can describe ..." means you have only lightweight tags; add --tags. "fatal: Not a valid object name HEAD" appears on an empty repo with no commits.
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached