Great work!

XP to next level

BugEater

The Pickaxe: git log -S and git log -G

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 -S and -G differ
  • Combine the pickaxe with path and date filters to narrow a search
  • Choose between the pickaxe and git grep for 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-btn removed, 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 changes
  • git log is newest-first, so the last result is the original introduction; --reverse flips it
  • Narrow with a path after --, --since / --until, or --author
  • git grep searches a snapshot; the pickaxe searches history — real investigations use both

Quiz

What does git log -S "login-btn" find?

A config line changed from timeout: 30 to timeout: 60. Which command finds that commit?

You ran git log -S "retryCount" and got four results. Which one is the original introduction?

What is the difference between git grep "login-btn" and git log -S "login-btn"?