Git Object Model: Blobs, Trees, Commits, Tags, and the Object Database: Guided Hands-On Workflow and Core Operations
Trace real Git objects in a disposable repository with hash-object, cat-file, ls-tree, show, and rev-parse; prove blob reuse, parent ancestry, and annotated-tag indirection.
Learning objectives
- Trace a branch name to its commit, root tree, and individual blob objects.
- Use git hash-object safely with and without object-database writes.
- Prove unchanged file content reuses the same blob across multiple commits.
- Inspect commit parent links and compare them with revision selectors.
- Create and inspect an annotated tag object and peel it to its target commit.
1. Build a disposable object-model repository
This lab intentionally uses ordinary porcelain commands to create
valid objects before using plumbing commands to inspect them. The
only plumbing write demonstration, hash-object -w,
comes later and is isolated.
Git Bash, Bash, or zsh
mkdir git-object-lab
cd git-object-lab
git init -b trunk
git config user.name "Object Lab Learner"
git config user.email "object-lab@example.invalid"
printf "stable line\n" > stable.txt
printf "version one\n" > changing.txt
git status --short
git add stable.txt changing.txt
git diff --staged
git commit -m "Create first snapshot"
PowerShell file-creation alternative
New-Item -ItemType Directory git-object-lab | Out-Null
Set-Location git-object-lab
git init -b trunk
git config user.name "Object Lab Learner"
git config user.email "object-lab@example.invalid"
Set-Content stable.txt 'stable line'
Set-Content changing.txt 'version one'
git status --short
git add stable.txt changing.txt
git diff --staged
git commit -m "Create first snapshot"
2. Resolve the branch name to a commit object
git rev-parse --symbolic-full-name HEAD
git rev-parse HEAD
git cat-file -t HEAD
git cat-file -s HEAD
git cat-file -p HEAD
The symbolic name identifies the current branch ref.
rev-parse HEAD resolves that naming layer to an object
ID. cat-file -t should report commit. The
size is the uncompressed object-content size, not necessarily the
bytes occupied on disk.
3. Follow the commit's tree edge
git rev-parse HEAD^{tree}
git cat-file -t HEAD^{tree}
git ls-tree HEAD
git cat-file -p HEAD^{tree}
git ls-tree HEAD asks Git to show the tree associated
with the commit-ish. Each row carries a mode, object type, object
ID, and path name. This is where the filenames connect to blob
objects.
4. Resolve a path directly to its blob
git rev-parse HEAD:stable.txt
git cat-file -t HEAD:stable.txt
git cat-file -s HEAD:stable.txt
git cat-file -p HEAD:stable.txt
The revision/path form asks Git to resolve
stable.txt inside the tree reachable from
HEAD. The blob content should be the stored text, but
the blob itself does not know that the tree called it
stable.txt.
5. hash-object can compute an ID without writing
git hash-object stable.txt
git rev-parse HEAD:stable.txt
If no clean/smudge attribute changes the stored representation,
these should match because both identify the blob content Git would
store for the file. Without -w,
hash-object computes and reports an ID but does not add
a new object to the object database.
6. Create a second snapshot while leaving one file unchanged
Git Bash, Bash, or zsh
printf "version two\n" > changing.txt
git status --short
git diff
git add changing.txt
git diff --staged
git commit -m "Change only changing file"
git log --oneline --decorate -2
PowerShell alternative
Set-Content changing.txt 'version two'
git status --short
git diff
git add changing.txt
git diff --staged
git commit -m "Change only changing file"
git log --oneline --decorate -2
7. Prove blob reuse across commits
git rev-parse HEAD~1:stable.txt
git rev-parse HEAD:stable.txt
git rev-parse HEAD~1:changing.txt
git rev-parse HEAD:changing.txt
Expected: the two stable.txt OIDs are
identical because the stored content is identical. The
changing.txt OIDs differ. This is the key correction to
the “every commit physically duplicates every file” misconception:
commits refer to trees, and trees can reuse objects.
8. Prove the commit-parent edge
git rev-parse HEAD
git rev-parse HEAD^
git cat-file -p HEAD
The parent line printed from the second commit should
equal git rev-parse HEAD^. That object is the first
commit, not its tree.
9. Create and inspect an annotated tag object
git status --short --branch
git tag -a v1.0 -m "Object model checkpoint"
git show-ref --tags
git cat-file -t v1.0
git cat-file -p v1.0
git rev-parse v1.0^{tag}
git rev-parse v1.0^{commit}
git rev-parse HEAD
An annotated tag creates a tag object. The tag ref
names that tag object; the tag object contains an
object line naming its target. Because this tag targets
HEAD, v1.0^{commit} should resolve to the
same commit as HEAD.
10. What git show does with different names
git show --no-patch --format=fuller HEAD
git show --no-patch v1.0
git show HEAD:stable.txt
git show is a convenient porcelain viewer. It can
follow a tag to show tagged content, or display a blob selected by
revision/path syntax. cat-file is the lower-level
microscope when you need explicit type/size/content queries.
11. One controlled plumbing write: create an unreachable blob
hash-object -w writes an object without creating a
tree/commit/ref that reaches it. That makes it a useful
demonstration of “object exists” versus “object belongs to visible
history.”
Git Bash, Bash, or zsh
printf "orphan experiment\n" > orphan.txt
git hash-object orphan.txt
git hash-object -w orphan.txt
git status --short
git cat-file -t "$(git hash-object orphan.txt)"
PowerShell
Set-Content orphan.txt 'orphan experiment'
$oid = git hash-object orphan.txt
git hash-object -w orphan.txt
git status --short
git cat-file -t $oid
The working-tree file remains untracked and the written blob is not automatically added to the index or a commit. Chapter 22 will revisit maintenance; do not prune it aggressively here.
12. Challenge — choose the edge, not the memorized command sequence
Starting only from the name v1.0, produce:
- the tag object's type and OID;
- the target commit OID;
- the root tree OID of that commit;
- the blob OID for
stable.txt; - the blob content.
Use the object graph to decide when you need rev-parse,
cat-file, or ls-tree. Verify each result's
type before trusting it.
13. Cleanup
The lab is intentionally disposable. Return to the parent directory and remove it only after checking the path.
Git Bash, Bash, or zsh
cd ..
pwd
rm -rf git-object-lab
PowerShell
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-object-lab
14. Knowledge check
Question 1. Why did the stable.txt blob OID remain
identical across two commits?
Question 2. What does git hash-object do
differently when -w is added?
-w it computes/reports the object ID. With
-w it also writes the object into the object
database.
Question 3. Why does git cat-file -t v1.0 report
tag for an annotated tag?
^{commit} reaches the target commit.
Question 4. Did writing an orphan blob with
hash-object -w stage or commit
orphan.txt?
15. Summary
You traced a normal ref → commit → tree → blob path, proved unchanged blobs are reused, verified parent ancestry, and inspected an annotated tag's extra object layer. You also saw that a written object can exist without being reachable from visible history.
Authoritative references
git-hash-object
git-cat-file
git-ls-tree
git-rev-parse
git-tag
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.