Learning Objectives
By the end of this module, you will be able to:
- Format
git logoutput with--oneline,--graph,--all,--decorate, and custom--pretty=format:strings - Filter history by author, date, commit message, and content changes using
--author,--since,--until,--grep,-S, and-G - Scope log output to specific files and directories with path filtering
- Use range notation (
main..feature,main...feature) to explore commit reachability - Navigate merge-heavy histories with
--first-parentand--merges/--no-merges - Search code within any commit using
git grepandgit log -L(line-range history) - Create reusable log aliases for daily workflows
1. git log Basics and Display Formats
git log is your primary tool for reading commit history. Out of the box, it shows each commit's hash, author, date, and message — but its real power comes from the dozens of formatting and filtering options.
Default Output
git logcommit abc1234567890abcdef1234567890abcdef123456
Author: Alice Smith <alice@example.com>
Date: Mon Mar 15 14:22:30 2024 +0100
feat: add user authentication
commit def5678901234567890abcdef1234567890abcde
Author: Bob Jones <bob@example.com>
Date: Fri Mar 12 09:15:00 2024 -0500
fix: resolve login redirect loop
--oneline — Compact Single-Line Format
git log --onelineabc1234 feat: add user authentication
def5678 fix: resolve login redirect loop
ghi9012 refactor: extract validation helpers
Shows abbreviated SHA + first line of commit message. This is the most commonly used format for scanning history.
--graph — Visualize Branch Topology
git log --oneline --graph* f5a3b2c Merge branch 'feature/auth'
|\
| * abc1234 feat: add user authentication
| * def5678 feat: add login form
|/
* ghi9012 refactor: extract validation helpers
* jkl3456 initial commit
The ASCII art shows branch/merge structure. Essential for understanding how branches were integrated.
--all — Show All Branches
git log --oneline --graph --allWithout --all, log only shows commits reachable from HEAD (your current branch). With --all, it shows every branch, tag, and ref — giving you the complete picture of the repository.
--decorate — Show Branch and Tag Labels
git log --oneline --graph --all --decorate* f5a3b2c (HEAD -> main, origin/main) Merge branch 'feature/auth'
|\
| * abc1234 (origin/feature/auth) feat: add user authentication
| * def5678 feat: add login form
|/
* ghi9012 (tag: v1.0) refactor: extract validation helpers
--decorate adds branch names, tags, and HEAD indicators. Modern Git enables this by default.
The Classic Combo
git log --oneline --graph --all --decorateThis is so common that most developers alias it. You'll see it abbreviated as glog, gloga, or similar in shell configurations.
git show — Inspect a Single Commit
While git log lists commits, git show displays a single commit with its diff:
# Show the latest commit with its patch
git show
# Show a specific commit
git show abc1234
# Show without the diff (metadata only)
git show --no-patch abc1234
git show -s abc1234
# Show with stat summary instead of full diff
git show --stat abc12342. Built-In Format Presets
Git provides several preset formats via --format= (or --pretty=):
| Format | What It Shows |
|---|---|
oneline | SHA message (single line) |
short | SHA, author, message (no date) |
medium | SHA, author, date, message (default) |
full | SHA, author, committer, message |
fuller | SHA, author + date, committer + date, message |
raw | Internal Git object format |
git log --format=short -3
git log --format=full -3
git log --format=fuller -3Author vs. Committer
The full and fuller formats reveal something important: Git tracks both author and committer separately.
- Author — the person who originally wrote the change
- Committer — the person who applied the change to the repository
They're usually the same person. They differ when:
- Someone cherry-picks or rebases your commit (you're the author, they're the committer)
- Someone applies a patch you emailed (historical workflow)
- CI systems or maintainers merge contributions from external sources
commit abc1234
Author: Alice Smith <alice@example.com> ← wrote the code
AuthorDate: Mon Mar 15 14:22:30 2024 +0100
Commit: Bob Jones <bob@example.com> ← applied via cherry-pick
CommitDate: Tue Mar 16 09:00:00 2024 -0500
3. Custom Formats with --pretty=format:
For complete control over log output, use format placeholders:
git log --pretty=format:"%h %s (%an, %ar)"abc1234 feat: add user authentication (Alice Smith, 2 days ago)
def5678 fix: resolve login redirect loop (Bob Jones, 5 days ago)
Essential Format Placeholders
| Placeholder | Meaning | Example |
|---|---|---|
%H | Full commit hash | abc1234567890abcdef... |
%h | Abbreviated hash | abc1234 |
%T | Tree hash | def5678901234... |
%P | Parent hash(es) | ghi9012... jkl3456... |
%an | Author name | Alice Smith |
%ae | Author email | alice@example.com |
%ad | Author date | Mon Mar 15 14:22:30 2024 |
%ar | Author date (relative) | 2 days ago |
%ai | Author date (ISO format) | 2024-03-15 14:22:30 +0100 |
%cn | Committer name | Bob Jones |
%ce | Committer email | bob@example.com |
%cd | Committer date | Tue Mar 16 09:00:00 2024 |
%cr | Committer date (relative) | 1 day ago |
%s | Subject (first line of message) | feat: add user auth |
%b | Body (rest of commit message) | Multi-line text |
%d | Ref names (branches, tags) | (HEAD -> main, tag: v1.0) |
%D | Ref names (no wrapping parens) | HEAD -> main, tag: v1.0 |
%n | Newline |
Color in Custom Formats
git log --pretty=format:"%C(yellow)%h%C(reset) %C(blue)%an%C(reset) %s %C(green)(%ar)%C(reset)%C(red)%d%C(reset)"Available colors: red, green, blue, yellow, cyan, magenta, white, reset (back to default), bold, dim, ul (underline).
Practical Custom Format Examples
Release changelog style:
git log --pretty=format:"- %s (%an)" v1.0..v2.0- feat: add user authentication (Alice Smith)
- fix: resolve login redirect loop (Bob Jones)
- docs: update API reference (Carol Lee)
Commit audit trail:
git log --pretty=format:"%h | %ai | %-20an | %s"abc1234 | 2024-03-15 14:22:30 +0100 | Alice Smith | feat: add user auth
def5678 | 2024-03-12 09:15:00 -0500 | Bob Jones | fix: login redirect
JSON-ish output for scripting:
git log --pretty=format:'{"hash":"%H","author":"%an","date":"%ai","message":"%s"},'4. Filtering by Author and Date
Author Filter
# Commits by a specific author (substring match, case-insensitive)
git log --author="Alice"
# Combine with oneline for scanning
git log --oneline --author="alice@example.com"
# Multiple authors (OR logic)
git log --oneline --author="Alice\|Bob"The --author flag matches against both name and email using a regex pattern.
Date Filters: --since / --until
Git accepts remarkably natural date expressions:
# Relative dates
git log --since="2 weeks ago"
git log --since="3 days"
git log --until="1 month ago"
# Absolute dates
git log --since="2024-01-01"
git log --until="2024-06-30"
# Combined range
git log --since="2024-01-01" --until="2024-03-31"
# Natural language
git log --since="last Monday"
git log --since="yesterday"--after is a synonym for --since. --before is a synonym for --until.
Combining Filters
Filters are AND-combined by default:
# Alice's commits from the last month
git log --oneline --author="Alice" --since="1 month ago"
# Bob's commits between two dates
git log --oneline --author="Bob" --since="2024-01-01" --until="2024-03-31"5. Filtering by Commit Message and Content
--grep — Search Commit Messages
# Find commits mentioning "login"
git log --oneline --grep="login"
# Case-insensitive search
git log --oneline --grep="login" -i
# Regex patterns
git log --oneline --grep="fix\|bug\|patch"
# All branches
git log --oneline --grep="login" --all
# Invert match (commits NOT mentioning "WIP")
git log --oneline --grep="WIP" --invert-grep--grep with Multiple Patterns
By default, multiple --grep flags use OR logic. Use --all-match for AND:
# Commits mentioning "login" OR "auth"
git log --grep="login" --grep="auth"
# Commits mentioning "login" AND "auth" (both in the message)
git log --grep="login" --grep="auth" --all-match-S (Pickaxe) — Find When a String Was Added/Removed
# Find commits where "validateEmail" was added or removed
git log -S "validateEmail" --oneline
# With the actual diff
git log -S "validateEmail" -p
# Restrict to specific file types
git log -S "validateEmail" --oneline -- "*.js"Pickaxe counts the occurrences of the string in the old and new versions. If the count changes, the commit is included. This is how you find when a function/variable was introduced or deleted.
-G — Regex Search in Diffs
# Find commits where lines matching a regex were changed
git log -G "function\s+validate" --oneline
# Find TODO additions/removals
git log -G "TODO|FIXME" --oneline -p-G is broader than -S: it includes commits where any diff line matches the regex, even if the count didn't change (e.g., a line containing the term was modified).
6. Path Filtering
Restrict log output to commits that touched specific files or directories:
# Commits that modified a specific file
git log --oneline -- src/auth.js
# Commits that touched anything in a directory
git log --oneline -- src/components/
# Multiple paths
git log --oneline -- src/auth.js src/config.js
# Glob patterns
git log --oneline -- "*.test.js"
# Follow renames (for a single file)
git log --oneline --follow -- src/auth.jsThe -- separator ensures Git doesn't confuse file paths with branch names.
--follow — Track File Renames
Without --follow, if a file was renamed at some point, git log only shows history from the current name onward. --follow detects renames and continues showing history under the old name:
git log --oneline --follow -- src/utils/helpers.js
# Shows commits even from when it was called src/helpers.jsLimitation: --follow only works for a single file, not directories or glob patterns.
-L — Line-Range History
Track the history of specific lines in a file:
# History of lines 10-20 in auth.js
git log -L 10,20:src/auth.js
# History of a function (Git auto-detects function boundaries)
git log -L :validateEmail:src/auth.js
# From a line to end of function
git log -L 15,+10:src/auth.jsThe -L flag shows the evolution of those specific lines across commits — like git blame but showing every change, not just the latest.
7. Range Notation in git log
Range notation controls which commits to display based on branch reachability.
Two-Dot: main..feature
git log --oneline main..featureShows commits reachable from feature but NOT reachable from main — in other words, what feature has that main doesn't.
A --- B --- C (main)
\
D --- E --- F (feature)
git log main..feature → shows D, E, F
git log feature..main → shows C
This is the most common range: "What commits are on this branch that haven't been merged yet?"
Three-Dot: main...feature
git log --oneline main...featureShows commits reachable from either main OR feature, but NOT from both — the symmetric difference. These are the commits on both sides since they diverged.
A --- B --- C (main)
\
D --- E --- F (feature)
git log main...feature → shows C, D, E, F
Add --left-right to see which side each commit is on:
git log --oneline --left-right main...feature< abc1234 C (commit on main)
> def5678 F (commit on feature)
> ghi9012 E (commit on feature)
> jkl3456 D (commit on feature)
< = left side (main), > = right side (feature).
Important: .. and ... in git log vs. git diff
| Syntax | git log | git diff |
|---|---|---|
A..B | Commits reachable from B but not A | Compare snapshots at A and B |
A...B | Commits reachable from A or B but not both | Diff from merge-base to B |
They have completely different semantics between the two commands. This is one of Git's most confusing asymmetries.
8. Navigating Merge-Heavy Histories
Real-world repositories with many contributors and frequent merging can have complex, tangled graphs. These options help you cut through the noise.
--first-parent — Follow Only the Main Line
git log --oneline --first-parentIn a merge commit, the first parent is always the branch you were on when you ran git merge. --first-parent follows only these, skipping the merged-in branches. The result is a linear history of merges into the main branch:
Without --first-parent:
* f5a3b2c Merge feature/auth
|\
| * abc1234 feat: add login
| * def5678 feat: add form
|/
* ghi9012 Merge feature/api
|\
| * jkl3456 feat: add endpoint
|/
* mno7890 initial commit
With --first-parent:
* f5a3b2c Merge feature/auth
* ghi9012 Merge feature/api
* mno7890 initial commit
This is invaluable for repos using merge-based workflows (GitHub PRs, GitLab MRs) where you want to see what was merged and when, without the details of every feature branch.
--merges / --no-merges
# Show only merge commits
git log --oneline --merges
# Show only non-merge commits (skip merge commits)
git log --oneline --no-merges--ancestry-path
When using a range, --ancestry-path limits output to commits that are descendants of the first commit AND ancestors of the second:
# Commits directly on the path between v1.0 and v2.0
git log --oneline --ancestry-path v1.0..v2.0This filters out side branches that were merged but weren't on the direct line between the two points.
Limiting Output
# Show only the last N commits
git log -5
git log -n 5
# Skip the first N commits
git log --skip=10 -5
# Stop after N commits total
git log --max-count=209. git grep — Search Files at Any Point in History
While git log -S searches commit diffs for string changes, git grep searches the actual file contents of a specific snapshot.
Basic Usage
# Search current working tree
git grep "validateEmail"
# Search with line numbers
git grep -n "validateEmail"
# Case-insensitive
git grep -i "validateemail"
# Count matches per file
git grep -c "validateEmail"Why git grep Over Regular grep?
- Faster — it only searches tracked files (ignores
node_modules, build outputs, etc.) - History-aware — you can search any commit, not just the working tree
- Scope detection — it can show the enclosing function/scope
Searching a Specific Commit
# Search in the v1.0 tag
git grep "validateEmail" v1.0
# Search in a specific commit
git grep "validateEmail" abc1234
# Compare: current vs. old
git grep -c "TODO" HEAD
git grep -c "TODO" v1.0Advanced Options
# Show enclosing function/scope
git grep -p "validateEmail"
# Group results with headings (file name as header)
git grep --heading --break "validateEmail"
# Search with regex
git grep -e "validate.*Email" --and -e "return"
# Boolean combinations
git grep -e "error" --and -e "handler" -- "*.js"
# Invert match (lines NOT containing)
git grep -v "console.log" -- "*.js"Combining git grep with git log
# Find the commit that first introduced "validateEmail" in any file
git log --oneline --all -S "validateEmail"
# Then see how it was used in that commit
git grep "validateEmail" abc123410. Creating Useful Log Aliases
The most productive Git users have a set of well-crafted log aliases. Here are battle-tested examples.
Essential Aliases
# Compact graph log (the everyday workhorse)
git config --global alias.lg "log --oneline --graph --decorate"
# Full graph with all branches
git config --global alias.lga "log --oneline --graph --decorate --all"
# Log with author and relative date
git config --global alias.ll "log --pretty=format:'%C(yellow)%h%C(reset) %s %C(cyan)(%ar)%C(reset) %C(blue)<%an>%C(reset)%C(red)%d%C(reset)'"
# Log with full date
git config --global alias.lf "log --pretty=format:'%C(yellow)%h%C(reset) %C(green)%ai%C(reset) %s %C(blue)<%an>%C(reset)%C(red)%d%C(reset)'"
# What did I do today?
git config --global alias.today "log --oneline --since='midnight' --author='$(git config user.name)'"
# What changed between this branch and main?
git config --global alias.ahead "log --oneline main..HEAD"
git config --global alias.behind "log --oneline HEAD..main"Oh My Zsh Git Plugin Aliases
If you use Oh My Zsh, the git plugin provides these log aliases out of the box:
| Alias | Command | Description |
|---|---|---|
glog | git log --oneline --graph --decorate | Compact graph |
gloga | git log --oneline --graph --decorate --all | Full graph |
glol | git log --graph --pretty='...' | Graph with author + date |
glola | git log --graph --pretty='...' --all | Full graph with author + date |
glod | git log --graph --pretty='...' --date=short | Graph with short date |
glods | git log --graph --pretty='...' --date=iso | Graph with ISO date |
You can override these in your .zshrc to customize the format (e.g., remove parentheses around dates, angle brackets around author names).
Creating Git Config Aliases
Aliases defined in .gitconfig use the [alias] section:
[alias]
lg = log --oneline --graph --decorate
lga = log --oneline --graph --decorate --all
ll = log --pretty=format:'%C(yellow)%h%C(reset) %s %C(cyan)(%ar)%C(reset) %C(blue)<%an>%C(reset)%C(red)%d%C(reset)'
# Count commits by author
who = shortlog -sn --no-merges
# Show files changed in last commit
last = diff-tree --no-commit-id --name-only -r HEADCommand Reference
| Command | Description |
|---|---|
git log --oneline | One commit per line (short SHA + subject) |
git log --graph | ASCII art branch topology |
git log --all | Include all branches and tags |
git log --decorate | Show branch/tag labels on commits |
git log -n 5 | Limit to last 5 commits |
git log --pretty=format:"..." | Custom output format |
git log --author="name" | Filter by author (regex match) |
git log --since="2 weeks" | Commits after a date |
git log --until="2024-01-01" | Commits before a date |
git log --grep="pattern" | Search commit messages |
git log -S "string" | Commits that add/remove a string (pickaxe) |
git log -G "regex" | Commits with diff lines matching regex |
git log -- path/to/file | Commits touching a specific path |
git log --follow -- file | Follow renames for a single file |
git log -L 10,20:file | History of specific lines in a file |
git log main..feature | Commits on feature not on main |
git log main...feature | Commits on either but not both |
git log --left-right A...B | Show which side each commit is on |
git log --first-parent | Follow only first parent (main line) |
git log --merges | Show only merge commits |
git log --no-merges | Exclude merge commits |
git show <commit> | Display a single commit with its diff |
git show -s <commit> | Commit metadata only (no diff) |
git grep "pattern" | Search file contents in current tree |
git grep "pattern" <commit> | Search file contents at a specific commit |
git grep -n -p "pattern" | Search with line numbers and scope |
git shortlog -sn | Commit count by author, sorted |
Hands-On Lab
Setup
We'll build a repository with a rich enough history to make exploration meaningful.
mkdir log-lab && cd log-lab
git init
# Simulate a multi-author project by configuring author per commit
# (using --author flag)
# Initial commit
cat > app.js << 'EOF'
function main() {
console.log("App starting");
initialize();
}
function initialize() {
console.log("Initializing...");
}
module.exports = { main, initialize };
EOF
git add app.js
git commit --author="Alice Smith <alice@example.com>" -m "feat: initial application setup"
# Tag v1.0
git tag -a v1.0 -m "Version 1.0"
# Add config
cat > config.js << 'EOF'
const config = {
port: 3000,
host: "localhost",
debug: false,
logLevel: "info"
};
module.exports = config;
EOF
git add config.js
git commit --author="Bob Jones <bob@example.com>" -m "feat: add configuration module"
# Add utils
cat > utils.js << 'EOF'
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function formatDate(date) {
return date.toISOString().split('T')[0];
}
module.exports = { validateEmail, formatDate };
EOF
git add utils.js
git commit --author="Alice Smith <alice@example.com>" -m "feat: add utility functions
Includes email validation and date formatting helpers.
These will be used across multiple modules."
# Create a feature branch
git checkout -b feature/auth
cat > auth.js << 'EOF'
const { validateEmail } = require('./utils');
function login(email, password) {
if (!validateEmail(email)) {
throw new Error("Invalid email");
}
// TODO: implement actual auth
return { token: "abc123", user: email };
}
function logout(token) {
// TODO: invalidate token
return true;
}
module.exports = { login, logout };
EOF
git add auth.js
git commit --author="Carol Lee <carol@example.com>" -m "feat: add authentication module
Implements login and logout functions.
Uses email validation from utils."
echo '// Session management coming soon' >> auth.js
git add auth.js
git commit --author="Carol Lee <carol@example.com>" -m "chore: add session management placeholder"
# Switch back to main and make a parallel change
git checkout main
cat > logger.js << 'EOF'
const config = require('./config');
function log(level, message) {
const levels = ['debug', 'info', 'warn', 'error'];
if (levels.indexOf(level) >= levels.indexOf(config.logLevel)) {
console.log(`[${level.toUpperCase()}] ${new Date().toISOString()}: ${message}`);
}
}
module.exports = { log };
EOF
git add logger.js
git commit --author="Bob Jones <bob@example.com>" -m "feat: add logging module
Respects logLevel from config.
Supports debug, info, warn, error levels."
# Merge feature branch
git merge --no-ff feature/auth -m "Merge branch 'feature/auth' into main"
# More commits after merge
cat > config.js << 'EOF'
const config = {
port: 3000,
host: "localhost",
debug: true,
logLevel: "debug",
sessionTimeout: 3600
};
module.exports = config;
EOF
git add config.js
git commit --author="Bob Jones <bob@example.com>" -m "fix: enable debug mode and add session timeout
BREAKING: debug is now true by default.
Added sessionTimeout config option."
# Tag v2.0
git tag -a v2.0 -m "Version 2.0 - Authentication release"
# A few more commits
echo "// Performance optimization" >> app.js
git add app.js
git commit --author="Alice Smith <alice@example.com>" -m "perf: optimize main startup sequence"
echo "// Error boundary" >> app.js
git add app.js
git commit --author="Alice Smith <alice@example.com>" -m "fix: add error boundary to main
Prevents unhandled exceptions from crashing the app.
Related to issue #42."
echo "// Metrics" >> logger.js
git add logger.js
git commit --author="Dave Wilson <dave@example.com>" -m "feat: add metrics collection to logger"
echo "# Project README" > README.md
git add README.md
git commit --author="Carol Lee <carol@example.com>" -m "docs: add project README"
echo ""
echo "Repository ready! Commits:"
git log --oneline --allPart 1: Display Formats
# 1. Default full format
git log -3
# 2. Compact oneline
git log --oneline
# 3. Graph with all branches
git log --oneline --graph --all --decorate
# 4. Full format (author + committer)
git log --format=full -1
# 5. Custom format — hash, subject, author, relative date
git log --pretty=format:"%C(yellow)%h%C(reset) %s %C(blue)<%an>%C(reset) %C(green)(%ar)%C(reset)"Checkpoint: The graph view should show the merge commit and the feature/auth branch topology. The custom format should produce a clean, colored single-line output.
Part 2: Filtering by Author and Date
# All commits by Alice
git log --oneline --author="Alice"
# All commits by Bob
git log --oneline --author="Bob"
# Commits by Alice OR Carol
git log --oneline --author="Alice\|Carol"
# Count commits per author
git shortlog -sn
# Commits in the last 30 minutes (should show your recent work)
git log --oneline --since="30 minutes ago"Exercise: How many commits did each author make? Who made the most?
Checkpoint: git shortlog -sn should show Alice with the most commits, followed by Bob, Carol, and Dave.
Part 3: Message and Content Search
# Find commits mentioning "auth" in the message
git log --oneline --grep="auth"
# Find commits mentioning "feat:" (conventional commit prefix)
git log --oneline --grep="feat:"
# Find commits mentioning "fix:"
git log --oneline --grep="fix:"
# Pickaxe: when was "validateEmail" introduced?
git log --oneline -S "validateEmail"
# Pickaxe: when was "debug: true" introduced?
git log --oneline -S "debug: true"
# Regex search: find commits that touched any TODO
git log --oneline -G "TODO"
# Invert grep: commits NOT mentioning "feat" or "fix" or "chore"
git log --oneline --grep="feat:\|fix:\|chore:" --invert-grepCheckpoint: The -S "validateEmail" search should return the commit that added utils.js. The -S "debug: true" should return Bob's config change.
Part 4: Path Filtering and Line History
# Commits that touched config.js
git log --oneline -- config.js
# Commits that touched anything in the project root
git log --oneline -- "*.js"
# History of the config object (line range)
git log -L 1,7:config.js
# What changed in auth.js across all commits?
git log -p -- auth.jsCheckpoint: git log -- config.js should show Bob's two commits (initial add and the debug change). git log -L 1,7:config.js should show the evolution of the config object.
Part 5: Range Notation
# What's on feature/auth that isn't on main?
git log --oneline main..feature/auth
# (Should be empty — it was merged)
# What was on feature/auth before it was merged?
# Use the merge commit's parents:
git log --oneline HEAD~4^2 --not HEAD~4^1
# Or more simply, look at the merge commit:
git log --oneline --first-parent
# What's between v1.0 and v2.0?
git log --oneline v1.0..v2.0
# What's happened since v2.0?
git log --oneline v2.0..HEAD
# Symmetric difference between v1.0 and HEAD
git log --oneline --left-right v1.0...HEADCheckpoint: git log --oneline v1.0..v2.0 should show all commits between the two tags. git log --oneline v2.0..HEAD should show commits after the v2.0 tag.
Part 6: Navigating Merge History
# Full history (with merge details)
git log --oneline --graph
# First-parent only (main line)
git log --oneline --first-parent
# Only merge commits
git log --oneline --merges
# Only non-merge commits
git log --oneline --no-mergesCheckpoint: --first-parent should produce a clean linear history without the feature branch commits. --merges should show only the one merge commit.
Part 7: git grep — Searching Code
# Search for "validateEmail" in current code
git grep "validateEmail"
# With line numbers
git grep -n "validateEmail"
# Count occurrences per file
git grep -c "TODO"
# Search with function scope
git grep -p "validateEmail"
# Search at the v1.0 tag
git grep "validateEmail" v1.0
# (Should find nothing — utils.js didn't exist at v1.0)
# Search at v2.0
git grep "validateEmail" v2.0
# (Should find it in utils.js and auth.js)Checkpoint: git grep "validateEmail" v1.0 should return nothing. git grep "validateEmail" v2.0 should return matches in both utils.js and auth.js.
Challenge
- Create an alias called
git summarythat shows the last 10 commits with: abbreviated hash, relative date, author name, and subject — all nicely formatted with colors - Find all commits that mention an issue number (search for
#followed by digits in commit messages) - Use
git log -L :login:auth.jsto trace the history of theloginfunction - Generate a changelog for the v1.0 to v2.0 range using
git shortlog - Count how many
feat:vsfix:vs other commit types exist in the repo
Common Pitfalls
| Pitfall | What Happens | Prevention |
|---|---|---|
Using --all unnecessarily | Cluttered output with irrelevant remote branches | Default to no --all; add it only when you need the full picture |
Confusing .. in log vs. diff | log A..B = reachability; diff A..B = snapshot comparison | Remember: log thinks in terms of commit sets, diff thinks in terms of snapshots |
Forgetting -- before file paths | Git may confuse a filename with a branch name | Always use -- separator: git log -- path/to/file |
--follow with multiple files | Silently ignores all but the first file | --follow works with a single file only; for multiple files, omit it |
Searching with -S when -G is needed | -S misses commits that modify (but don't add/remove) a string | Use -G for regex matching within diffs, -S for introduction/removal |
Not using --first-parent on busy repos | Overwhelming graph with hundreds of interleaved branch commits | Use --first-parent to see just the merge points on the main line |
| Custom format without colors | Output is hard to parse visually | Add %C(color) and %C(reset) around key fields |
Pro Tips
-
--first-parentis your best friend on active repositories. When a repo has dozens of merged feature branches,--first-parentgives you a clean, linear narrative of what was merged and when. -
Pipe to
wc -lfor quick counts:git log --oneline v1.0..v2.0 | wc -linstantly tells you how many commits are in a release. -
git log -1 --format=%Hgives you just the SHA of the latest commit — useful in shell scripts, CI pipelines, and build systems. -
Combine
--grepwith--allto search for a commit message across all branches, not just the current one. A commit might be on a branch you forgot about. -
git log -p -S "function_name"is the ultimate archeology tool. It shows you the exact diff of every commit that added or removed that function name. -
Save your favorite format as a Git alias, not a shell alias. Git aliases in
.gitconfigare portable across machines and shell environments. Shell aliases are tied to your.zshrcor.bashrc. -
git grepovergrep -r: For any tracked repository,git grepis faster because it skips untracked files, build artifacts, andnode_modules. It's also history-aware.
Quiz / Self-Assessment
- What does
git log --oneline --graph --all --decorateshow, and why is it useful?
Answer
It shows every commit in the repository (from all branches and tags) in a compact single-line format, with an ASCII art graph showing branch and merge topology, and labels showing which branches/tags point to which commits. It's useful for getting a complete picture of the repository's branching structure. This is the most commonly aliased log command.
- How do you find all commits by a specific author in the last month?
Answer
git log --oneline --author="Author Name" --since="1 month ago"--author accepts a regex that matches against both name and email. --since (synonym: --after) accepts natural language date expressions. The filters are AND-combined.
- What is the difference between
git log main..featureandgit log main...feature?
Answer
main..feature (two dots) shows commits reachable from feature but NOT from main — the commits that feature has that main doesn't. main...feature (three dots) shows commits reachable from EITHER branch but NOT from both — the symmetric difference, showing divergent commits on both sides. Add --left-right with three dots to see which side each commit belongs to.
- What's the difference between
--grep,-S, and-Gingit log?
Answer
--grepsearches commit messages for a pattern-S(pickaxe) searches diffs for commits where the number of occurrences of a string changed (introduction/removal)-Gsearches diffs for commits where any changed line matches a regex pattern (broader than-S)
Use --grep for "what commits mentioned X?", -S for "when was X introduced or removed?", and -G for "when was code matching X touched?"
- What does
--first-parentdo, and when should you use it?
Answer
--first-parent follows only the first parent of each merge commit, effectively showing the "main line" history without diving into the details of merged feature branches. In a merge commit, the first parent is always the branch you were on when you ran git merge. Use it on merge-heavy repositories (typical in GitHub PR workflows) to see a clean, linear sequence of what was merged and when, without the noise of individual feature branch commits.
- How do you view the history of a specific function across all commits?
Answer
git log -L :functionName:path/to/file.jsThe -L flag with the :funcname:file syntax uses Git's built-in function detection to track the evolution of that function across commits. It shows the diff of the function in each commit that changed it. For specific line ranges, use git log -L 10,20:file.js.
- What format placeholder gives you the author name in a custom
--pretty=format:string? How about relative date?
Answer
%an gives the author name, %ar gives the author date in relative format (e.g., "2 days ago"). Other useful placeholders: %h (short hash), %s (subject), %ae (author email), %ai (ISO date), %d (ref names/decorations), %cn (committer name).
- How does
git grepdiffer fromgit log -S?
Answer
git grep searches the contents of files at a specific point in time (defaulting to HEAD). It tells you "where does this string appear right now?" git log -S searches through commit diffs across history. It tells you "in which commits was this string added or removed?" Use git grep to find current occurrences; use git log -S to find when something was introduced.
- You want to generate a list of all "feat:" commits between v1.0 and v2.0 with just the commit message (no hash). What command do you use?
Answer
git log --pretty=format:"%s" --grep="^feat:" v1.0..v2.0Or for a simpler approach:
git log --oneline --grep="feat:" v1.0..v2.0--pretty=format:"%s" outputs only the subject line. --grep="^feat:" matches commit messages starting with "feat:". v1.0..v2.0 limits to the range between the two tags.
- How do you follow a file's history across renames?
Answer
git log --oneline --follow -- path/to/file.jsThe --follow flag tells Git to detect renames and continue showing history under the file's previous name. Without --follow, git log only shows history from the current filename onward. Limitation: --follow only works with a single file path, not directories or patterns.