Is it possible to perform a 'grep search' in all the branches of a Git project?
Asked Answered
S

6

190

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

Spindrift answered 8/3, 2013 at 10:58 Comment(4)
possible duplicate of Using git, how could I search for a string across all branches?Cedeno
Possible duplicate of How to grep (search) committed code in the git history?Obituary
The second answer is the best.Geology
That is in terms of votes total. It is the fourth answer chronologically.Mckenziemckeon
B
239

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>

Jan. 2024: Gabriel Staples adds in the comments:

For anyone who wants to search just the tip commits on every branch in the repo, rather than every commit in the repo, and if you'd like to do parallel processes to speed up the search, see my very extensive answer here:

All about searching (via grep or similar) in your git repositories.
I build upon some of the content in this answer.

Beadle answered 8/3, 2013 at 11:47 Comment(14)
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 https://mcmap.net/q/12335/-how-to-iterate-through-all-git-branches-using-bash-script for preferred alternatives.Divisor
@Divisor Good point. Ironically, I posted the answer on porcelain long before this answer: https://mcmap.net/q/12336/-what-does-the-term-quot-porcelain-quot-mean-in-git. And I use it for instance in https://mcmap.net/q/12337/-what-to-add-to-quot-git-status-porcelain-quot-to-make-it-behave-like-quot-git-status-quot.Beadle
Yep, nice. Looking through the other answers, I think the answer by @errordeveloper is the cleanest: https://mcmap.net/q/12318/-is-it-possible-to-perform-a-39-grep-search-39-in-all-the-branches-of-a-git-project : "git grep <regexp> $(git for-each-ref --format='%(refname)' refs/)"Divisor
Is there any way to show the name of the branch that matched the search?Shirlshirlee
What does "while read rev; do git grep -e <regexp> $rev; done" do exactly? Must I press a key to continue viewing?Prut
@EmmanuelGoldstein It will read each lines of the git rev-list --all command. You won't have to press any key. (unix.stackexchange.com/a/117502/7490)Beadle
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.Prut
grep.extendRegexp should be grep.extendedRegexpCrow
@Crow thank you for the feedback. I have edited the answer accordingly.Beadle
@Beadle Np. Nice answer btw, upvoted.Crow
@Beadle 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!Dropkick
Surprisingly fast. If it's slow for you, just grep a different / smarter way. But for a moderately sized repo with ~= 25 engineers' commits, it was fast.Dropkick
Upvoted. Great answer. For anyone who wants to search just the tip commits on every branch in the repo, rather than every commit in the repo, and if you'd like to do parallel processes to speed up the search, see my very extensive answer here: All about searching (via grep or similar) in your git repositories. I build upon some of the content in this answer.Fabozzi
@GabrielStaples Thank you for your feedback. I have edited the answer to include your comment and reference your answer.Beadle
S
101

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
Spanner answered 7/10, 2014 at 0:24 Comment(2)
Easiest way! Thank you for the answer!Poundage
Yes, this returned almost immediately while the git rev-list --all approach took several minutes. The output is more useful too (includes the diff).Lynelllynelle
T
35

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/)'
Trotline answered 22/1, 2014 at 13:32 Comment(4)
Interesting alias. +1. More precise than in my answer.Beadle
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 revisionsLambert
Try escaping the space: "foo\ bar"Platinize
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!Fiddlewood
B
11

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"

1. Grep: using Git aliases

In 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.

Explanation:

  • git branch -a - Display all branches;
  • sed -e 's/[ \\*]*//' - Trim spaces (from branch -a) and *, which is shown in front of the active branch name;
  • 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)

2. 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"
Beulabeulah answered 27/1, 2017 at 16:17 Comment(3)
Sounds nice, but I got this error from git grep-branch "find my text": fatal: ambiguous argument 'client': both revision and filenameAvatar
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 branchesCyrilcyrill
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 "Perhaps
E
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

Eberto answered 8/3, 2013 at 11:46 Comment(0)
C
4

Here's how I do it:

git for-each-ref --format='%(*refname)' | xargs git grep SEARCHTERM
Consciencestricken answered 24/9, 2015 at 15:51 Comment(1)
The only solution that worked for me on Windows (in Git bash)Vogt

© 2022 - 2024 — McMap. All rights reserved.