Finding the exact commit that introduced a bug — especially one that was buried under dozens of subsequent changes — is one of Git’s most powerful forensic capabilities. The combination of git log -S (the “pickaxe” search), git log -G (regex diff search), git blame --follow, and git bisect forms a complete bug-archaeology toolkit that works even on files that have been renamed or heavily refactored.
Problem: A production WordPress site has a bug that appears to have been introduced weeks ago — git log shows hundreds of commits, and it is unclear which commit introduced the regression or why a particular line was written.
Solution: Use git blame file.php to identify the commit that last changed each line. Use git log -S 'search string' (pickaxe) to find the commit that introduced or removed a specific string. For binary search through the history, git bisect start + git bisect bad/git bisect good automatically finds the first bad commit in O(log n) steps.
The commands below find when a specific string was added or removed, trace a function's history across renames, use git bisect to binary-search for a regression, and produce an annotated blame view with ignored whitespace and move detection.
# 1. Find every commit that added or removed a specific string (pickaxe)
git log -S "wp_enqueue_script( 'my-script'" --oneline --all
# 2. Find commits where a regex pattern changed in the diff
git log -G "function my_broken_function" --oneline -p
# 3. Full blame with line ranges, ignoring whitespace, tracking moves
git blame -w -M -C -L 42,80 wp-content/plugins/my-plugin/class-main.php
# -w: ignore whitespace changes
# -M: detect lines moved within the same commit
# -C: detect lines moved from other files in the same commit
# 4. Follow a function's history even across file renames
git log --follow -p --all -S "my_broken_function" -- "*.php"
# 5. git bisect: binary search for a regression
git bisect start
git bisect bad # current HEAD is broken
git bisect good v2.1.0 # this tag was known good
# Git checks out the midpoint commit — test it, then:
git bisect good # or: git bisect bad
# Repeat until Git prints: "XXXXXXX is the first bad commit"
git bisect reset # return to HEAD when done
# 6. Automate bisect with a test script
git bisect start HEAD v2.1.0
git bisect run bash -c "cd wp-content/plugins/my-plugin && php -r \"require 'class-main.php'; exit(my_broken_function() === true ? 0 : 1);\""
# 7. Show the diff of the exact commit that introduced the bug
git show XXXXXXX --stat
git show XXXXXXX -- wp-content/plugins/my-plugin/class-main.php
# 8. Find who last touched each line in a blame, with commit message
git blame --line-porcelain HEAD wp-content/plugins/my-plugin/class-main.php \
| grep -A 1 "^[0-9a-f]\{40\}" | grep "^summary" | sort | uniq -c | sort -rn
NOTE: git log -S finds commits where the search string's count in the file changed (i.e., the string was added or removed), while git log -G finds commits where any line matching the regex appears in the diff — use -S to track additions/removals of a specific call site and -G when you need to find all commits that touched a pattern regardless of net change.