git log: Master Commit History Navigation and Filtering
Master git log formatting, filtering, searching history, and navigating commit history effectively for version control debugging and auditing.
Introduction
git log is your window into the repository’s history. Every commit, every branch point, every merge — it is all there, waiting to be queried. But the default git log output is just the tip of the iceberg. Git’s log command supports dozens of formatting options, powerful filters, and search capabilities that turn your commit history into a searchable database.
Most developers use git log as a simple list of recent commits. Power users use it to find exactly when a bug was introduced, track who changed a specific function, visualize branch topology, audit compliance, and generate release notes. The difference is not in the tool — it is in knowing which flags to combine.
This guide covers every important form of git log, from basic usage to advanced filtering, searching, and visualization. Mastering these techniques makes you a Git historian who can answer any question about your repository’s past. For understanding commit creation, see git commit.
When to Use / When Not to Use
Use git log when:
- Finding when a bug was introduced using
git log -Sorgit log -G - Understanding the history of a specific file or directory
- Reviewing what changed between releases or tags
- Auditing who made what changes and when
- Visualizing branch structure and merge patterns
- Generating changelogs or release notes
Use specialized tools instead when:
- You need to see the actual code changes — use
git difforgit show - You need to interactively walk through history — use
git reflog - You need to find which commit broke something — use
git bisect - You need to see file-level changes — use
git log --followfor renames
Core Concepts
git log walks the commit graph starting from the specified commit(s) (default: HEAD) and outputs information about each commit. It follows parent pointers, so it naturally traverses the entire history reachable from the starting point. The output format, filtering, and sorting are all configurable.
graph TD
A[HEAD] --> B[git log reads commit graph]
B --> C[Applies filters]
C --> D[Formats output]
D --> E[Displays results]
C --> C1[By author]
C --> C2[By date range]
C --> C3[By message content]
C --> C4[By file path]
C --> C5[By code changes]
D --> D1[One line]
D --> D2[Full message]
D --> D3[With diff]
D --> D4[Custom format]
D --> D5[Graph visualization]
The Commit Graph
graph LR
A[A] --> B[B]
B --> C[C]
C --> D[D]
C --> E[E]
D --> F[F]
E --> F
git log walks from HEAD (F) backwards through parents, visiting each commit exactly once.
Architecture or Flow Diagram
git log Filter Pipeline
graph LR
A[All Commits<br/>from HEAD] --> B{Author Filter}
B -->|match| C{Date Filter}
B -->|skip| A
C -->|match| D{Message Filter}
C -->|skip| A
D -->|match| E{File Filter}
D -->|skip| A
E -->|match| F{Code Search}
E -->|skip| A
F -->|match| G[Format Output]
F -->|skip| A
Output Format Options
graph TD
A[git log] --> B[Default<br/>Full commit info]
A --> C[--oneline<br/>Hash + message]
A --> D[--stat<br/>File change summary]
A --> E[--graph<br/>ASCII branch graph]
A --> F[--format<br/>Custom template]
A --> G[--patch<br/>With diff content]
B --> H[Best for: detailed review]
C --> I[Best for: quick overview]
D --> J[Best for: impact assessment]
E --> K[Best for: branch visualization]
F --> L[Best for: custom reports]
G --> M[Best for: code review]
Step-by-Step Guide / Deep Dive
Basic Usage
# Default log output
git log
# Compact one-line per commit
git log --oneline
# Show only the last N commits
git log -5
git log -n 10
Formatting Options
# Pretty formats
git log --oneline # abc1234 feat: add auth
git log --short # Short hash + author + date + message
git log --medium # Default format
git log --full # Full message with full headers
git log --fuller # Both author and committer info
# Custom format with placeholders
git log --format="%h %s" # Hash + subject
git log --format="%h %an %ar %s" # Hash + author + relative date + subject
git log --format="%C(yellow)%h %C(reset)%s" # Colored output
git log --format="%H%n%an%n%ae%n%ad%n%s" # Multi-line detailed
# Common placeholders:
# %H - Full commit hash
# %h - Abbreviated commit hash
# %an - Author name
# %ae - Author email
# %ad - Author date (respects --date format)
# %ar - Author date, relative
# %s - Subject (first line of message)
# %b - Body
# %D - Ref names (branches, tags)
Visualizing Branch Structure
# ASCII graph of branch topology
git log --oneline --graph
# Show all branches
git log --oneline --graph --all
# Show all branches with decoration (branch/tag labels)
git log --oneline --graph --all --decorate
# The ultimate alias
git log --oneline --graph --all --decorate --color
Output:
* abc1234 (HEAD -> main) feat: add user profile
| * def5678 (feature/auth) fix: handle token expiry
| * ghi9012 feat: add JWT authentication
|/
* jkl3456 (tag: v1.0.0) Release v1.0.0
* mno7890 Initial commit
Filtering by Author
# Commits by a specific author
git log --author="Jane"
git log --author="jane@example.com"
# Commits NOT by a specific author
git log --author="Jane" --invert-grep
# Case-insensitive author search
git log --author="jane" -i
Filtering by Date
# Commits since a date
git log --since="2026-01-01"
git log --after="2026-01-01"
# Commits before a date
git log --until="2026-03-01"
git log --before="2026-03-01"
# Relative dates
git log --since="2 weeks ago"
git log --since="3 months ago"
git log --after="yesterday"
# Date range
git log --since="2026-01-01" --until="2026-03-31"
Filtering by Message Content
# Commits whose message contains a keyword
git log --grep="authentication"
git log --grep="fix" --oneline
# Case-insensitive search
git log --grep="AUTH" -i
# Multiple keywords (OR by default)
git log --grep="auth" --grep="login"
# Multiple keywords (AND)
git log --all-match --grep="auth" --grep="token"
# Regular expression search
git log --grep="^feat:" --oneline
Filtering by File Path
# Commits that touched a specific file
git log -- src/app.py
# Commits in a directory
git log -- src/
# Commits that touched any of multiple files
git log -- src/app.py src/utils.py
# Follow file across renames
git log --follow -- src/app.py
# Show only commits that changed files matching a pattern
git log -- "*.py"
Searching Code Changes
# Commits that added or removed a specific string
git log -S "function_name"
git log -S "API_KEY"
# Commits that added or removed a regex pattern
git log -G "def\s+\w+\("
# The difference: -S looks for exact string count changes, -G looks for regex matches in diff
# -S "foo" finds commits where the number of "foo" occurrences changed
# -G "foo.*bar" finds commits where any diff line matches the regex
Comparing Ranges
# Commits reachable from feature but not from main
git log main..feature
# Commits reachable from either but not both (symmetric difference)
git log main...feature
# Commits between two tags
git log v1.0.0..v2.0.0
# Commits since a tag
git log v1.0.0..HEAD
Statistics and Summaries
# Show files changed per commit
git log --stat
# Show number of files changed
git log --shortstat
# Show only file names
git log --name-only
# Show file names with status (A/M/D/R)
git log --name-status
# Custom stat format
git log --stat --stat-width=80
Advanced Combinations
# All commits by Jane in the last month touching Python files
git log --author="Jane" --since="1 month ago" -- "*.py"
# Find when a function was introduced
git log -S "def calculate_total" --oneline -- src/
# Visualize feature branch history
git log --oneline --graph --all --grep="feat:"
# Audit all changes to a sensitive file
git log --follow --format="%h %an %ad %s" --date=short -- src/config.py
# Generate changelog between releases
git log v1.0.0..v2.0.0 --oneline --no-merges
Production Failure Scenarios + Mitigations
| Scenario | Impact | Mitigation |
|---|---|---|
| Cannot find when a bug was introduced | Extended debugging time, delayed fixes | Use git log -S or git log -G to search for specific code changes |
| Missing commits in log output | Incorrect assumptions about what was deployed | Verify with git log --all to include all branches, not just current |
| Merge commits cluttering the log | Hard to read linear history | Use git log --no-merges to hide merge commits |
| Large repository log is too slow | Developer productivity impact | Use --since, --max-count, or shallow clones to limit scope |
| Cannot track file across renames | Lost history for refactored files | Use git log --follow -- filename to follow renames |
| Wrong date format in audit reports | Compliance issues, incorrect timelines | Use --date=iso or --date=short for consistent date formatting |
Trade-offs
| Approach | Advantages | Disadvantages | When to Use |
|---|---|---|---|
--oneline | Compact, readable, great for overview | Minimal detail | Quick history review, presentations |
--stat | Shows scope of each commit | No actual code changes | Impact assessment, release notes |
--graph | Visual branch structure | ASCII art can be hard to read in wide histories | Understanding branch topology |
--format | Fully customizable | Requires learning format placeholders | Custom reports, automation |
--patch | Shows actual code changes | Verbose, slow for large histories | Code review, debugging |
-S / -G | Finds specific code changes | May return false positives | Bug investigation, code archaeology |
Implementation Snippets
Essential Log Aliases
# Add these to your ~/.gitconfig
[alias]
lg = log --oneline --graph --all --decorate --color
lgs = log --oneline --graph --all --decorate --color --stat
lgf = log --oneline --graph --all --decorate --color --format="%h %C(yellow)%an%C(reset) %C(green)%ar%C(reset) %s"
who = shortlog -sn --no-merges
yesterday = log --since="yesterday" --oneline
changes = log --stat --oneline -10
Finding a Specific Change
# When was this function added?
git log -S "def authenticate_user" --oneline -- src/auth.py
# When was this line last modified?
git log -L 42,42:src/app.py
# Who last touched this file?
git log -1 --format="%an %ad" -- src/app.py
# Show the exact commit that introduced a line
git blame src/app.py
Generating Reports
# Commits per author this month
git log --since="1 month ago" --format="%an" | sort | uniq -c | sort -rn
# Files changed most frequently
git log --name-only --format="" | sort | uniq -c | sort -rn | head -20
# Average commits per day
git log --format="%ad" --date=short | sort | uniq -c
# Release changelog
git log v1.2.0..v1.3.0 --oneline --no-merges | sed 's/^/- /' > CHANGELOG.md
Audit Trail
# Complete audit of a sensitive file
git log --follow --format="Commit: %h%nAuthor: %an <%ae>%nDate: %ad%nMessage: %s%n---" --date=iso -- src/secrets.py
# All commits by a specific person with dates
git log --author="john@example.com" --format="%h %ad %s" --date=short --since="2026-01-01"
# Commits that touched production config
git log --format="%h %ad %an: %s" --date=short -- config/production/
Observability Checklist
- Logs: Use
git log --oneline --since="1 week ago"for weekly team standup updates - Metrics: Track commit frequency with
git log --format="%ad" --date=short | sort | uniq -c - Traces: Use
git log --follow -- filenameto trace a file’s entire history across renames - Alerts: Monitor for commits to sensitive paths:
git log --since="1 day ago" -- config/ secrets/ - Audit: Generate compliance reports with
git log --format="%h %an %ad %s" --date=iso - Health: Review
git log --oneline --graph --allmonthly to ensure branch hygiene - Validation: Verify release contents with
git log tag1..tag2 --oneline --no-merges
Security/Compliance Notes
- Log output may contain sensitive information: Commit messages, author emails, and file paths visible in
git logmay include internal details. Be careful when sharing log output externally - Audit compliance:
git logwith proper formatting provides a complete audit trail. Regulated industries should archive log output for compliance records - Author identity verification: Without signed commits,
git logauthor information can be spoofed. Usegit log --show-signatureto verify commit authenticity - Sensitive file access tracking:
git log --follow -- sensitive-filetracks every access to sensitive files, which is valuable for security audits - Data retention:
git logshows the complete history. If sensitive data was committed and later removed, it is still visible in the log. Usegit filter-branchorgit filter-repoto purge
Common Pitfalls / Anti-Patterns
- Forgetting
--allwhen searching:git logonly shows commits reachable from HEAD. If the commit you are looking for is on another branch, you needgit log --all - Confusing
..and...in ranges:A..Bmeans “commits in B but not in A.”A...Bmeans “commits in either A or B but not both.” Using the wrong operator gives wrong results - Not using
--followfor renamed files: Without--follow,git log -- filenamestops at the rename. The file’s history before the rename is hidden - Overlooking
--no-merges: Merge commits clutter the log with “Merge branch X into Y” messages. Use--no-mergeswhen you want to see actual work commits - Reading log without
--stat: The log message tells you what the author intended;--stattells you what they actually changed. Always compare the two - Assuming log order is chronological:
git logshows commits in reverse chronological order by default, but with--topo-orderor--date-orderthe order changes. Be aware of which ordering you are using
Quick Recap Checklist
-
git log --onelinefor compact commit listing -
git log --graph --all --decoratefor branch visualization -
git log --author="name"to filter by author -
git log --since="date"and--until="date"for date ranges -
git log --grep="keyword"to search commit messages -
git log -S "string"to find commits that added/removed specific code -
git log -G "regex"to find commits matching a regex in diffs -
git log -- filenameto see history of a specific file -
git log --follow -- filenameto follow file across renames -
git log A..Bshows commits in B but not in A -
git log --statshows files changed per commit -
git log --no-mergeshides merge commits -
git log --format="custom"for custom output formatting - Use
git blameto see who last modified each line
Interview Q&A
git log -S "string" (pickaxe) finds commits where the number of occurrences of the exact string changed — meaning the string was either added or removed. git log -G "regex" finds commits where any line in the diff matches the regular expression. The key difference: -S counts occurrences (it would NOT find a commit that just reformats a line containing the string), while -G matches any diff line against the regex. Use -S for finding when a function or variable was introduced; use -G for finding when a pattern appeared in any changed line.
Use git log --oneline --graph --all --decorate. The --graph flag draws an ASCII representation of the branch topology, --all includes all branches (not just the current one), --decorate adds branch and tag labels next to commits, and --oneline keeps it compact. This produces a visual map showing where branches diverged, merged, and where tags are placed. Save it as an alias like git lg for frequent use.
There are several approaches. Use git log -S "suspicious_code" to find commits that added or removed the specific code. Use git log --oneline -- src/file.py to see all commits touching the affected file. Use git blame src/file.py to see who last modified each line. For systematic investigation, use git bisect to binary-search through history between a known-good and known-bad commit. Combine git log --grep="keyword" to find commits related to the feature area.
git log main..feature (two dots) shows commits that are reachable from feature but NOT from main — essentially, what feature has that main does not. This is what would be merged into main. git log main...feature (three dots) shows commits that are reachable from either main or feature but NOT from both — the symmetric difference. This shows what each branch has that the other does not, which is useful for understanding how two branches have diverged. Two dots is far more commonly used.
Commit History Graph with Branches, Merges, and Tags
graph LR
A[A<br/>Initial] --> B[B<br/>feat: add auth]
B --> C[C<br/>feat: add API]
C --> D[D<br/>fix: auth bug]
C --> E[E<br/>feat: add UI]
D --> F[F<br/>Merge feature/auth]
E --> F
F --> G[G<br/>Release v1.0]
G --> H[H<br/>feat: add search]
H --> I[I<br/>HEAD, main]
- Branch points (C → D, E) show where work diverged
- Merge commits (F) combine parallel work streams
- Tags (G) mark significant milestones like releases
- HEAD (I) points to the current commit on the active branch
Production Failure: Lost Commits After Force Push
A developer force-pushes to main, overwriting 3 commits that a teammate had based their work on. The teammate’s branch now references commits that no longer exist on main. Their git pull fails with divergence errors, and hours of work appear “lost.”
Recovery with reflog:
# On the affected machine, find the lost commits
git reflog
# Output shows:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: feat: add payment processing
# ghi9012 HEAD@{2}: commit: fix: handle null response
# jkl3456 HEAD@{3}: commit: feat: add user preferences
# Recover the lost branch state
git branch recovery def5678
# Or reset back to where you were
git reset --hard def5678
Prevention: Never force-push to shared branches. Use --force-with-lease instead of --force. Enable branch protection rules on main to reject force-pushes entirely.
Trade-offs: Log Formats for Different Use Cases
| Format | Output Example | Best For | Readability |
|---|---|---|---|
--oneline | abc1234 feat: add auth | Quick overview, presentations, standups | High |
--graph | ASCII branch topology with * and | | Understanding merge history, branch cleanup | Medium |
--stat | File names with +/- bars | Impact assessment, release notes | Medium |
--name-only | Just file paths, one per line | Scripts, CI/CD, automation | Low (raw) |
--name-status | M src/app.py, A new_file.py | Tracking file lifecycle (add/modify/delete) | Medium |
--format=custom | User-defined template output | Reports, audits, custom tooling | Variable |
--patch / -p | Full diff content per commit | Code review, debugging | Low (verbose) |
--shortstat | 3 files changed, 45 insertions(+), 12 del | Commit size metrics, velocity tracking | High |
Implementation: Powerful Log Aliases for Daily Use
Add these to your ~/.gitconfig for instant access to the most useful log views:
[alias]
# The ultimate log visualization
lg = log --oneline --graph --all --decorate --color
# Log with file change statistics
lgs = log --oneline --graph --all --decorate --color --stat
# Log with author and relative date
lgf = log --oneline --graph --all --decorate --color --format="%h %C(yellow)%an%C(reset) %C(green)%ar%C(reset) %s"
# Who contributed most (by commit count)
who = shortlog -sn --no-merges
# What happened yesterday
yesterday = log --since="yesterday" --oneline --no-merges
# Last 10 commits with file stats
changes = log --stat --oneline -10
# Find when a string was added or removed
find = log -S
# Log for a specific file across renames
filelog = log --follow --oneline --
# Commits by me this week
mine = log --author="$(git config user.name)" --since="1 week ago" --oneline
Usage examples:
git lg # Full branch visualization
git lgf # See who did what and when
git find "API_KEY" # Find when API_KEY was added/removed
git filelog src/auth.py # Full history of auth.py
git mine # My recent contributions
Resources
- Git Log Documentation — Complete reference
- Pro Git — Viewing the Commit History — Official guide
- Pretty Formats — Format placeholder reference
- Git Bisect Documentation — Binary search through history
- git commit — Understanding commit creation
Category
Related Posts
Git Bisect for Bug Hunting: Binary Search Through Commit History
Master git bisect to find the exact commit that introduced a bug using binary search. Automate bug hunting with scripts and handle complex scenarios.
Git Blame and Annotate: Line-by-Line Code Attribution
Master git blame for line-by-line code attribution, understanding code history, finding when code changed, and using blame effectively for code comprehension.
Git Reflog and Recovery: Your Safety Net for Destructive Operations
Master git reflog to recover lost commits, undo destructive operations, and understand Git's safety net. Learn recovery techniques for reset, rebase, and merge disasters.