Home / Alt manpages / git-for-each-ref(1)

  • git-for-each-ref(1)
  • User command
  • linux

Inventory Git branches and tags with for-each-ref

In about 15 minutes, you will be able to turn Git refs into stable, readable reports: branch names with their current marker, tags ordered by creation date, and filters for merged or unmerged work. The examples use Git 2.43.0, installed here from the git-man package. They only inspect repository data. No elevated privileges are needed.

Checkpoint 1: confirm the repository and version

  1. Change to the repository you want to inspect and check the Git version.
cd /path/to/repository
git --version
git status --short

The version check should print a Git version. An empty git status --short is not required, but it helps you spot unrelated changes before running commands that might later be copied into a script. This guide is written against 2.43.0. Later Git releases may add fields or options, so check the local manual when portability matters.

Checkpoint 2: list refs with a useful format

A ref is a named pointer such as refs/heads/main or refs/tags/v2.0. for-each-ref reads refs, formats fields with %(fieldname), and prints one formatted record per ref. Supplying a ref prefix keeps the report focused.

  1. Print local branches and tags with a short object name and commit subject.
git for-each-ref \
  --format='%(HEAD) %(refname:short) %(objectname:short) %(subject)' \
  --sort=refname \
  refs/heads refs/tags

Typical output looks like this, although the object IDs and names depend on the repository:

  feature  d8f0e5f second commit
* main     d8f0e5f second commit
  v1.0     142d2cc first commit
  v2.0     55825ca release v2.0

%(HEAD) expands to * for the checked-out branch and a space for other refs. refname:short removes the usual refs/heads/ or refs/tags/ prefix. The default format, when --format is omitted, is the full object name, object type, and ref name.

Checkpoint 3: select and order the records

Patterns are optional. A literal prefix such as refs/remotes/origin selects matching refs below that path, while shell-style patterns can be used when you need a broader match. Exclusions use the same matching rules.

  1. Show only remote-tracking branches, newest by committer date, capped at ten results.
git for-each-ref \
  --count=10 \
  --sort=-committerdate \
  --format='%(committerdate:short) %(refname:short) %(subject)' \
  refs/remotes

The minus sign makes a sort key descending. Multiple --sort options are allowed; the last key is the primary key. Dates such as committerdate can take a date format suffix. If a field does not apply to a particular object, Git returns an empty value rather than failing.

For release names, lexical order is often misleading: v10.0 sorts before v2.0 as text. Ask for version-aware refname sorting instead:

git for-each-ref \
  --sort=version:refname \
  --format='%(refname:short)' \
  refs/tags

Checkpoint 4: answer history questions safely

Reachability filters are useful for release and cleanup reports. --merged lists refs whose tips are reachable from a commit, defaulting to HEAD. --no-merged does the opposite. The related --contains and --no-contains filters ask whether a ref contains a specified commit.

  1. List local branches already merged into the current branch.
git for-each-ref \
  --merged=HEAD \
  --format='%(refname:short)' \
  --sort=refname \
  refs/heads

Expected output includes the current branch and any branch whose tip is reachable from it. This command does not delete anything. Treat its output as a review list, not as automatic permission to remove branches: a branch may contain a name or workflow you still need.

To find refs containing a known commit, first capture an object name, then filter with it:

commit=$(git rev-parse --verify HEAD~2)
git for-each-ref \
  --contains="$commit" \
  --format='%(refname:short)' \
  --sort=refname \
  refs/heads refs/tags

If the revision does not exist, git rev-parse stops the command before the report runs. That is preferable to silently inspecting the wrong commit. When combining several --contains filters, a ref only needs to contain one of the requested commits; it must contain none of the commits named by --no-contains.

Checkpoint 5: make output script-friendly

Formatting is interpolation, not a shell language. A ref name or commit subject can contain characters that change how an unquoted shell line is interpreted. If you are generating assignments for a shell script, request shell quoting from Git.

  1. Generate quoted assignments and consume them as data in a controlled loop.
git for-each-ref --shell \
  --format='ref=%(refname) subject=%(subject)' \
  refs/heads

The output is suitable for the shell's string-literal syntax. Do not pass arbitrary output to eval merely because --shell is available. If a report only needs to be displayed or piped to another tool, use a delimiter such as %09 for a tab and parse it with that tool's safe, non-evaluating input mode.

git for-each-ref \
  --format='%(refname:short)%09%(objectname:short)' \
  refs/heads | while IFS='	' read -r branch commit; do
    printf 'branch=%s commit=%s\n' "$branch" "$commit"
done

Here %09 is Git's hexadecimal escape for a tab, and the shell variables are quoted when printed. Keep the format simple when another program will consume it. For human output, fields such as %(upstream:short), %(upstream:trackshort), %(worktreepath), %(creatordate:short), and %(subject) are often more useful than plumbing details.

Common traps and recovery

  • Unexpected tags or branches: without a pattern, all refs are eligible. Add refs/heads, refs/tags, or refs/remotes explicitly.
  • Wrong ordering: the default sort is refname. Use a date field for recency and prefix it with - for newest first.
  • Blank fields: upstream, push, author, and tag-specific fields can be empty when the ref has no corresponding data. Design the report to tolerate that.
  • Ambiguous short names: refname:short is intended to be non-ambiguous, but full names are safer when exporting data across repositories.
  • Disk-size conclusions: objectsize:disk describes stored object data, but pack deltas and duplicate copies mean it cannot reliably identify which ref is responsible for repository usage.

The commands in this guide do not alter refs, commits, the index, or the working tree, so there is no undo operation to perform. Stop before adding a separate cleanup command based on a report, and review each candidate with the project owner first.

Done means

  • You can restrict reports to the ref namespace you intended.
  • You can format names, IDs, dates, subjects, and current-branch markers.
  • You can sort by refname, version, or commit metadata and limit the count.
  • You can distinguish merged, unmerged, containing, and non-containing refs.
  • You keep generated values quoted and avoid evaluating untrusted output.

For the complete field list and the Git release history, read the official git-for-each-ref documentation. On this machine, the installed manual is the authority for the Git 2.43.0 behaviour used above.