Git, CI/CD & GitOpsIV · Commit Graph and HistoryCommit Graph and History
History traversal — log options that actually change the graph walk
What you'll learn
- Explain how `git log` walks the DAG by default and how each flag changes the walk or the rendering
- Choose between `--topo-order` and `--date-order` for code review versus chronological output
- Use `--reverse` to walk from the oldest commit in a range to the newest, which is the order needed for replay and changelog generation
- Use `--follow` to track renames in the history of a single path
- Combine `--all`, `--decorate`, `--graph`, and `--oneline` to render the full DAG with every ref
- Filter by merge commits with `--merges` and `--no-merges` to separate trunk history from feature-branch history
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git log is the most-used Git command, and most of its flags do not
change what commits are returned - they change either the walk
strategy or the rendering. This lesson collects the flags that
actually matter for an infrastructure team: which change the graph
walk (which commits are visited and in what order), and which only
change the formatting (one line per commit, decorations, etc.). The
distinction is what separates a useful log from a misleading one.
The default walk
Without flags, git log starts at HEAD and walks parent edges back
to the root, emitting one commit per line. The walk is depth-first
along the first-parent edge, with non-first-parent branches visited
when their merge commit is reached. This is the default behaviour,
and it produces output that looks linear but is in fact a
first-parent-biased DAG walk.
git log --oneline
# 8a3f9d2 bump terraform module to v1.4.0
# 4d2c8e0 Merge branch 'feature/iam-rotation' into main
# 9f3c1d7 rotate iam keys
# 7a1b2c3 add rotation cron
# 6f4e5a6 initial commit
The middle two lines are the feature branch, reached because the
walk visited the merge commit and then descended into the second
parent. Without --first-parent, the default walk visits every
reachable commit; with --first-parent, only the trunk chain is
visited.
—graph, —oneline, —decorate, —all: the rendering four
These four flags do not change the walk’s reachability - they change what is shown about each commit and what starting points the walk considers.
--graph: renders the DAG edges as ASCII characters. Every commit is positioned according to its parents; branches and merges are visible as pipe-and-slash characters.--oneline: renders each commit as a single line: abbreviated OID plus subject. Replaces the verbose default with a one-line format.--decorate: annotates each commit with the refs that point at it (branches, tags, HEAD). Without it, refs are invisible.--all: extends the walk to include every ref’s tip, not just HEAD. Without it, only the current branch is enumerated.
git log --graph --oneline --decorate --all
# * 8a3f9d2 (HEAD -> main, tag: v1.4.0) bump terraform module to v1.4.0
# * 4d2c8e0 (origin/main) Merge branch 'feature/iam-rotation' into main
# |\
# | * 9f3c1d7 (origin/feature/iam-rotation) rotate iam keys
# | * 7a1b2c3 add rotation cron
# |/
# * 6f4e5a6 initial commit
This four-flag combination is the canonical “show me the DAG” command. Every ref is visible (HEAD, branches, tags, remote tracking), every commit is on one line, and the parent edges are drawn.
—topo-order versus —date-order
The walk strategy determines the order in which commits are
emitted. --topo-order is the default; it ensures parents always
appear before children. --date-order orders by committer
timestamp instead.
# Topological order: parents before children, regardless of date
git log --oneline --topo-order
# Date order: by committer timestamp, parents may appear after children
git log --oneline --date-order
The difference matters when a commit’s children have older
committer timestamps than the parent - which happens after a
rebase, a backport, or a squash merge from a long-lived branch.
--topo-order keeps the DAG readable: a parent is always shown
above its children. --date-order keeps the timeline readable: a
commit from 2023 appears before a commit from 2024, even if the
2024 commit is a child.
For code review, --topo-order is almost always better: the
reviewer reads parents before children, which matches the causal
order. For audit trails and chronological event logs,
--date-order is appropriate. The default is --topo-order.
—reverse: walk from root to tip
By default, git log walks from the starting commit toward the
root (newest to oldest). --reverse flips the walk to oldest to
newest. The DAG is the same; only the emission order changes.
# Default: newest first
git log --oneline
# 8a3f9d2 bump terraform module to v1.4.0
# 6f4e5a6 initial commit
# Reverse: oldest first
git log --oneline --reverse
# 6f4e5a6 initial commit
# 8a3f9d2 bump terraform module to v1.4.0
--reverse is the correct flag for changelog generation, replay
scripts, and any other “process commits in chronological order”
operation. Without it, the script will process the newest commit
first and may apply changes in reverse order, producing a
different result.
—follow: track renames
Git tracks renames by content similarity, not by file path. By
default, git log -- <path> only shows commits that touched the
path’s current name. After a rename, history appears to be cut
off. --follow extends the walk back through renames, treating
the path as a moving target.
# Default: stops at the rename
git log --oneline -- ansible/roles/webserver/tasks/main.yml
# With --follow: walks through the rename
git log --oneline --follow -- ansible/roles/webserver/tasks/main.yml
--follow is restricted: it works on a single path, and it
follows renames only along the first-parent chain by default
(this is an implementation detail of the rename-detection
heuristic). For multi-path history, use git log --diff-filter=R --name-only to find the rename commit and reconstruct the
history manually.
—merges and —no-merges: filter by commit type
--merges includes only merge commits; --no-merges excludes
them. Combined with --first-parent, these flags separate the
trunk’s commits into “ordinary commits” and “merge commits”.
# Every merge commit reachable from main
git log --oneline --merges main
# Every ordinary commit on main's trunk (first-parent only, no merges)
git log --oneline --first-parent --no-merges main
--first-parent --no-merges is the canonical “what work landed
on the trunk, excluding the merge events themselves” view. It is
the input to release notes that do not list merge commits
explicitly and to deployment counters that count “work commits”
rather than “trunk steps”.
flowchart LR
M1["merge M1"] --> T1["trunk commit T1"]
M1 --> F1["feature commit F1"]
M2["merge M2"] --> T2["trunk commit T2"]
M2 --> F2["feature commit F2"]
T1 --> T2
In the diagram, git log --first-parent --no-merges walks the
trunk chain (T1, T2) and skips both merge commits and feature
commits. git log --merges walks both merge commits. git log main walks everything.
Combining flags
Flags compose. The combination git log --graph --oneline --decorate --all --topo-order --reverse shows every commit, in
topological order reversed to chronological, drawn as a graph
with decorations. This is rarely useful interactively but is the
baseline for “process every commit in chronological order with
its branch context” scripts.
# All-in-one: every commit, every ref, decorated, graphed
git log --graph --oneline --decorate --all
# Replay every commit in chronological order, no graph noise
git log --oneline --reverse --no-merges main
# Find every rename commit reachable from any ref
git log --diff-filter=R --name-status --oneline --all
Production discipline
- Choose flags by question, not by habit. “What is on the
trunk?” is
--first-parent. “What is on either side of the divergence?” isA...B. “What is on this branch but not on that?” isA..B. “Show me the graph” is--graph --oneline --decorate --all. - Use
--reversefor replay and changelog generation. A script that processes commits without--reversemay apply changes in the wrong order. - Use
--followfor single-file history across renames. It is the only flag that does rename detection within the walk. - Always set
--mergesor--no-mergesdeliberately. A script that does not specify either captures both kinds and may produce surprising output.
Cross-course references
- GitOps with Argo CD - Part VII (Diffing) uses
git log --oneline --reverseto enumerate the manifests that must be replayed in order when reconciling a drifted cluster. - CI/CD Pipeline Patterns - Part II (TriggerLogic) uses
--first-parent --no-mergesto identify the commits that should trigger a build, excluding merge commits that have no new content of their own. - Ansible for Production Sysadmins - Part XXXIX (HistoryAudit)
uses
--followto reconstruct the history of a renamed role across organisational restructuring.
Quiz
Knowledge check · 4 questions
Q1. Which combination of `git log` flags renders the full DAG with every ref, draws parent edges as ASCII, and shows each commit on a single line?
Q2. `--reverse` changes which commits `git log` walks, not just the order in which they are emitted.
Q3. Which `git log` flag tracks a file through renames by extending the walk to treat the path as a moving target, and what is its main limitation?
Q4. Construct the `git log` invocation that produces the commit list for a chronological replay of all trunk work since the last release, excluding merge commits.
An infrastructure team needs a script that replays every Terraform apply on the trunk in chronological order, skipping the merge commits themselves (which carry no new apply content). The trunk is `main`, the last release tag is `v1.3.0`, and the script must enumerate OIDs only.
Passing score: 75%. Answers are checked in this browser.