Chapter 18Lesson 05~175 minutes

Checkpoint Lab — Plumbing Commands, Index Internals, cat-file, hash-object, and commit-tree

Checkpoint the entire plumbing pipeline from an empty repository: write a blob, stage it, write a tree, create a root commit, attach a temporary branch, verify with porcelain, and repeat index work through an alternate index.

CheckpointBlob-to-refMachine evidenceAlternate index

Learning objectives

  • Predict and verify each state change from object creation through ref attachment.
  • Construct a root commit manually without relying on a pre-existing HEAD commit.
  • Verify manually built objects/history using standard porcelain commands.
  • Build a second hypothetical tree through GIT_INDEX_FILE and prove live staging is unchanged.
  • Map each plumbing operation to the porcelain workflow layer it helps explain.

1. Checkpoint scenario — construct a named commit from an empty repository

You will begin with an empty disposable repository whose trunk branch is unborn. You will create one blob, stage its mode/object/path tuple using plumbing, write a tree, create a root commit, inspect it, and only then create a temporary branch ref. Finally, you will prove the result with normal porcelain and repeat an index change through an alternate index.

2. Predictions before any mutation

  1. After hash-object -w, will git status consider the file tracked?
  2. After update-index --cacheinfo, what four fields should ls-files --stage show?
  3. After commit-tree, will the unborn trunk branch automatically point to the new commit?
  4. If an alternate index adds a second path, should the live index suddenly contain that path?

3. Create the empty disposable repository

Git Bash, Bash, or zsh

mkdir git-plumbing-checkpoint
cd git-plumbing-checkpoint
git init -b trunk repo
cd repo
git config user.name "Plumbing Checkpoint"
git config user.email "plumbing-checkpoint@example.invalid"

git status --short --branch
git rev-parse --absolute-git-dir
git rev-parse --git-path index
git rev-parse --show-object-format
git show-ref || true

PowerShell

New-Item -ItemType Directory git-plumbing-checkpoint | Out-Null
Set-Location git-plumbing-checkpoint
git init -b trunk repo
Set-Location repo
git config user.name "Plumbing Checkpoint"
git config user.email "plumbing-checkpoint@example.invalid"

git status --short --branch
git rev-parse --absolute-git-dir
git rev-parse --git-path index
git rev-parse --show-object-format
git show-ref

Expected: the repository has no commit refs yet. HEAD is a symbolic branch state for the explicitly created trunk name, but no commit object is reachable from it.

4. Write one blob and verify prediction 1

printf "service=checkout\n" > service.conf
BLOB=$(git hash-object -w service.conf)

git cat-file -t "$BLOB"
git cat-file -s "$BLOB"
git cat-file -p "$BLOB"
git status --short
git ls-files --stage

Expected: cat-file proves the blob exists. Status still reports service.conf as untracked and the index listing is empty. Prediction 1 is therefore “no.”

5. Stage the blob/path tuple using plumbing and verify prediction 2

git update-index --add --cacheinfo "100644,$BLOB,service.conf"

git ls-files --stage -- service.conf
git status --short
git diff --cached -- service.conf

The stage listing should have the form:

100644 <BLOB> 0	service.conf

The four conceptual fields are mode, object ID, stage, and path. The working-tree file matches the blob, so there is one staged addition and no unstaged content difference.

6. Write and inspect the tree object

TREE=$(git write-tree)

git cat-file -t "$TREE"
git cat-file -p "$TREE"
git ls-tree "$TREE"

The tree should contain one 100644 blob ... service.conf entry. The tree is now an object, but there is still no commit or branch tip pointing to it.

7. Create a root commit object and verify prediction 3

COMMIT=$(printf 'checkpoint: manual root commit\n' |
  git commit-tree "$TREE")

git cat-file -t "$COMMIT"
git cat-file -p "$COMMIT"
git show-ref || true
git rev-parse --verify trunk 2>/dev/null || echo "trunk still has no commit"

Expected: the commit has a tree line but no parent line because it is a root commit. The branch still has no commit. commit-tree created an object; it did not move the branch.

8. Produce a compact object inventory

printf '%s\n' "$BLOB" "$TREE" "$COMMIT" |
git cat-file --batch-check='%(objectname) %(objecttype) %(objectsize)'

You should see exactly one blob, one tree, and one commit in the three requested rows. This is a useful machine-oriented sanity check before naming history.

9. Predict the ref change, inspect once more, then attach a temporary branch

Prediction: after the next update-ref, refs/heads/plumbing-demo will resolve to COMMIT; the trunk branch remains unborn.

git cat-file -p "$COMMIT"
git update-ref refs/heads/plumbing-demo "$COMMIT"

git rev-parse plumbing-demo
git show-ref --heads
git log --all --decorate --oneline --max-count=5

Compare the printed ref OID with the captured COMMIT. This is the point where the previously unnamed commit becomes reachable from a normal branch ref.

10. Verify the manually constructed commit using normal porcelain

git switch plumbing-demo
git status --short --branch
git log -1 --format=fuller
git show --stat --oneline HEAD
git diff HEAD -- service.conf
git ls-files --stage

