Chapter 19Lesson 01~115 minutes

Refs, Reflogs, Packed Refs, Symbolic References, and Reference Transactions: Concepts, Architecture, and Mental Model

Build the mental model for Git reference namespaces, symbolic HEAD, reflogs, loose versus packed ref storage, guarded compare-and-swap-like updates, and multi-ref transactions.

RefsReflogsSymbolic refsTransactions

Learning objectives

  • Distinguish immutable objects from movable reference names.
  • Explain local branches, tags, remote-tracking refs, custom namespaces, and symbolic refs.
  • Treat reflogs as finite local histories of ref movement rather than shared project history.
  • Understand loose/packed/reftable storage as implementation beneath logical ref APIs.
  • Explain expected-old guarded updates and multi-ref transaction safety.

1. Objects are immutable; automation still needs movable names

Chapter 18 ended with a manually created commit and then used git update-ref to give that commit a branch name. That final step is the starting point of this chapter. Git objects are immutable snapshots and history records. Day-to-day workflows need references, or refs: names that can move from one object ID to another as work advances.

This naming layer looks simple until two processes try to move the same name, a ref is packed into a different storage representation, a symbolic ref such as HEAD is detached, or recovery depends on a reflog that may not exist. Production automation therefore needs a stronger mental model than “a branch is a file containing a hash.”

2. Inspect logical refs before thinking about storage files

git status --short --branch
git show-ref --head
git for-each-ref --format='%(refname) %(objectname) %(objecttype)'
git symbolic-ref -q HEAD
git reflog list
git rev-parse --show-ref-format

Expected observations: show-ref reports full ref names and object IDs; for-each-ref lets you select stable fields; symbolic-ref reports the branch target of HEAD when attached; reflog list reports only refs that actually have reflogs; and --show-ref-format identifies the repository's ref-storage backend where supported.

3. Ref namespaces separate different kinds of names

A ref is a Git name stored in a namespace. Common namespaces include:

