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.
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
-
After
hash-object -w, willgit statusconsider the file tracked? -
After
update-index --cacheinfo, what four fields shouldls-files --stageshow? -
After
commit-tree, will the unborntrunkbranch automatically point to the new commit? - 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, stage0, and pathservice.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/showrecognizes the manually built history. -
The alternate index added
parallel.confwithout adding it to the live index. -
No raw
.git/indexor.git/refs/*file was manually overwritten.
15. Cleanup/restore
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?
git ls-files --stage after
clearing/not setting GIT_INDEX_FILE showed only the live entries.
Question 4. A script reads default
git status prose and splits on spaces. What should
replace it?
git status --porcelain=v2 -z with a NUL-safe parser.
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.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.