Chapter 03Lesson 02~95 minutes

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.

cat-filels-treerev-parseAnnotated tags

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

Disposable repository only. 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:

  1. the tag object's type and OID;
  2. the target commit OID;
  3. the root tree OID of that commit;
  4. the blob OID for stable.txt;
  5. 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?

Question 3. Why does git cat-file -t v1.0 report tag for an annotated tag?

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.

Next

Make object-format and storage choices explicit

Lesson 3 explains SHA-1 versus SHA-256 repository format, collision-safe abbreviation, loose versus packed storage, reachability/retention, and why object IDs provide integrity but not signer trust.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.