307

I have a Git repository with few branches and dangling commits. I would like to search all such commits in repository for a specific string.

I know how to get a log of all commits in history, but these don't include branches or dangling blobs, just HEAD's history. I want to get them all, to find a specific commit that got misplaced.

I would also like to know how to do this in Mercurial, as I'm considering the switch.

Kenny Evitt
  • 9,291
  • 5
  • 65
  • 93
Josip
  • 6,677
  • 9
  • 26
  • 27
  • Related: [How to grep git commits for a certain word](http://stackoverflow.com/questions/1337320/how-to-grep-git-commits-for-a-certain-word/1340245#1340245). –  Sep 05 '13 at 22:04

10 Answers10

339

You can see dangling commits with git log -g.

-g, --walk-reflogs
 Instead of walking the commit ancestry chain, walk reflog entries from
 the most recent one to older ones. 

So you could do this to find a particular string in a commit message that is dangling:

git log -g --grep=search_for_this

Alternatively, if you want to search the changes for a particular string, you could use the pickaxe search option, "-S":

git log -g -Ssearch_for_this
# this also works but may be slower, it only shows text-added results
git grep search_for_this $(git log -g --pretty=format:%h)

Git 1.7.4 will add the -G option, allowing you to pass -G<regexp> to find when a line containing <regexp> was moved, which -S cannot do. -S will only tell you when the total number of lines containing the string changed (i.e. adding/removing the string).

Finally, you could use gitk to visualise the dangling commits with:

gitk --all $(git log -g --pretty=format:%h)

And then use its search features to look for the misplaced file. All these work assuming the missing commit has not "expired" and been garbage collected, which may happen if it is dangling for 30 days and you expire reflogs or run a command that expires them.

richq
  • 55,548
  • 20
  • 150
  • 144
  • 4
    Perhaps instead of running "git grep" on a (possibly large) number of commits, which would find all commits that have 'search_for_this' somewhere in a project, use so called "pickaxe" search, i.e. '-S' option to git log, which finds commits that introduced or removed given string, or to be more exact where number of occurences of a given string changed. – Jakub Narębski Apr 17 '09 at 10:04
  • 5
    You can specify multiple branches, or use '--all' option, e.g. 'git log --grep="string in a commit message" --all' – Jakub Narębski Apr 17 '09 at 10:06
  • This just allowed me to find a lost commit for 2 day's worth of work. Totally saved my ass, thanks! – Mike Chamberlain Mar 20 '12 at 02:47
  • 2
    I've come across some situations where I had commits in my database but not in my reflog. I don't know how common this is. I was trying out different hg/git bridges. I think it can also arise with dropped stashes. In any case, this alias works nicely to catch those cases: `!git fsck --unreachable | sed -ne 's/^unreachable commit //p' | xargs git log --no-walk` – dubiousjim Jul 03 '12 at 11:25
  • Note, that does not include searching note objects. That hasn't been implemented yet: http://git.661346.n2.nabble.com/Quickly-searching-for-a-note-td7567649.html – Antony Stubbs Dec 20 '12 at 23:12
  • To search for ALL commits on ALL branches for a SPECIFIC author you can use `git log -g --all --pretty=format:"%h - %an, %ar : %s" | grep -i ` – Adriano Nov 05 '13 at 11:38
  • To visualize dangling commits you should use `gitk --all $(git log -g --all --pretty=format:%h)` (notice the second `--all`) – user Mar 30 '15 at 23:24
  • 1
    Is the `-g` option really necessary? Isn't `git log --grep=search_for_this` sufficient? – Evan Aad Dec 05 '21 at 08:01
57

In Mercurial you use hg log --keyword to search for keywords in the commit messages and hg log --user to search for a particular user. See hg help log for other ways to limit the log.

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
  • 37
    Josip wrote that he is considering to switch to Mercurial and that he would also like to hear how it's done there. – Martin Geisler Apr 26 '10 at 10:49
  • 1
    `hg log -k` searches commit user name and file names in changesets as well (I see that in commands.py:log), which is one of the few things that I don't understand in hg. There should be separate options to search in commit messages and file names. Seems like `hg log --template '{desc}\n'|grep` is the sure way. – Geoffrey Zheng Jan 19 '11 at 19:29
  • @GeoffreyZheng: there are ways to do that. See "hg help revsets", esp the desc(), user(), and file() functions. There are also hg log switches for most of this behavior. In my experience, though -k/keyword() is usually the most helpful way to search for things. – Kevin Horn Dec 17 '12 at 17:08
  • How does one search through actual committed file content... the diffs? I know it would be a slow search, but I want to do a deep search for a missing function name. – Jonathan Feb 18 '16 at 01:39
  • 1
    Oh here it is: `hg grep --all ` – Jonathan Feb 18 '16 at 01:50
26

In addition to richq answer of using git log -g --grep=<regexp> or git grep -e <regexp> $(git log -g --pretty=format:%h): take a look at the following blog posts by Junio C Hamano, current git maintainer


Summary

Both git grep and git log --grep are line oriented, in that they look for lines that match specified pattern.

You can use git log --grep=<foo> --grep=<bar> (or git log --author=<foo> --grep=<bar> that internally translates to two --grep) to find commits that match either of patterns (implicit OR semantic).

Because of being line-oriented, the useful AND semantic is to use git log --all-match --grep=<foo> --grep=<bar> to find commit that has both line matching first and line matching second somewhere.

With git grep you can combine multiple patterns (all which must use the -e <regexp> form) with --or (which is the default), --and, --not, ( and ). For grep --all-match means that file must have lines that match each of alternatives.

Community
  • 1
  • 1
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • Hey Jakub, mind integrating quotes/summaries from those blog posts here? Looks like one of the vintage link-only answers right now. – Nathan Tuggy Oct 04 '15 at 00:48
11

Building on rq's answer, I found this line does what I want:

git grep "search for something" $(git log -g --pretty=format:%h -S"search for something")

Which will report the commit ID, filename, and display the matching line, like this:

91ba969:testFile:this is a test

... Does anyone agree that this would be a nice option to be included in the standard git grep command?

Sam Hiatt
  • 385
  • 4
  • 8
6

Any command that takes references as arguments will accept the --all option documented in the man page for git rev-list as follows:

   --all
       Pretend as if all the refs in $GIT_DIR/refs/ are listed on the
       command line as <commit>.

So for instance git log -Sstring --all will display all commits that mention string and that are accessible from a branch or from a tag (I'm assuming that your dangling commits are at least named with a tag).

adl
  • 15,627
  • 6
  • 51
  • 65
  • 3
    This does not appear to be the case for `git grep`, where `--all` appears to get translated to / used as `--all-match`. This looks like a bug to me.. using Git 1.7.2.3 (using `$(git rev-list --all)` works). – blueyed Feb 02 '11 at 12:23
6

With Mercurial you do a

$ hg grep "search for this" [file...]

There are other options that narrow down the range of revisions that are searched.

Yawar
  • 11,272
  • 4
  • 48
  • 80
2

Don't know about git, but in Mercurial I'd just pipe the output of hg log to some sed/perl/whatever script to search for whatever it is you're looking for. You can customize the output of hg log using a template or a style to make it easier to search on, if you wish.

This will include all named branches in the repo. Mercurial does not have something like dangling blobs afaik.

Kurt Schelfthout
  • 8,880
  • 1
  • 30
  • 48
1

if you are a vim user, you can install tig (apt-get install tig), and use /, same command to search on vim

https://blogs.atlassian.com/2013/05/git-tig/

jacktrade
  • 3,125
  • 2
  • 36
  • 50
1

To add just one more solution not yet mentioned, I had to say that using gitg's graphical search box was the simplest solution for me. It will select the first occurrence and you can find the next with Ctrl-G.

icarito
  • 328
  • 2
  • 11
1

One command in git that I think it's much easier to find a string:

git log --pretty=oneline --grep "string to search"

works in Git 2.0.4

Adriano Rosa
  • 8,303
  • 1
  • 25
  • 25