Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

git

grep

People also ask

Does git grep search all branches?

Git includes a grep command to search through commits to a repo as well as the local files in the repo directory: git grep. Sometimes it is useful to search for a string throughout an entire repo, e.g. to find where an error message is produced.


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

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

I found this most useful:

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/)'

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"

Here's how I do it:

git for-each-ref --format='%(*refname)' | xargs git grep SEARCHTERM

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