Namespace Meaning Typical target
refs/heads/* Local branch tips Commit
refs/tags/* Tags Any Git object; often a tag object or commit
refs/remotes/* Local remote-tracking refs Commit copied from a remote namespace by fetch
refs/notes/* Git notes namespaces Notes history
refs/replace/* Replacement-object mapping Replacement object
refs/automation/* Example custom namespace for team tooling Policy-defined object, usually commit

The custom namespace is a convention, not a special built-in branch type. Naming it explicitly helps tooling avoid pretending that every operational pointer is a developer branch.

4. A remote-tracking ref is local state, not the live server ref

refs/remotes/origin/trunk is a ref in your local repository that records what a fetch or related operation last learned about the remote. It is not a network link and does not automatically change when the server changes. This distinction matters during incident or release automation: inspect the remote directly or fetch deliberately before treating a remote-tracking ref as current.

5. A symbolic ref points to another ref rather than directly to an object

The most familiar symbolic ref is HEAD. While attached to a branch, HEAD names a branch ref such as refs/heads/trunk; that branch ref then names a commit.

Attached HEAD versus detached HEAD
flowchart LR
HA[HEAD symbolic ref] -->|ref target| B[refs/heads/trunk]
B -->|object ID| C[Commit C]
HD[Detached HEAD] -->|object ID directly| D[Commit D]

In the top path, a new commit can advance the branch ref while HEAD continues to point symbolically at that branch. In the bottom path, detached HEAD contains an object ID directly; git symbolic-ref -q HEAD therefore exits non-zero.

6. Use git symbolic-ref instead of treating HEAD as a filesystem trick

git symbolic-ref HEAD
git symbolic-ref --short HEAD

Git's symbolic-ref API exists because the implementation needs to be portable and because ref storage can evolve. Directly opening or replacing .git/HEAD is unnecessary for normal tooling.

7. Loose refs and packed refs are two storage representations of the same logical names

In the traditional files ref backend, a recently created or updated ref may be stored as a loose file under the ref hierarchy. git pack-refs can move eligible logical refs into a packed representation for efficiency. A later update can make a branch loose again.

This means “scan .git/refs” is not a complete ref query even in the traditional backend. Current Git also has a reftable ref-storage format. Therefore scripts should ask Git for refs rather than parsing storage files.

8. show-ref is the simple logical-ref inventory

git show-ref
git show-ref --branches
git show-ref --tags
git show-ref --verify refs/heads/trunk

Use an exact full ref with --verify when automation needs to prove a specific name exists. The command is designed to hide whether the ref is loose, packed, or stored by another supported backend.

9. for-each-ref is the structured ref query engine

git for-each-ref \
  --sort=refname \
  --format='%(refname) %(objectname) %(objecttype)' \
  refs/heads refs/tags

Unlike a hand-written directory walk, this interface understands Git's logical ref database and exposes selectable fields. It can filter by reachability, points-at relationships, namespace, and more.

10. A reflog is local history of ref movement, not project history

A reflog records recent updates to a ref in one local repository. For example, a branch reflog can show old branch-tip values; the HEAD reflog additionally records branch switches. Reflogs support selectors such as HEAD@{2}.

git reflog show HEAD
git reflog show refs/heads/trunk
git reflog exists refs/heads/trunk

A reflog is not guaranteed to exist for every ref, every repository, or every server. Its retention is finite. It is recovery evidence, not a permanent audit database.

11. Reflogs do not automatically synchronize between clones

Fetch and push exchange objects and selected refs according to refspecs. They do not merge your local reflog history into another clone. If an incident report relies on a reflog entry, record which repository produced that evidence.

12. One ref update should be atomic; a guarded update should also verify what you observed

Suppose automation reads refs/heads/release at object A and decides to move it to B. Another process may move the ref to C before the first process writes. A blind write would overwrite C. A guarded update supplies the expected old object ID:

git update-ref refs/heads/release "$B" "$A"

Git performs the update only if the current ref value still matches A. This is analogous to compare-and-swap: “move it only if nobody changed it since I observed it.”

13. A reference transaction groups several guarded ref changes

Guarded transaction
flowchart TD
Q[Queue updates with expected old values] --> P[Prepare]
P -->|all refs lock and match| K[Commit ref updates]
P -->|one lock/value fails| A[Abort all queued updates]
K --> V[Verify through ref APIs]

git update-ref --stdin supports transactional commands such as start, prepare, and commit. If the required refs cannot all be locked with matching expected values, the transaction is not committed. Individual ref updates are atomic, although current documentation warns that a concurrent reader may still observe only a subset while a multi-ref transaction becomes visible.

14. Current Git exposes a reference-transaction hook, but it is executable policy

The current hook contract can notify executable local policy when reference transactions are preparing, prepared, committed, or aborted. It can receive each old value, new value, and full ref name on standard input, and current documentation also includes symbolic-ref transaction support.

Version and trust boundary: exact hook lifecycle behavior has evolved. A hook is executable local/server configuration, not a portable ref-object property. Verify the Git version and hook policy where automation runs.

15. DevOps connection — named source is a concurrency-sensitive deployment input

Release tools, CI coordinators, deployment systems, and maintenance automation often move refs to publish source state. The safe design is to read logical refs through Git, retain the observed old value, perform a guarded or transactional update, add meaningful reflog messages where appropriate, and verify the final ref. Filesystem overwrites of ref storage bypass those protections.

16. Knowledge check

Question 1. Is origin/trunk the live branch on the remote server?

Question 2. What does an attached HEAD usually point to?

Question 3. Why can scanning only .git/refs miss valid refs?

Question 4. What extra safety does the old-object argument to git update-ref provide?

Question 5. Why should reflog evidence identify its repository?

17. Summary

Refs are the movable naming layer over immutable objects. Symbolic refs add one level of naming, reflogs record local ref movement, packed refs are a storage optimization rather than a second logical namespace, and guarded transactions prevent stale automation from silently overwriting concurrent changes.

Next

Operate refs in a disposable repository

Lesson 2 creates branch/tag/remote refs, inspects attached and detached HEAD, performs guarded create/update/delete operations, reads reflogs, packs refs, and proves porcelain behavior is unchanged.

Authoritative references

 git-show-ref
 git-for-each-ref
 git-symbolic-ref
 git-reflog
 git-update-ref
 git-pack-refs

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.