Learning Objectives
By the end of this lesson you will be able to:
- Find the commit where a string first entered or left the codebase
- Explain precisely how
-Sand-Gdiffer - Combine the pickaxe with path and date filters to narrow a search
- Choose between the pickaxe and
git grepfor a given question
The Question Blame Can't Answer
Blame needs a line that still exists. But the interesting questions often concern something that is gone:
- "When was
login-btnremoved, and what replaced it?" - "When did this feature flag first appear?"
- "Which commit deleted the retry logic that used to be here?"
The pickaxe searches the entire history for commits whose changes involve a given string, whether they added it or removed it. The name comes from the flag's original description — digging through history.
-S: Occurrence Count Changed
git log -S "login-btn-v2" --oneline
8f3c2a1 Rename login button ID for the new auth flow
-S "string" finds commits where the number of occurrences of that exact string changed. A commit that added it, or removed it, or added two more of it, all match. A commit that merely moved a line containing it does not — the count is the same before and after.
That last property is what makes -S so precise. It gives you introductions and deletions, not noise.
Add the diff to see exactly what happened:
git log -S "login-btn" -p --oneline
Now you can read the change: login-btn became login-btn-v2, in the same commit, on the same line.
Note the ordering: git log shows newest first, so the last entry in the output is the original introduction. To read it the other way round:
git log -S "login-btn" --oneline --reverse
-G: Diff Matches a Regular Expression
git log -G "login-btn(-v[0-9])?" --oneline
-G "pattern" finds commits whose diff contains any added or removed line matching the regular expression, regardless of whether the total count changed.
The practical difference:
-S "timeout" |
-G "timeout" |
|
|---|---|---|
Line added containing timeout |
✅ | ✅ |
Line deleted containing timeout |
✅ | ✅ |
Line containing timeout moved elsewhere in the file |
❌ | ✅ |
timeout: 30 changed to timeout: 60 |
❌ | ✅ |
That fourth row is the important one. -S "timeout" will not find a value change, because timeout appears once before and once after. -G "timeout" will, because both the removed and added lines match.
Rule of thumb: use -S for "when did this appear or disappear?" and -G for "when was anything about this touched?"
Narrowing the Search
The pickaxe reads every commit's diff, so on a large repository it can be slow. Narrow it:
git log -S "login-btn" -- src/views/
Only commits touching that directory. The -- separates paths from other arguments and is worth including out of habit.
git log -S "retryCount" --since="2026-05-01" --until="2026-08-01" --oneline
A date window — useful when you know roughly when the regression appeared.
git log -S "retryCount" --author="Marta" --oneline
By author, when you already know who was working in the area.
git log -S "MAX_RETRIES" --pickaxe-regex --oneline
Treats the -S argument as a regular expression rather than a literal string. Without this flag, -S matches literally — which is usually what you want and is much faster.
Pickaxe vs. git grep
They answer different questions and it is worth keeping them straight:
git grep "login-btn"
Searches the current state of the working tree. "Where is this string right now?"
git grep "login-btn" v2.4.0
Searches the state at any commit or tag. "Where was this string when we shipped 2.4.0?"
git log -S "login-btn"
Searches the history of changes. "When did this string come and go?"
A typical investigation uses both: git grep to establish it is genuinely absent now, then git log -S to find out when and why it left.
A Complete Investigation
The automated login test broke sometime this quarter.
git grep "login-btn" # 1. is it there now?
git grep "login-btn" v2.4.0 # 2. was it there at the last release?
git log -S "login-btn" --oneline -- src/ # 3. when did it change?
git show 8f3c2a1 # 4. what was the change, and why?
git branch --contains 8f3c2a1 # 5. is it live?
git tag --contains 8f3c2a1
Five commands, and you have a bug report with a commit ID, an author, a date, a reason and a deployment status. That report gets fixed today rather than next sprint.
Pro Tip: Put the commit ID in every bug report you file after an investigation like this. "Broken since 8f3c2a1 (Rename login button ID, 2026-07-14)" is the single most useful sentence you can give a developer, and it costs you nothing — you already had it on screen.
Key Takeaways
- The pickaxe searches history for commits whose changes involve a string
-S "string"matches when the number of occurrences changed — introductions and deletions-G "pattern"matches any added or removed line matching a regex, including value changesgit logis newest-first, so the last result is the original introduction;--reverseflips it- Narrow with a path after
--,--since/--until, or--author git grepsearches a snapshot; the pickaxe searches history — real investigations use both