Chapter 05Lesson 02~115 minutes

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.

git loggit rev-listTwo-dot/three-dotMerge parents

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.

12. Observe author and committer dates deliberately

Create one disposable commit with a historical author date while letting Git assign the current committer date:

Git Bash / Bash / zsh

git switch -c date-demo
printf "date demonstration
" > date.txt
git add date.txt
GIT_AUTHOR_DATE="2025-01-15T10:00:00+00:00" git commit -m "Demonstrate author and committer dates"
git show --no-patch --format=fuller HEAD

PowerShell

git switch -c date-demo
Set-Content date.txt 'date demonstration'
git add date.txt
$env:GIT_AUTHOR_DATE='2025-01-15T10:00:00+00:00'
git commit -m "Demonstrate author and committer dates"
Remove-Item Env:GIT_AUTHOR_DATE
git show --no-patch --format=fuller HEAD

The author and committer timestamps should differ. The commit OID also depends on these metadata fields, so changing them creates a different object.

13. Challenge — answer questions, not commands

Without copying a sequence, determine:

  1. Which commits were unique to the feature branch immediately before the merge?
  2. Which commits were unique to trunk?
  3. What changed on the feature side since the merge base?
  4. What is the second parent of the merge commit?
  5. 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.

Next

Turn mechanics into a durable team history policy

Lesson 3 covers commit templates, message conventions, aliases/display defaults, introductory diff algorithm choices, mailmap identity normalization, and policies for commit size and generated changes.

Authoritative references

 git-commit
 git-show
 git-log
 git-rev-list
 gitrevisions

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.