Checkout Tags Scripts

Checking out a tag puts you in detached HEAD, which is safe to read from and risky to commit to.

Checking out a tag is how you get the exact tree a release shipped, which makes it the first thing to try when a bug is reported against a version. The catch is that a tag is not a branch, and Git puts you into a state called detached HEAD when you do it.

The basic script

#!/usr/bin/env bash
# checkout-tag.sh
#
# Usage: checkout-tag.sh <tag> [--branch <name>]

set -euo pipefail

tag="${1:-}"
branch="${3:-}"

if [ -z "$tag" ]; then
  echo "Usage: $0 <tag> [--branch <name>]" >&2
  exit 1
fi

if ! git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
  echo "No such tag: $tag" >&2
  echo "Known tags:" >&2
  git tag -l | sed 's/^/  /' >&2
  exit 1
fi

# the tags/ prefix disambiguates from a branch of the same name
if [ -n "$branch" ]; then
  git checkout -b "$branch" "tags/$tag"
  echo "Created branch $branch at $tag"
else
  git checkout "tags/$tag"
  cat <<'MSG'
You are in detached HEAD state. Commits made here belong to no branch.

  git switch -c my-fix    to keep work started here
  git switch -            to go back to where you were
MSG
fi

What detached HEAD means

HEAD normally points at a branch, and commits move the branch. After a tag checkout, HEAD points straight at a commit. Nothing moves it forward except a new commit, and that commit belongs to no branch — so a week later it is only reachable through the reflog, and git gc may eventually collect it.

The state is completely safe for reading, building and testing, which is what you usually want a tag for. It only becomes a problem if you commit to it.

The fix, if you did:

git switch -c my-fix

That attaches a branch at your commit, and it is now kept. Check git reflog if you have already switched away — see Git Reflog.

Listing what is available

git tag -l --sort=-v:refname | head -5

Sorts by version rather than alphabetically, which is the ordering you want when picking which release to reproduce. See Managing Tags Scripts.

Getting back

git switch -

Returns to wherever you were before, and is the quickest way out of a tag checkout you did not mean to make. git switch - is the git equivalent of git checkout -.

Reproducing a release build

./checkout-tag.sh v1.4.0
npm ci
npm test

Checking out the tag and then installing from the lockfile as committed is what makes a bug report reproducible — and it is worth doing before you file one, because it is rarely the code's fault.