Commits, Diffs, History Inspection, Revision Ranges, and Message Discipline: Guided Hands-On Workflow and Core Operations
Build and interrogate a disposable divergent history with commit, show, log, diff, rev-parse, rev-list, dotted ranges, parent selectors, path disambiguation, and a simple merge commit.
Learning objectives
- Create reviewable commits from a verified index and inspect the resulting commit object/view.
- Compare working tree, index, HEAD, and arbitrary commit endpoints.
- Use rev-parse for object resolution and rev-list/log for commit-set traversal.
- Explain two-dot and three-dot semantics for traversal and their different meaning in git diff.
- Inspect a merge commit through first and second parent selectors without relying on advanced merge internals.
1. Build a disposable history laboratory
This lab creates a small divergent graph and later a merge commit. It uses only local branches; no hosting account or remote is required.
Git Bash, Bash, or zsh
mkdir git-history-lab
cd git-history-lab
git init -b trunk
git config user.name "History Lab"
git config user.email "history@example.invalid"
printf "service v1
" > service.txt
printf "runbook v1
" > runbook.md
git add service.txt runbook.md
git diff --staged
git commit -m "Create service baseline"
git log --oneline --decorate
PowerShell file-creation alternative
New-Item -ItemType Directory git-history-lab | Out-Null
Set-Location git-history-lab
git init -b trunk
git config user.name "History Lab"
git config user.email "history@example.invalid"
Set-Content service.txt 'service v1'
Set-Content runbook.md 'runbook v1'
git add service.txt runbook.md
git diff --staged
git commit -m "Create service baseline"
git log --oneline --decorate
2. Reconfirm working tree → index → commit causality
Git Bash / Bash / zsh
printf "service v2 local
" > service.txt
git status --short
git diff -- service.txt
git add -- service.txt
git diff -- service.txt
git diff --staged -- service.txt
git commit --dry-run
git commit -m "Update service baseline"
git show --stat --oneline HEAD
git show HEAD
The ordinary diff disappears after staging because working tree and
index agree. The staged diff then shows the proposed commit.
git commit --dry-run is a useful final preflight: it
reports what would be committed and what would remain outside the
commit without creating history.
3. Resolve names before using them in comparisons
git rev-parse --verify HEAD
git rev-parse --verify HEAD^
git rev-parse --short=12 HEAD
git rev-list --count HEAD
rev-parse resolves a revision expression to an object
ID; rev-list traverses commit ancestry and is often
better for machine-oriented set/count questions than parsing
decorated human log output.
4. Create deliberate divergence
BASE=$(git rev-parse HEAD)
git switch -c feature/reporting
printf "reporting enabled
" > reporting.txt
git add reporting.txt
git commit -m "Add reporting capability"
FEATURE1=$(git rev-parse HEAD)
git switch trunk
printf "runbook v2: monitor reporting queue
" > runbook.md
git add runbook.md
git commit -m "Document reporting queue monitoring"
TRUNK1=$(git rev-parse HEAD)
git log --graph --decorate --oneline --all
PowerShell users can capture OIDs with
$BASE = git rev-parse HEAD and equivalent assignments.
The important state is graph shape: trunk and
feature/reporting now each contain a commit the other
cannot reach.
5. Two-dot range: commits unique to the right side
git log --oneline trunk..feature/reporting
git rev-list --oneline trunk..feature/reporting
git log --oneline feature/reporting..trunk
The first pair lists commits reachable from
feature/reporting but not from trunk.
Reversing the endpoints reverses the question.
6. Three-dot range: commits unique to either side
git log --left-right --graph --oneline trunk...feature/reporting
git rev-list --left-right --oneline trunk...feature/reporting
The left/right markers show which tip can reach each unique commit. Commits reachable from both sides—including their common base—are excluded.
7. Compare arbitrary endpoint trees
git diff trunk feature/reporting
git diff trunk..feature/reporting
git diff trunk...feature/reporting
The first two compare the endpoint snapshots directly. The three-dot
diff instead compares the merge base to
feature/reporting, answering “what did the feature side
introduce since divergence?” Run the reverse
git diff feature/reporting...trunk to ask the analogous
trunk-side question.
8. Disambiguate a path from a revision
git log --oneline -- runbook.md
git diff HEAD^ HEAD -- runbook.md
git show HEAD:runbook.md
The -- boundary makes it explicit that
runbook.md is a path, not a revision name. This habit
becomes essential when a branch/tag and path can share a token.
9. Add another feature commit and inspect one-dot parent selectors
git switch feature/reporting
printf "reporting enabled
format=json
" > reporting.txt
git add reporting.txt
git commit -m "Emit reporting output as JSON"
git show --no-patch --format=fuller HEAD
git show --stat HEAD^
git rev-parse HEAD^
git rev-parse HEAD~1
For this ordinary one-parent commit, HEAD^ and
HEAD~1 resolve to the same first parent. The
distinction matters once merge commits have multiple parents.
10. Create a merge only to inspect parent choices
Chapter 06 teaches merge mechanics in depth. Here the merge is intentionally conflict-free so we can focus on the resulting commit graph.
git switch trunk
git status --short
git merge --no-ff feature/reporting -m "Merge reporting feature"
MERGE=$(git rev-parse HEAD)
git show --no-patch --format=raw HEAD
git rev-parse HEAD^1
git rev-parse HEAD^2
git diff HEAD^1 HEAD
git diff HEAD^2 HEAD
The merge commit stores two parent OIDs. HEAD^1 is the
first parent (the branch you were on); HEAD^2 is the
second parent (the merged tip in this simple case). Comparing the
merge against each parent answers two different questions.
11. Do not assume one default merge patch tells the whole story
git show --no-patch --pretty=fuller HEAD
git show --stat HEAD
git show -m --oneline HEAD
Merge diff presentation has special modes and can be surprising. When the question is “what differs from parent 1?” or “what differs from parent 2?”, explicit parent comparisons are often easier for beginners than interpreting a combined merge diff.
13. Challenge — answer questions, not commands
Without copying a sequence, determine:
- Which commits were unique to the feature branch immediately before the merge?
- Which commits were unique to trunk?
- What changed on the feature side since the merge base?
- What is the second parent of the merge commit?
- Which commits touched
runbook.md?
Choose among rev-list, log,
diff, rev-parse, and path-limited history
based on the shape of each question.
14. Cleanup
Return to the parent directory and remove only the disposable repository after confirming the path.
Git Bash / Bash / zsh
cd ..
pwd
rm -rf git-history-lab
PowerShell
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-history-lab
15. Knowledge check
Question 1. What does git commit --dry-run give
you before a commit?
Question 2. In the divergent graph, what does
trunk..feature/reporting mean to
rev-list?
Question 3. What does
git diff trunk...feature/reporting compare?
Question 4. Why inspect a merge with HEAD^1 and
HEAD^2?
Question 5. Which defines ancestry: timestamps or parent OIDs?
16. Summary
You used commits as graph nodes, diffs as explicit state
comparisons, rev-list as a commit-set engine, and
parent selectors to interrogate a merge without conflating branch
history with patch presentation.
Authoritative references
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.