27

As the title says, I want to find every commit whose diff contains specific string.

At the moment, I use

git log -p 'filename'

Which shows less like interface of every diff, where I search for the string. Then I backtrace to find the actual commit msg.

Simple alternative might be to pipe git log -p into grep, but I can not find the commit id or message that way.

bbaja42
  • 2,099
  • 18
  • 34
  • 1
    Do you mind reviewing the answers? I believe the other answers are better than a script (would actually be one line) and one provides a more detailed explanation – Geoff Lee Nov 08 '17 at 21:30

7 Answers7

32
git log -p -S'string'

can be used to search for commits that add or remove a string. It doesn't behave exactly the same, because it only matches commits that actually add or remove an instance of the pattern, and not (for instance) commits where it occurs in the diff context. But maybe that's good enough for you.

Potherca
  • 13,207
  • 5
  • 76
  • 94
David
  • 2,947
  • 1
  • 25
  • 19
15

Here's a one-liner shell script (split into more than one line for formatting purposes) that extracts the rev-numbers of current-branch-reachable commits affecting path where git show -p contains the given pattern. It's not perfect (it will match commit messages as well as diffs) but it should be easy to tweak however you like, from here.

git rev-list HEAD -- path |
while read rev; do
    if git show -p $rev | grep pattern >/dev/null; then
        echo $rev
    fi
done

Note that you can replace git show with, e.g., git diff $rev^ $rev (note that this only compares against first-parent if it's a merge), or git whatchanged $rev, or whatever you like. The main trick is to start with git rev-list to extract all the candidates (commits affecting the given path; omit the -- path part to get all commits starting from HEAD). See git-rev-list(1) for lots of other things you can do with git rev-list.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Little too verbose, for a one liner (normal scenario when I need to use this). But it is clean solution and it works. I will let the question stay open for next 24 hours, and unless there is a better solution, accept yours. Thanks for help – bbaja42 Aug 21 '13 at 18:48
  • 1
    Yep! It's a one-liner alright, especially if your `grep` has `--quiet`. `git rev-list HEAD | while read rev; do git show -p $rev | grep 'CacheManager' -q && echo $rev; done ` – jpaugh Oct 02 '18 at 19:42
13

I know this question's been answered for a while now, but I came across this as well and found a different solution so I thought I'd share. Git-log's -G switch should do what you're aksing, where the -S switch would only output commits where the number of occurences of the matched string changes.

From git-log's man page:

-G Look for differences whose patch text contains added/removed lines that match .

To illustrate the difference between -S --pickaxe-regex and -G, consider a commit with the following diff in the same file:

+    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
...
-    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);

While git log -G"regexec(regexp" will show this commit, git log -S"regexec(regexp" --pickaxe-regex will not (because the number of occurrences of that string did not change).

See the pickaxe entry in gitdiffcore(7) for more information.

Likeyn
  • 193
  • 1
  • 8
  • Note, by the way, that the difference between this built-in `-G` (grep for change) and my long "one-liner" answer is that this will find this particular `regexec` occurrence, but *not* one that's just in the context shown for some `git log -p` output. For instance if the line that was changed was the line just *before* the `regexec` call, `git log -G` won't find that change, but searching each `git show -p` command's output will. (Hence, which command(s) to try depends on what you want found.) – torek Oct 13 '15 at 21:29
5
git log --all -G'my_search' -i -m -p

mnemonic: git log all GIMP

  • --all: search through all possible paths, not just through HEAD.
  • -G: more general than -S because -S searches only for diffs which show different number of occurrences of my_search. -G searches full diffs and accepts regexes.
  • -i[optional]: make -G ignore case.
  • -m: by default git log does not output diffs for merge commits. -m changes this default behaviour making merge commits output full diffs. This is crucial: if a malformed merge commit changed your line you would not be able to see without -m.
  • -p: also output the full patch for each commit found.

you can even connect this to vim. I find it very useful:

git log --no-color --all -G'regex' -i -m -p | vim -

here I'm using --no-color because vim highlights patches for me.

ninrod
  • 523
  • 6
  • 25
0

If you're on linux, pipe it through egrep and search on a regex

git log -p 'filename' | egrep '(yourstring|commit-message)'
user1333371
  • 598
  • 3
  • 13
  • Yeah, nice dea, but I don't see the context (which commit was the one that had the change). Thnx anyway. – bbaja42 Mar 04 '14 at 15:48
  • @bbaja42 what about egrep? git log -p 'filename' | egrep '(yourstring|commit-message)'. That way the output will be the commit messages followed by the lines changed. To get more context around matches, the -C flag can be passed to egrep – user1333371 Mar 04 '14 at 21:16
  • Yeah that might work git log -p 'filename' | egrep '(yourstring|commit-message)'| grep -B 1 yourstring . This should show only commit messages and 'theString' that match – bbaja42 Mar 04 '14 at 23:16
-1

Have you considered using a pipe and grep command?

Example:

 git log -p | grep 'filename'
John Lee
  • 1,357
  • 1
  • 13
  • 26
  • Not a bad idea, but I really need the commit msg, so I can get the context of that commit. And since commits can be quite large, I can't really use -C option of grep – bbaja42 Aug 21 '13 at 18:07
-1

You can use git for this, for example:

git log --grep="filename"

Or on a specific file:

git log --grep="filename" README