Refs, Reflogs, Packed Refs, Symbolic References, and Reference Transactions: Configuration, Design Choices, and Tradeoffs
Design reference automation around core.logAllRefUpdates, custom namespaces, files/reftable backend abstraction, update-ref transactions, reference-transaction hooks, and server-side atomic push capability.
Learning objectives
- Design explicit reflog creation and meaningful reflog reasons for tool-owned refs.
- Use custom namespaces without confusing naming convention with authorization.
- Keep application logic independent from loose/packed/reftable storage details.
- Coordinate several local ref changes with guarded update-ref transactions.
- Separate local reference-transaction behavior from remote atomic push and hosting policy.
1. Ref design is naming policy plus concurrency policy plus retention policy
A team deciding to create
refs/automation/deploy-candidate is making three
decisions at once: what the name means, who may move it, and what
evidence should remain after movement. The storage file is
secondary. Design the logical namespace and update contract first.
2. Inspect ref/reflog configuration before assuming recovery behavior
git config --list --show-origin --show-scope
git config --show-origin --get core.logAllRefUpdates
git rev-parse --show-ref-format
git reflog list
core.logAllRefUpdates is repository-sensitive. A normal
repository with a working tree commonly enables
branch/remote/notes/HEAD reflog creation by default, while bare
repositories historically default differently. Explicit team policy
is clearer than relying on hidden defaults.
3. core.logAllRefUpdates controls automatic reflog
creation, not permanence
When enabled as true, Git automatically creates missing
reflogs for documented common ref classes such as local branches,
remote-tracking refs, notes refs, and HEAD. Current Git also
documents an always mode that allows automatic reflog
creation under the broader refs/ hierarchy.
4. Tool-owned custom refs can request a reflog explicitly
git update-ref \
--create-reflog \
-m "automation: create deployment candidate" \
refs/automation/deploy-candidate "$OID" ""
An explicit reflog request makes the intent visible and reduces dependence on whether a custom namespace is covered by the repository's automatic creation policy.
5. Reflog messages should explain the operation, not duplicate the object ID
The -m reason supplied to
update-ref becomes useful operational context such as
“release controller advanced verified candidate” or “rollback
automation restored previous approved tip.” Avoid embedding secrets
or tokens in reflog messages because reflogs are local metadata that
may persist beyond the working action.
6. Give automation its own full ref namespace when the name is not a developer branch
git check-ref-format refs/automation/deploy-candidate
git check-ref-format refs/releases/staging/current
A custom namespace makes ownership clearer and prevents branch-oriented porcelain from accidentally presenting internal automation pointers as ordinary feature branches. The namespace itself does not grant permissions; access control belongs to server/host policy.
7. Ref storage format is a repository implementation choice
The traditional files format combines loose ref
files and packed-refs. Current Git also exposes the
reftable format as an experimental alternative.
Query the repository rather than assuming the backend:
git rev-parse --show-ref-format
Because backend choices can change, scripts should use ref commands,
not parse packed-refs or enumerate
.git/refs.
8. pack-refs is a storage optimization, not an
application protocol
Repositories with many refs can reduce storage/filesystem overhead
by packing eligible refs in the files backend. Whether a ref is
loose or packed must not change the semantic result of
show-ref, for-each-ref,
rev-parse, or guarded updates.
9. Use a transaction when several refs describe one logical state change
Suppose deployment automation wants to advance both
refs/automation/candidate and
refs/automation/verified. Updating them independently
creates a window where only one has moved.
git update-ref --stdin can queue guarded changes,
prepare locks, then commit them as one reference transaction.
Git Bash / Bash / zsh
{
echo start
echo "update refs/automation/candidate $NEW_CANDIDATE $OLD_CANDIDATE"
echo "update refs/automation/verified $NEW_VERIFIED $OLD_VERIFIED"
echo prepare
echo commit
} | git update-ref --stdin
If a required expected old value or lock fails, Git does not commit the queued updates. Current documentation also warns that readers are not guaranteed a fully atomic multi-ref view during visibility of the transaction, so downstream readers should model cross-ref consistency deliberately.
10. Current Git can include symbolic-ref operations in update-ref transactions
Current update-ref --stdin documentation includes
symref-update, symref-create,
symref-delete, and symref-verify commands,
allowing ordinary and symbolic refs to participate in one
transaction.
git symbolic-ref for
symbolic-ref inspection and ordinary OID ref transactions that work
on the validation baseline.
11. The reference-transaction hook observes/refuses local reference transactions
Current hook documentation lists lifecycle states including
preparing, prepared,
committed, and aborted. For each queued
ref change the hook receives old value, new value, and full ref name
on standard input. A non-zero status during documented pre-commit
transaction phases can abort the transaction.
This hook can enforce local repository rules or record diagnostics, but it is executable configuration. A clone does not magically inherit a trusted server's hook policy, and a hosted service may expose different governance mechanisms.
12. Harmless disposable reference-transaction observation
cat > .git/hooks/reference-transaction <<'EOF'
#!/bin/sh
state=$1
while IFS= read -r line
do
printf '%s %s\n' "$state" "$line" >> .git/reference-transaction.log
done
exit 0
EOF
chmod +x .git/hooks/reference-transaction
git update-ref -m "hook demo" refs/automation/hook-demo "$OID"
cat .git/reference-transaction.log
Exact states emitted are Git-version-sensitive; compare the installed version's manual with the observed log. The hook should remain non-destructive in training.
13. Local ref transactions and remote atomic push solve related but different boundaries
git update-ref --stdin operates on the local
repository's reference database.
git push --atomic requests an atomic transaction on the
receiving remote side: either all requested remote refs update or
none do. The server must advertise/support the capability; otherwise
the atomic push fails.
14. Hosted branch protection and ref permissions are not Git ref objects
A hosting platform may restrict who can update branches/tags,
require reviews, or connect CI results to ref updates. Those
policies surround Git's ref update mechanism; they are not
properties embedded inside refs/heads/trunk. Keep
Git-level expected-old/ref transaction logic separate from
platform-specific authorization.
15. Configuration scope matters when reflog or hook policy must be reproducible
core.logAllRefUpdates can be influenced by repository
configuration, while hook location can be affected by
core.hooksPath. Inspect origin/scope before relying on
either. For automation tests, prefer repository-local settings in
disposable repos rather than altering a learner's global Git
configuration.
16. Decision table — choose a ref update strategy
| Scenario | Recommended mechanism | Why | Cost/risk |
|---|---|---|---|
| One local tool-owned ref | Guarded update-ref new old |
Prevents stale overwrite | Low |
| Several local refs form one state | update-ref --stdin transaction |
Coordinate locks/expected values | More protocol complexity |
| Need local movement evidence | --create-reflog + useful -m |
Explicit local audit/recovery context | Finite/local retention |
| Many mostly stable refs | Backend-aware Git optimization | Reduce ref storage overhead | Do not parse storage directly |
| Several remote refs must publish together | push --atomic if server supports |
Remote all-or-none request | Capability/server policy dependent |
17. Knowledge check
Question 1. Does
core.logAllRefUpdates=true guarantee permanent
reflog history?
Question 2. Why use a custom
refs/automation/* namespace?
Question 3. Why should scripts not parse packed-refs?
Question 4. What happens if one guarded update in a prepared update-ref transaction cannot lock/match?
Question 5. Does git push --atomic always work
against every remote?
18. Summary
Ref design should specify namespace ownership, expected-old concurrency checks, reflog intent, storage abstraction, and local-versus-remote transaction boundaries. Current Git offers powerful transactional and hook interfaces, but production policy must verify the deployed Git version and server/hosting capabilities.
Authoritative references
git-config
git-update-ref
githooks reference-transaction
git-push atomic option
git-refs
reftable
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.