git show-ref --verify: Test a Ref Exists in CI
git show-ref --verify --quiet refs/heads/<branch> exits 0 if that exact ref exists and nonzero otherwise, the reliable way to test for a branch or tag in a script.
Scripts often need to know whether a branch or tag exists before acting. git show-ref --verify gives a clean exit code on a fully qualified ref, avoiding the ambiguity of partial name matching.
What it does
git show-ref lists refs and their SHAs from refs/. With a pattern it lists matching refs; with --verify it requires an exact, fully qualified ref path and exits nonzero if it is absent. --quiet suppresses output so only the exit code matters. --heads and --tags filter to those namespaces.
Common usage
# does a local branch exist?
git show-ref --verify --quiet refs/heads/release && echo yes
# does a tag exist?
git show-ref --verify --quiet refs/tags/v1.4.0
# list all branch refs with SHAs
git show-ref --heads
# get the SHA of a specific tag
git show-ref --tags refs/tags/v1.4.0Options
| Flag | What it does |
|---|---|
| --verify | Require an exact, fully qualified ref; nonzero if missing |
| --quiet | Print nothing; rely on the exit code |
| --heads | Limit to branch refs (refs/heads/) |
| --tags | Limit to tag refs (refs/tags/) |
| --hash[=<n>] | Print only the SHA (abbreviated to n) |
| -d / --dereference | Also show the SHA an annotated tag points to |
In CI
Use the fully qualified path with --verify (refs/heads/x, refs/tags/x); a bare name fails. To check a remote-tracking branch fetched into the clone, verify refs/remotes/origin/<branch>. On a shallow single-branch clone, other branches and tags are simply absent, so show-ref legitimately reports them missing.
Common errors in CI
"fatal: 'main' - not a valid ref" from --verify means you passed a short name instead of refs/heads/main. A nonzero exit with --quiet is the intended "not found" signal, not a crash. If a tag you expect is missing, the clone likely did not fetch tags; git fetch --tags.
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