Find When Code Changed with Git Log -S and -G
Find the commit that added a string or changed a matching line. Learn how Git log -S and -G differ, with a practical example and search limits.
A setting changed somewhere in a project's history, but the commit message does not mention it. Git can search the changes themselves: use git log -S to find commits that change the number of occurrences of an exact string, or git log -G to find added or removed lines matching a regular expression. Both narrow the investigation; neither proves why a change was made.
This guide uses Git's command line and a repository you can inspect locally. The examples are based on the Git 2.45 log documentation; the small example was checked with Git 2.39.5. These are read-only history commands, so they do not change your files or branches.
Find an added or removed string
From the repository, start with a distinctive literal string and ask for the patch as well as the commit:
git log -p -S 'timeout' -- settings.txt -S 'timeout' selects file changes where the number of occurrences of timeout differs between the before and after versions. -p displays the patch, and -- settings.txt limits the search to that path. Replace both the string and path with your own. The -- separates the path from options and revisions. Single quotes work in Bash, including Git Bash; in another shell use its appropriate quoting syntax.
Read the patch and commit message together. A matching commit could add the string, remove it, or change its count by another amount. Open a candidate in context with git show <commit> (replace <commit> with the actual hash). Check its parent version and surrounding changes before assuming it introduced a bug. A commit can contain several changes, and a match alone does not establish causation.
Why a changed line can be missing from -S
Imagine one commit adds this line to settings.txt:
timeout = 30 A later commit changes it to:
timeout = 60 The first commit changes the count of timeout from zero to one, so git log -S 'timeout' -- settings.txt finds it. The second has one occurrence before and one after in that file, so the same -S search does not select it. This is true even though the value on the line changed. Git calls this family of searches *pickaxe*; the manual explains the count rule.
To find changes to lines containing the word, use:
git log -p -G 'timeout' -- settings.txt -G examines added and removed patch lines for a match. In this example it finds both commits: the later patch removes one matching line and adds another. The argument is a regular expression, so punctuation can have special meaning. Start with a simple distinctive term; if you need literal punctuation, escape it according to the expression syntax and your shell. Unlike default -S, -G is not a literal-string search. The manual also notes that -G normally ignores binary patches without a text conversion filter unless --text is supplied.
Make a result easier to investigate
For a compact candidate list, drop -p and add --oneline:
git log --oneline -G 'timeout' -- settings.txt Then inspect a candidate using git show <commit>. If other changed files matter, --pickaxe-all asks Git to include all file changes in matching commits rather than only matching file pairs:
git log -p --pickaxe-all -G 'timeout' -- settings.txt The path limit still applies: remove -- settings.txt when you want to investigate changes across the repository. A short path-limited search is easier to read, but it can miss earlier history under a different filename. Search more broadly and inspect likely rename commits if the file moved. Avoid treating a file's current name as proof of its entire history.
To search another local branch, supply its name before the path separator, for example git log -p -S 'timeout' main -- settings.txt. --all searches histories reachable from local refs, including locally known remote-tracking refs; it does not download missing history. Confirm you have the relevant branch and commits locally before drawing conclusions. A shallow clone may not contain older commits.
If there are no matches
First confirm the spelling, case, path and branch. Remove the path restriction to test whether the code lived elsewhere, and try a smaller distinctive fragment. If the line changed but the occurrence count stayed the same, switch from -S to -G. If you need to find a word in the current files rather than changes in history, use git grep instead; for words in commit messages, use git log --grep. These answer different questions from a pickaxe search.
Merge commits need care. Git's default log presentation can omit merge diffs, and history simplification can affect path-limited results. If the change appears to have arrived through a merge, inspect the merge and its parents explicitly; the Git log reference documents merge-diff and history options. A search result is a lead, not an audit of every possible history path.
Once you have a candidate, inspect the surrounding code, tests and related discussion. If you instead know a good revision and a bad revision but cannot name the changed text, a binary search with Git bisect can be a better next step.
Subscribe
Get new articles by email. Unsubscribe with one click.