Checkpoint Lab — Git Object Model: Blobs, Trees, Commits, Tags, and the Object Database
Checkpoint the Git object model by creating three snapshots, proving object reuse, walking every commit/tree/blob edge, inspecting an annotated tag, and validating an object-format-aware provenance graph.
Learning objectives
- Create a reproducible three-commit history with controlled changed and unchanged content.
- Capture and type-check commit, tree, blob, and annotated-tag object IDs.
- Prove blob reuse and commit parent ancestry with Git commands.
- Draw and verify every reference/object edge in the repository graph.
- Produce an object-format-aware source provenance report and clean up safely.
1. Checkpoint scenario — prove the graph rather than memorizing it
You will create three commits containing two files.
stable.txt remains unchanged throughout;
changing.txt changes in commits 2 and 3. You will then
walk from HEAD to each commit, tree, and blob, create
an annotated tag, and verify every edge in a final graph.
2. Predictions before mutation
Write your answers before running the lab:
-
Will changing only
changing.txtcreate a new blob forstable.txt? - Will commit 2 have the same OID as commit 1 if most files are unchanged?
- When an annotated tag is created at commit 3, will the tag ref point directly to the commit or to a tag object?
- If the branch advances to a fourth commit later, should the existing annotated tag automatically move?
3. Setup and preflight
Git Bash, Bash, or zsh
mkdir git-object-checkpoint
cd git-object-checkpoint
git init -b trunk
git config user.name "Object Checkpoint"
git config user.email "checkpoint@example.invalid"
git status --short --branch
git rev-parse --show-object-format=storage
PowerShell
New-Item -ItemType Directory git-object-checkpoint | Out-Null
Set-Location git-object-checkpoint
git init -b trunk
git config user.name "Object Checkpoint"
git config user.email "checkpoint@example.invalid"
git status --short --branch
git rev-parse --show-object-format=storage
4. Commit 1 — establish the first tree
Git Bash, Bash, or zsh
printf "never changes\n" > stable.txt
printf "one\n" > changing.txt
git status --short
git add stable.txt changing.txt
git diff --staged
git commit -m "Snapshot one"
C1=$(git rev-parse HEAD)
T1=$(git rev-parse HEAD^{tree})
S1=$(git rev-parse HEAD:stable.txt)
X1=$(git rev-parse HEAD:changing.txt)
printf "C1=%s\nT1=%s\nS1=%s\nX1=%s\n" "$C1" "$T1" "$S1" "$X1"
PowerShell
Set-Content stable.txt 'never changes'
Set-Content changing.txt 'one'
git status --short
git add stable.txt changing.txt
git diff --staged
git commit -m "Snapshot one"
$C1 = git rev-parse HEAD
$T1 = git rev-parse HEAD^{tree}
$S1 = git rev-parse HEAD:stable.txt
$X1 = git rev-parse HEAD:changing.txt
$C1; $T1; $S1; $X1
5. Commit 2 — change only one blob
Git Bash, Bash, or zsh
printf "two\n" > changing.txt
git status --short
git diff
git add changing.txt
git commit -m "Snapshot two"
C2=$(git rev-parse HEAD)
T2=$(git rev-parse HEAD^{tree})
S2=$(git rev-parse HEAD:stable.txt)
X2=$(git rev-parse HEAD:changing.txt)
PowerShell
Set-Content changing.txt 'two'
git status --short
git diff
git add changing.txt
git commit -m "Snapshot two"
$C2 = git rev-parse HEAD
$T2 = git rev-parse HEAD^{tree}
$S2 = git rev-parse HEAD:stable.txt
$X2 = git rev-parse HEAD:changing.txt
Now verify prediction 1: S1 and S2 should
match; X1 and X2 should differ.
6. Commit 3 — repeat the pattern
Git Bash, Bash, or zsh
printf "three\n" > changing.txt
git add changing.txt
git commit -m "Snapshot three"
C3=$(git rev-parse HEAD)
T3=$(git rev-parse HEAD^{tree})
S3=$(git rev-parse HEAD:stable.txt)
X3=$(git rev-parse HEAD:changing.txt)
test "$S1" = "$S2" && test "$S2" = "$S3"
test "$X1" != "$X2" && test "$X2" != "$X3"
PowerShell
Set-Content changing.txt 'three'
git add changing.txt
git commit -m "Snapshot three"
$C3 = git rev-parse HEAD
$T3 = git rev-parse HEAD^{tree}
$S3 = git rev-parse HEAD:stable.txt
$X3 = git rev-parse HEAD:changing.txt
$S1 -eq $S2 -and $S2 -eq $S3
$X1 -ne $X2 -and $X2 -ne $X3
The equality checks should report success/true for the stable blob, while every changed-content blob should have a different OID.
7. Verify commit ancestry, not timestamp guesses
git log --graph --decorate --oneline --all
git rev-parse HEAD
git rev-parse HEAD^
git rev-parse HEAD~2
git cat-file -p HEAD
git cat-file -p HEAD^
The parent line of commit 3 should name commit 2; the parent line of commit 2 should name commit 1. This proves the ancestry edges directly.
8. Verify every tree edge
git ls-tree HEAD~2
git ls-tree HEAD~1
git ls-tree HEAD
Each tree should list the same blob OID for
stable.txt and a different blob OID for
changing.txt. The root tree OID itself changes because
at least one entry changed.
9. Add an annotated tag to commit 3
git status --short --branch
git tag -a checkpoint-v1 -m "Object checkpoint release"
TAGOID=$(git rev-parse checkpoint-v1^{tag})
# PowerShell: $TAGOID = git rev-parse checkpoint-v1^{tag}
git cat-file -t checkpoint-v1
git cat-file -p checkpoint-v1
git rev-parse checkpoint-v1^{commit}
git rev-parse HEAD
The tag type should be tag; the peeled commit should
equal HEAD. This is different from a lightweight tag,
where the tag ref directly names its target without a separate
annotated tag object.
10. Draw the graph you actually verified
flowchart TD H[HEAD] --> B[refs/heads/trunk] B --> C3[commit C3] C3 -->|parent| C2[commit C2] C2 -->|parent| C1[commit C1] C3 -->|tree| T3[tree T3] C2 -->|tree| T2[tree T2] C1 -->|tree| T1[tree T1] T1 -->|stable.txt| S[blob stable] T2 -->|stable.txt| S T3 -->|stable.txt| S T1 -->|changing.txt| X1[blob one] T2 -->|changing.txt| X2[blob two] T3 -->|changing.txt| X3[blob three] R[refs/tags/checkpoint-v1] --> A[annotated tag] A -->|object| C3
Verify each arrow using a Git command:
git symbolic-ref HEADfor HEAD → branch ref;git rev-parse trunkfor branch ref → C3;git cat-file -p C3for C3 → T3 and C2;git ls-tree C3for tree → blob edges;-
git rev-parse checkpoint-v1^{tag}andgit cat-file -p checkpoint-v1for tag ref → tag object → C3.
11. Type-check every captured object
Git Bash, Bash, or zsh
for oid in "$C1" "$C2" "$C3" "$T1" "$T2" "$T3" "$S1" "$X1" "$X2" "$X3" "$TAGOID"; do
printf "%s " "$oid"
git cat-file -t "$oid"
done
PowerShell
@($C1,$C2,$C3,$T1,$T2,$T3,$S1,$X1,$X2,$X3,$TAGOID) | ForEach-Object {
"$_ $(git cat-file -t $_)"
}
You should see three commits, three trees, four distinct blobs (one stable plus three changing versions), and one tag object.
12. Capture an object-format-aware provenance report
git rev-parse --show-object-format=storage
git rev-parse --verify HEAD
git rev-parse --short=12 HEAD
git show --no-patch --format=fuller HEAD
git show-ref --heads --tags
This report stores the full commit OID for machine provenance while also showing a Git-validated abbreviation suitable for humans.
13. Verification checklist
- Exactly three ordinary commits exist on
trunk. - Commit 3 points to commit 2; commit 2 points to commit 1.
-
The root-tree OIDs differ as
changing.txtchanges. -
The
stable.txtblob OID is identical in all three commits. -
The
changing.txtblob OID is different in all three commits. -
checkpoint-v1is an annotatedtagobject. - The tag object's target commit equals commit 3.
- The repository's object format was discovered from Git rather than inferred from OID length.
-
No direct file editing occurred inside
.git/objects.
14. Cleanup
Git Bash, Bash, or zsh
cd ..
pwd
rm -rf git-object-checkpoint
PowerShell
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-object-checkpoint
15. Knowledge check
Question 1. Why did all three root tree OIDs change even though
stable.txt did not?
changing.txt entry points to a new blob each time,
the tree contents—and therefore the tree OID—change.
Question 2. What proves that commit 3 is a descendant of commit 2?
parent line inside commit 3 points to commit 2.
Ancestry is encoded by parent edges, not inferred from date order.
Question 3. Why is the annotated tag object not the same object as commit 3?
Question 4. If the trunk branch moves later, does
checkpoint-v1 automatically move?
Question 5. A teammate stores only the first 7 characters of a commit ID in a deployment database. What is the design problem?
16. What Chapter 03 adds to a production Git operating model
You can now distinguish logical source identity from filenames,
branches, and physical storage. You can trace a release/source ref
to immutable objects, verify ancestry, reason about reuse and
retention, and avoid destructive “repairs” based on false
assumptions about .git/objects.
17. Chapter checkpoint summary
The object database is the foundation under everything that follows: Chapter 04's index/working-tree lifecycle, branch movement, merge ancestry, recovery, tags/releases, fetch/push transfer, repository maintenance, and provenance automation all manipulate or navigate this same object/reference graph.
Authoritative references
git-cat-file
git-ls-tree
git-rev-parse
git-tag
gitrepository-layout
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.