Expected: the worktree is clean on plumbing-demo; the commit is visible through log/show; the index contains the same stage-0 tuple; and there is no working-tree difference for service.conf.

11. Map each plumbing step to the porcelain concept it exposed

Manual checkpoint step Repository layer Porcelain workflow normally coordinating it
hash-object -w Object database: blob Content hashing/storage during git add
update-index --cacheinfo Index path tuple git add
write-tree Tree object snapshot Commit preparation
commit-tree Commit object git commit
update-ref Reference Branch/ref update coordinated by porcelain

The mapping is conceptual. Porcelain can perform additional checks, hooks, configuration handling, reflog/ref coordination, and user-facing behavior.

12. Repeat index work through an alternate file

Git Bash, Bash, or zsh

ALT_INDEX="$PWD/alternate.index"
GIT_INDEX_FILE="$ALT_INDEX" git read-tree "$TREE"

printf "parallel=true\n" > parallel.conf
PARALLEL_BLOB=$(git hash-object -w parallel.conf)

GIT_INDEX_FILE="$ALT_INDEX" \
  git update-index --add --cacheinfo "100644,$PARALLEL_BLOB,parallel.conf"

GIT_INDEX_FILE="$ALT_INDEX" git ls-files --stage
ALT_TREE=$(GIT_INDEX_FILE="$ALT_INDEX" git write-tree)
git cat-file -p "$ALT_TREE"

git ls-files --stage
git status --short

PowerShell

$ALT_INDEX = Join-Path (Get-Location) 'alternate.index'
$env:GIT_INDEX_FILE = $ALT_INDEX
git read-tree $TREE

Set-Content parallel.conf 'parallel=true'
$PARALLEL_BLOB = git hash-object -w parallel.conf
git update-index --add --cacheinfo "100644,$PARALLEL_BLOB,parallel.conf"

git ls-files --stage
$ALT_TREE = git write-tree
git cat-file -p $ALT_TREE
Remove-Item Env:GIT_INDEX_FILE

git ls-files --stage
git status --short

Verify prediction 4: the alternate index/tree contains parallel.conf, but the live index still contains only service.conf. Normal status sees parallel.conf as untracked.

13. Save machine-readable evidence without parsing human status prose

Git Bash / Bash / zsh

git status --porcelain=v2 -z > ../status-v2.bin
git ls-files --stage -z > ../index-stage.bin
printf '%s\n' "$BLOB" "$TREE" "$COMMIT" |
  git cat-file --batch-check='%(objectname) %(objecttype) %(objectsize)' \
  > ../objects.txt

The two -z files are binary record streams because NUL terminators are deliberate. A production parser should read them as bytes/records rather than line-oriented text.

14. Verification checklist

  • The repository was explicitly initialized with branch name trunk; no default-branch assumption was used.
  • The object format was inspected rather than assumed to be fixed-length SHA-1.
  • The blob existed before any index entry referenced it.
  • The live index stage-0 tuple has mode 100644, the captured blob ID, stage 0, and path service.conf.
  • The tree contains that blob/path entry.
  • The root commit contains the tree and no parent.
  • No branch ref moved until git update-ref refs/heads/plumbing-demo ....
  • Porcelain switch/status/log/show recognizes the manually built history.
  • The alternate index added parallel.conf without adding it to the live index.
  • No raw .git/index or .git/refs/* file was manually overwritten.

15. Cleanup/restore

Copy the small evidence files elsewhere if you want to retain them. Then confirm the disposable parent path before deletion.

Git Bash / Bash / zsh

cd ../..
pwd
rm -rf git-plumbing-checkpoint

PowerShell

Set-Location ../..
Get-Location
Remove-Item -Recurse -Force git-plumbing-checkpoint

16. Knowledge check

Question 1. The blob exists but git status still reports the path untracked. Which layer is missing?

Question 2. Why did commit-tree not make trunk point to the root commit?

Question 3. Which command proved that the alternate index did not alter the live index?

Question 4. A script reads default git status prose and splits on spaces. What should replace it?

Question 5. Why is manually replacing .git/index unnecessary for this experiment?

17. What Chapter 18 adds to a production Git operating model

You can now reason explicitly about the blob → index → tree → commit → ref pipeline, identify which state layer a tool is reading or mutating, isolate hypothetical index construction from live staging, validate repository discovery/environment, and choose machine-oriented interfaces that survive difficult filenames and evolving Git internals.

18. Chapter checkpoint summary

The power of plumbing comes from precision, not from being “more advanced.” A low-level command that touches the wrong index or ref is more dangerous than a well-designed porcelain workflow. Production automation should therefore pair plumbing with explicit repository identity, alternate state where practical, robust input framing, and independent verification.

Next chapter

Refs, Reflogs, Packed Refs, Symbolic References, and Reference Transactions

Chapter 19 takes the final step of this chapter—the ref update—and studies it as a concurrency-sensitive naming layer, including reflogs, packed refs, symbolic refs, expected-old-value checks, and transactional updates.

Authoritative references

 git-hash-object
 git-update-index
 git-write-tree
 git-commit-tree
 git-update-ref
 git-status porcelain formats
 git-ls-files

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.