Git Describe

git describe turns the nearest tag into a version string, which is how build scripts derive a version number.

git describe looks backwards from a commit for the most recent tag and prints it, with the number of commits since as a suffix. v1.4.0-27 means "27 commits past the tag v1.4.0", which is a version number a build can use without any convention of your own.

The two forms

git describe --tags            # v1.4.0-27-g3f8a1b2
git describe --tags --abbrev=0  # v1.4.0

The default output has three parts: the tag, how many commits since it, and the hash of the commit you are on. --abbrev=0 drops the last two, giving the bare tag — the nearest release.

Annotated or lightweight — this is the gotcha

Plain git describe considers only annotated tags. One created with a bare git tag <name> has no tagger information, so git describe skips it — and if that lightweight tag is the only one, the command fails:

$ git describe
fatal: No annotated tags can describe 'a1b2c3d'.
However, there were unannotated tags: try --tags.

--tags is the flag that includes lightweight tags as well as annotated ones. The page above uses it throughout for exactly that reason, and this is the single most common reason git describe fails in a build script.

git tag -a v1.4.0 -m "Release 1.4.0"   # annotated, picked up by plain describe
git tag v1.5.0                         # lightweight, needs --tags

Prefer annotated tags regardless, because git describe in a build pipeline should not depend on which flag everybody remembered to pass. See Tagging.

Deriving a version

The usual use is a build script stamping a version into the artefact:

VERSION=$(git describe --tags --always --dirty)
# v1.4.0-27-g3f8a1b2-dirty

--always falls back to a bare hash when no tag is reachable, so the command does not fail in a shallow clone or a fresh clone with no tags. --dirty appends that suffix when the working tree has uncommitted changes, which stops a dirty build being published under a clean version string.

To get the next version rather than the current one, ask for the nearest tag and add one:

latest=$(git describe --tags --abbrev=0)
next="v$(printf '%s' "${latest#v}" | awk -F. '{printf "%d.%d.%d", $1, $2, $3+1}')"

From a specific tag

git describe --tags v1.4.0..HEAD   # commits since v1.4.0

Useful for release notes: it tells you how far the current branch is from a release.

Finding a commit from a version

The other direction, and the one that makes the format worth having:

git log v1.4.0..HEAD --oneline
git rev-list --count v1.4.0..HEAD

Tags as boundary markers are what make "what is in this release" a one-line question. See Pushing Tags for getting the tag to a remote, and Managing Tags Scripts for listing them in the right order — which git describe already does for you.