Searching Code and History

When you need to know where something is in the code, when it appeared, or why a line looks the way it does, Git has answers: git grep searches files, git log -S finds the commit that added or removed some text, and git blame shows who last changed each line and in which commit.

git grep: search the files

git grep searches tracked files — fast, and it skips ignored folders such as node_modules automatically:

$
git grep TAX_RATE
Output
basket.js:const TAX_RATE = 0.21;
basket.js:  return sum * (1 + TAX_RATE);
OptionEffect
-nShow line numbers
-iIgnore case
-wMatch whole words only
-cCount matches per file
-- "*.js"Search only matching files
$
git grep -n "function" -- "*.js"
Output
basket.js:3:function total(items) {
checkout.js:2:function pay(amount) {

You can also search the code as it was in any commit — without checking it out:

$
git grep TAX_RATE HEAD~3
Output
HEAD~3:basket.js:const TAX_RATE = 0.2;

git log -S: when did this text appear?

The "pickaxe" option -S finds commits that changed the number of times a string appears — in other words, commits that added or removed it:

$
git log --oneline -S "Nothing to pay"
Output
18b710f Reject empty payments
$
git log --oneline -S "0.2;"
Output
97ceeaa (HEAD -> main) Update tax rate for 2026
afad9da Add basket and checkout

-G takes a regular expression and finds commits whose changed lines match it, which also catches edits that don't add or remove the text outright. Add -p to see the actual changes.

Searching commit messages

$
git log --oneline --grep="tax" -i
Output
97ceeaa (HEAD -> main) Update tax rate for 2026
d251b62 Include quantity and tax in total

git blame: who changed each line?

git blame annotates every line of a file with the commit, author and date of its last change:

$
git blame basket.js
Output
97ceeaac (Alex Chen  2026-03-02 09:35:00 +0000 1) const TAX_RATE = 0.21;
^afad9da (Sam Rivera 2026-03-02 09:14:00 +0000 2)
^afad9da (Sam Rivera 2026-03-02 09:14:00 +0000 3) function total(items) {
d251b628 (Alex Chen  2026-03-02 09:21:00 +0000 4)   const sum = items.reduce((s, i) => s + i.price * i.qty, 0);
d251b628 (Alex Chen  2026-03-02 09:21:00 +0000 5)   return sum * (1 + TAX_RATE);
^afad9da (Sam Rivera 2026-03-02 09:14:00 +0000 6) }

A ^ before a hash marks lines that have been unchanged since the first commit in the range. Despite the name, blame is rarely about blame — it's the quickest way to find the commit (and its message) that explains a line. Take the hash from the left and look it up:

$
git show --no-patch 97ceeaa
Output
commit 97ceeaac8e5a6ce8245b9b5f864ac8126aec73a9 (HEAD -> main)
Author: Alex Chen <alex@example.com>
Date:   Mon Mar 2 09:35:00 2026 +0000

    Update tax rate for 2026

    The rate changed on 1 March; see finance ticket FIN-88.

Useful options:

OptionEffect
-L 3,6Only lines 3 to 6
-wIgnore whitespace-only changes
-CDetect lines moved or copied from other files
--ignore-rev <hash>Skip a commit, such as a big reformatting
$
git blame -L 4,5 basket.js
Output
d251b628 (Alex Chen 2026-03-02 09:21:00 +0000 4)   const sum = items.reduce((s, i) => s + i.price * i.qty, 0);
d251b628 (Alex Chen 2026-03-02 09:21:00 +0000 5)   return sum * (1 + TAX_RATE);

Projects often list mass-reformatting commits in a file called .git-blame-ignore-revs; set blame.ignoreRevsFile to that file (GitHub uses it automatically) so blame looks past them. GitHub and most editors show blame information inline as well.

The history of a few lines

git log -L follows a range of lines through history, showing every change to them:

$
git log -L 1,1:basket.js --oneline
Output
97ceeaa (HEAD -> main) Update tax rate for 2026

diff --git a/basket.js b/basket.js
--- a/basket.js
+++ b/basket.js
@@ -1,1 +1,1 @@
-const TAX_RATE = 0.2;
+const TAX_RATE = 0.21;
afad9da Add basket and checkout

diff --git a/basket.js b/basket.js
--- /dev/null
+++ b/basket.js
@@ -0,0 +1,1 @@
+const TAX_RATE = 0.2;

Try it yourself

In any project with some history, pick an interesting line. Use git blame to find the commit that last changed it, read that commit with git show, and then use git log -S to find when the line's key word first appeared.