How to find the Git commit that introduced a string in any branch?
Asked Answered
H

7

562

I want to be able to find a certain string which was introduced in any commit in any branch, how can I do that? I found something (that I modified for Win32), but git whatchanged doesn't seem to be looking into the different branches (ignore the py3k chunk, it's just a msys/win line feed fix)

git whatchanged -- <file> | \
grep "^commit " | \
python -c "exec(\"import sys,msvcrt,os\nmsvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\nfor l in sys.stdin: print(l.split()[1])\")" | \
xargs -i% git show origin % -- <file>

It doesn't really matter if your solution is slow.

Horten answered 28/4, 2011 at 8:58 Comment(1)
Closely related: How to grep git commits for a certain word.Kazbek
U
1001

You can do:

git log -S <search string> --source --all

To find all commits that added or removed the fixed string search string. The --all parameter means to start from every branch and --source means to show which of those branches led to finding that commit. It's often useful to add -p to show the patches that each of those commits would introduce as well.

Versions of git since 1.7.4 also have a similar -G option, which takes a regular expression. This actually has different (and rather more obvious) semantics, explained in this blog post from Junio Hamano.

As thameera points out in the comments, you need to put quotes around the search term if it contains spaces or other special characters, for example:

git log -S 'hello world' --source --all
git log -S "dude, where's my car?" --source --all

Here's an example using -G to find occurrences of function foo() {:

git log -G "^(\s)*function foo[(][)](\s)*{$" --source --all
Unroll answered 28/4, 2011 at 9:3 Comment(12)
+1 for excellence. Pointing at -S is one thing, explaining things, better. Also, I like to use --decorate to see what branches things come fromAtavistic
@sehe: Thanks for your nice comment. I guess it's worth noting that --decorate only adds the branch name to the commit at the tip of each branch. In practice I don't really use --source or --decorate, and instead use git branch -a --contains <commit-hash> to find which branches contain the commit I'm interested in.Unroll
add -p to see the inline diff, as well, FWIWOptime
@MarkLongair it doesn't show the changes made in merge. Any suggestion to show those as well?Doone
For me this only works if I remove the space between the -S and the search term, i.e., git log -S"dude, where's my car?" --source --all. @ribamar also wrote that in an answer below, but it might easily get overlooked next to this top answer.Triclinic
@Triclinic I can't reproduce that - can you let me know what version of Git you're seeing that problem with? (i.e. what is the output of git --version?) I see that the documentation prefers the form with no space, so perhaps I should change the answer to that form anyway, but it's a bit less clear to read.Unroll
@MarkLongair I just tried it again, your solution works on my machine with git version 1.7.9.5, however it does not work on another set up which uses version 1.7.1Triclinic
continued from above comment: $ git log -S 'hello world' --source --all returns: "fatal: ambiguous argument 'hello world': unknown revision or path not in the working tree. Use '--' to separate paths from revisions" Considering that this version is several years old and the current release is 2.11, it might be the time for our sysadmin to update it, but nevertheless it does work without the space.Triclinic
@MarkLongair, what type of regex does git use? is it extended regex, or basic regex or pcre?Carpio
Note: If a line removes and adds the same substring, it won't show up in this search (i.e. it seemingly uses the actual word diff rather than the line diff). Anyone know how to use the line diff?Junket
This is not what OP is asking. This answer actually does: https://mcmap.net/q/12340/-how-to-find-the-git-commit-that-introduced-a-string-in-any-branchLoos
To see the actual changes you can append --patch.Billfold
D
118

--reverse is also helpful since you want the first commit that made the change:

git log --all -p --reverse --source -S 'needle'

This way older commits will appear first.

Distinguish answered 25/7, 2015 at 1:24 Comment(3)
Is there a way to make the command stop after the first match is found?Xenophobia
@CorneliusRoemer maybe git log -n1?Distinguish
@CiroSantilliOurBigBook.com nope doesn't seem to work with --reverse. only shows the latest commitAstrograph
I
31

Messing around with the same answers:

$ git config --global alias.find '!git log --color -p -S '
  • ! is needed because other way, git do not pass argument correctly to -S. See this response
  • --color and -p helps to show exactly "whatchanged"

Now you can do

$ git find <whatever>

or

$ git find <whatever> --all
$ git find <whatever> master develop
Imitative answered 27/4, 2013 at 12:16 Comment(0)
C
23

Mark Longair’s answer is excellent, but I have found this simpler version to work for me.

git log -S whatever
Cyanite answered 30/12, 2012 at 17:8 Comment(1)
Just to clarify, that works fine if the commit you're looking for is in HEAD, but this particular question asked specifically about looking across all the branches in a repository.Unroll
D
9
git log -S"string_to_search" # options like --source --reverse --all etc

Pay attention not to use spaces between S and "string_to_search". In some setups (git 1.7.1), you'll get an error like:

fatal: ambiguous argument 'string_to_search': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions
Delightful answered 14/3, 2016 at 13:17 Comment(0)
S
6

While this doesn't directly answer you question, I think it might be a good solution for you in the future. I saw a part of my code, which was bad. Didn't know who wrote it or when. I could see all changes from the file, but it was clear that the code had been moved from some other file to this one. I wanted to find who actually added it in the first place.

To do this, I used Git bisect, which quickly let me find the sinner.

I ran git bisect start and then git bisect bad, because the revision checked out had the issue. Since I didn't know when the problem occured, I targetted the first commit for the "good", git bisect good <initial sha>.

Then I just kept searching the repo for the bad code. When I found it, I ran git bisect bad, and when it wasn't there: git bisect good.

In ~11 steps, I had covered ~1000 commits and found the exact commit, where the issue was introduced. Pretty great.

Serriform answered 28/10, 2016 at 5:37 Comment(0)
K
6

Not sure why the accepted answer doesn't work in my environment, finally I run below command to get what I need

git log --pretty=format:"%h - %an, %ar : %s"|grep "STRING"
Kempe answered 13/12, 2018 at 3:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.