Chapter 19Lesson 03~125 minutes

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.

ConfigurationRef policyReference hookAtomic push

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.

Do not equate creation with durable audit retention. Reflog expiry/garbage-collection policy is separate, and reflogs remain local repository state.

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.

Version-sensitive feature: do not require this syntax in automation until the deployed Git fleet is verified. The mandatory labs in this chapter use 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.

Next

Diagnose ref failures without editing ref storage by hand

Lesson 4 engineers stale-writer races, missing reflogs, packed-ref visibility errors, remote-tracking staleness, and a deliberately corrupted packed-refs file in an isolated backup-first lab.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.