Skip to content
Latchkey

git update-ref: Usage, Options & Common CI Errors

git update-ref is the low-level way to point a ref at a commit, with optional safety checks.

Automation that moves branches or custom refs should use update-ref rather than writing files under .git/refs by hand - it handles packed-refs, reflogs, and compare-and-swap correctly.

What it does

git update-ref sets a reference to a new object id, optionally only if its current value matches an expected old value (compare-and-swap), and can delete a ref or process a batch of updates from standard input.

Common usage

Terminal
git update-ref refs/heads/main <new-sha>
git update-ref refs/heads/main <new-sha> <old-sha>   # CAS guard
git update-ref -d refs/heads/stale
git update-ref --stdin < updates.txt

Options

FlagWhat it does
<ref> <newvalue> [<oldvalue>]Set ref, optionally guarded by old value
-d <ref> [<oldvalue>]Delete a ref
--stdinRead a batch of commands
--create-reflogForce a reflog entry
-zNUL-terminate stdin records

Common errors in CI

fatal: Cannot lock ref ‘refs/heads/main’: is at <X> but expected <Y> - the compare-and-swap old value did not match; another job moved the ref. Re-read the current value and retry. "unable to create ‘.git/refs/...’.lock: File exists" means a stale lock from a killed process; remove it only when sure no other git is running.

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.

.github/workflows/ci.yml
- 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

Frequently asked questions

git update-ref: Usage, Options & Common CI Errors?
Automation that moves branches or custom refs should use update-ref rather than writing files under .git/refs by hand - it handles packed-refs, reflogs, and compare-and-swap correctly.
What it does?
git update-ref sets a reference to a new object id, optionally only if its current value matches an expected old value (compare-and-swap), and can delete a ref or process a batch of updates from standard input.
Common errors in CI?
fatal: Cannot lock ref ‘refs/heads/main’: is at <X> but expected <Y> - the compare-and-swap old value did not match; another job moved the ref. Re-read the current value and retry. "unable to create ‘.git/refs/...’.lock: File exists" means a stale lock from a killed process; remove it only when sure no other git is running.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card