184

Is it possible to run git grep inside all the branches of a Git control sourced project? Or is there another command to run?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
epsilones
  • 11,279
  • 21
  • 61
  • 85
  • 5
    possible duplicate of [Using git, how could I search for a string across all branches?](http://stackoverflow.com/questions/7151311/using-git-how-could-i-search-for-a-string-across-all-branches) – Ciro Santilli OurBigBook.com Feb 20 '15 at 13:59
  • Possible duplicate of [How to grep (search) committed code in the git history?](https://stackoverflow.com/questions/2928584/how-to-grep-search-committed-code-in-the-git-history) – rofrol Oct 19 '18 at 14:45
  • The [second answer](https://stackoverflow.com/a/26226807/633961) is the best. – guettli Dec 07 '21 at 15:59

6 Answers6

232

The question "How to grep (search) committed code in the Git history?" recommends:

 git grep <regexp> $(git rev-list --all)

That searches through all the commits, which should include all the branches.

Another form would be:

git rev-list --all | (
    while read revision; do
        git grep -F 'yourWord' $revision
    done
)

You can find even more example in this article:

I tried the above on one project large enough that git complained about the argument size, so if you run into this problem, do something like:

git rev-list --all | (while read rev; do git grep -e <regexp> $rev; done)

(see an alternative in the last section of this answer, below)

Don't forget those settings, if you want them:

# Allow Extended Regular Expressions
git config --global grep.extendedRegexp true
# Always Include Line Numbers
git config --global grep.lineNumber true

This alias can help too:

git config --global alias.g "grep --break --heading --line-number"

Update August 2016: R.M. recommends in the comments

I got a "fatal: bad flag '->' used after filename" when trying the git branch version. The error was associated with a HEAD aliasing notation.

I solved it by adding a sed '/->/d' in the pipe, between the tr and the xargs commands.

git branch -a | tr -d \* | sed '/->/d' | xargs git grep <regexp>

That is:

alias grep_all="git branch -a | tr -d \* | sed '/->/d' | xargs git grep"
grep_all <regexp>

This is an improvement over the solution chernjie had suggested, since git rev-list --all is an overkill.

A more refined command can be:

# Don't use this, see above
git branch -a | tr -d \* | xargs git grep <regexp>

Which will allow you to search only branches (including remote branches)

You can even create a bash/zsh alias for it:

# Don't use this, see above  
alias grep_all="git branch -a | tr -d \* | xargs git grep"
grep_all <regexp>
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • It's not a good idea to pipe the output of `git branch` into tr or sed; `git branch` is a porcelain command meant for human consumption. See http://stackoverflow.com/a/3847586/2562319 for preferred alternatives. – jbyler May 11 '17 at 19:50
  • @jbyler Good point. Ironically, I posted the answer on porcelain long before this answer: http://stackoverflow.com/a/6978402/6309. And I use it for instance in http://stackoverflow.com/a/19206916/6309. – VonC May 11 '17 at 19:54
  • Yep, nice. Looking through the other answers, I think the answer by @errordeveloper is the cleanest: http://stackoverflow.com/a/21284342/2562319 : "git grep $(git for-each-ref --format='%(refname)' refs/)" – jbyler May 11 '17 at 20:05
  • 9
    Is there any way to show the name of the branch that matched the search? – franksands Oct 16 '18 at 16:26
  • What does "while read rev; do git grep -e $rev; done" do exactly? Must I press a key to continue viewing? – Emmanuel Goldstein Nov 28 '20 at 08:27
  • @EmmanuelGoldstein It will read each lines of the `git rev-list --all` command. You won't have to press any key. (https://unix.stackexchange.com/a/117502/7490) – VonC Nov 28 '20 at 12:10
  • Yes, I have. In fact, when I press 'q' I see every time a different commit. So this is confusing. The above command will only display one result per page, at least on my end. – Emmanuel Goldstein Nov 29 '20 at 05:54
  • `grep.extendRegexp` should be `grep.extendedRegexp` – AdmiralAdama Nov 11 '22 at 00:45
  • 1
    @AdmiralAdama thank you for the feedback. I have edited the answer accordingly. – VonC Nov 11 '22 at 00:54
  • @VonC Np. Nice answer btw, upvoted. – AdmiralAdama Nov 11 '22 at 00:55
  • @VonC Yes AdmiralAdama. As usual, all hail King VonC of the git kingdom. Let all those who benefit from his solutions pay sufficient tribute to the great one! – Nathan majicvr.com Jun 12 '23 at 17:20
99

git log can be a more effective way of searching for text across all branches, especially if there are many matches, and you want to see more recent (relevant) changes first.

git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'

These log commands list commits that add or remove the given search string/regex, (generally) more recent first. The -p option causes the relevant diff to be shown where the pattern was added or removed, so you can see it in context.

Having found a relevant commit that adds the text you were looking for (eg. 8beeff00d), find the branches that contain the commit:

git branch -a --contains 8beeff00d
Edward Anderson
  • 13,591
  • 4
  • 52
  • 48
33

I found this most useful (omit -i if you desire a case-sensitive search):

git grep -i foo `git for-each-ref --format='%(refname)' refs/`

You'd need to adjust the last arguments depending on whether you want to only look at remote vs. local branches, i.e.:

  • git grep -i foo $(git for-each-ref --format='%(refname)' refs/remotes)
  • git grep -i foo $(git for-each-ref --format='%(refname)' refs/heads)

The alias I created looks like this:

grep-refs = !sh -c 'git grep "$0" "$@" "$(git for-each-ref --format=\"%(refname)\"" refs/)'
ynn
  • 3,386
  • 2
  • 19
  • 42
errordeveloper
  • 6,716
  • 6
  • 41
  • 54
  • 1
    Interesting alias. +1. More precise than in my answer. – VonC Jan 22 '14 at 14:00
  • How to make your alias to work for phrase search? When I pass "foo bar" as a parameter, I get: fatal: ambiguous argument 'bar': unknown revision or path not in the working tree. Use '--' to separate paths from revisions – jutky Apr 06 '16 at 11:58
  • Try escaping the space: "foo\ bar" – Damion Gomez May 06 '19 at 22:12
  • This shall clearly be the accepted answer ;) I've expanded on it to search only across the latest branches of my project (Got more than 500, don't ask, and each grep takes about 4sec so I don't want nor need to search across more than the, say, 100 latest of them). For this I've updated the `git for-each-ref` with `--sort=-committerdate --count=100` ! Thanks for the original idea! – Vser Jan 29 '20 at 09:52
9

It's possible to do it in two common ways: Bash or Git aliases

Here are three commands:

  1. git grep-branch - Search in all branches local & remote
  2. git grep-branch-local - Search in local branches only
  3. git grep-branch-remote - Remote branches only

Usage is the same as git grep

git grep-branch "find my text"
git grep-branch --some-grep-options "find my text"

GREP using: Git aliases

File ~/.gitconfig

Commands should be added manually to ~/.gitconfig file, because git config --global alias evaluate complex code you add and mess it up.


[alias]
    grep-branch        = "!f(){ git branch -a | sed -e 's/[ \\*]*//' | grep -v -e '\\->' | xargs git grep $@; };f "
    grep-branch-remote = "!f(){ git branch -a | sed -e 's/[ \\*]*//' | grep -v -e '\\->' | grep '^remotes' | xargs git grep $@; };f"
    grep-branch-local  = "!f(){ git branch -a | sed -e 's/[ \\*]*//' | grep -v -e '\\->' -e '^remotes' | xargs git grep $@;  };f "

Note: When you add aliases and they fail to run - check backslashes \ they may require additional escape \\ in compare to bash commands.

  • git branch -a - Display all branches;
  • sed -e 's/[ \\*]*//' - Trim spaces (from branch -a) and * (active branch name have it);
  • grep -v -e '\\->' - Ignore complex names likeremotes/origin/HEAD -> origin/master;
  • grep '^remotes' - Get all remote branches;
  • grep -v -e '^remotes' - Get branches except remote branches;

Example git grep-branch-local -n getTastyCookies

-n Prefix the line number to matching lines.

[user@pc project]$ git grep-branch-local -n getTastyCookies

dev:53:modules/factory/getters.php:function getTastyCookies($user);
master:50:modules/factory/getters.php:function getTastyCookies($user)

The current structure is:

: - Separator

  1. Branch: dev
  2. Line number: 53
  3. File path: modules/factory/getters.php
  4. Matching line: function getTastyCookies($user)

GREP using: BASH

As you should know: Bash commands should be stored in .sh scripts or run in a shell.

Local branches only

git branch -a | sed -e 's/[ \*]*//' | grep -v -e '\->' -e '^remotes' | xargs git grep "TEXT"

Remote branches only

git branch -a | sed -e 's/[ \*]*//' | grep -v -e '\->' | grep '^remotes' | xargs git grep "TEXT"

Local & remote branches

git branch -a | sed -e 's/[ \*]*//' | grep -v -e '\->' | xargs git grep "TEXT"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Devaldo
  • 91
  • 1
  • 5
  • Sounds nice, but I got this error from `git grep-branch "find my text"`: `fatal: ambiguous argument 'client': both revision and filename` – nealmcb Aug 06 '17 at 15:16
  • Why always use `-a`, which shows all branches? I would suggest using options to the `git branch` command to separate braches. When looking at local branches, there's only a single `*`, so no need to escape it for sed. So: `git branch | sed -e 's/*/ /' | xargs git grep "TEXT"` for local branches only, `git branch -r | grep -v -- "->" | xargs git grep "TEXT"` for remote branches only, and `git branch -a | grep -v -- "->" | xargs git grep "TEXT"` for all branches – jalanb May 06 '20 at 01:25
  • I had a slightly different problem, but that this weird function thing solved my problem. thank you! and just for reference - I wanted to delete all local branches apart from master and dev. the alias to do that is: `cleanLocalBranches = "!f(){ git branch -a | egrep -v '(^\\*|master|dev)' | xarg s git branch -D; };f "` – WrRaThY Mar 23 '22 at 20:56
4

If you give any commit a SHA-1 hash value to git grep you have it search in them, instead of the working copy.

To search all branches, you can get all the trees with git rev-list --all. Put it all with

git grep "regexp" $(git rev-list --all)

... and have patience

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
CharlesB
  • 86,532
  • 28
  • 194
  • 218
4

Here's how I do it:

git for-each-ref --format='%(*refname)' | xargs git grep SEARCHTERM
William Entriken
  • 37,208
  • 23
  • 149
  • 195