260

Using Git, how could I search within all files in all local branches for a given string?

GitHub specific: is it possible to perform the above search across all GitHub branches? (There are several remote branches on my remote GitHub repository that ideally I wouldn't have to bring down for this search...)

Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Ivar
  • 4,722
  • 2
  • 32
  • 42
  • 1
    [git-grep](http://www.kernel.org/pub/software/scm/git/docs/git-grep.html) might be what you're looking for, but I'm not sure yet which options you'd need... – johnny Aug 22 '11 at 17:51
  • 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) – nekketsuuu Jul 11 '17 at 21:01

7 Answers7

224

You can do this on a Git repository:

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

GitHub advanced search has code search capability:

The code search will look through all of the code publicly hosted on GitHub. You can also filter by:

  • the language: language:
  • the repository name (including the username): repo:
  • the file path: path:
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
manojlds
  • 275,671
  • 58
  • 453
  • 409
  • This causes me a segmentation fault. Might be tortoisegitmerge (Windows), though. – bean5 Aug 01 '14 at 17:47
  • 11
    This is really not the best way to do this. It doesn't control the amount of git refs that are passed to `git grep ...`. Look to the other answers, they're far better than this one even though it's marked as the accepted answer! – slm Mar 20 '17 at 15:17
  • 1
    it would be great if you can add an example for your filters, e.g. `path:`, because the documentation at a glance doesnt look clear where to apply this filter, im assuming its before the quotes in your query example? – blamb May 30 '17 at 18:19
  • 3
    how can I list branch name only. Currently, it list all the hash contains the string. – harryfeng Aug 10 '17 at 19:11
  • 9
    Github search is on master branch *only*. From https://help.github.com/articles/searching-code/: "Only the default branch is indexed for code search. In most cases, this will be the master branch." – RedPanda Feb 28 '18 at 22:55
  • 1
    This will only work on a relatively smallish git repository. A git sha is 40 chars plus a space (or LF in this case) between them. On linux your arg list is limited to ~128kb (~256kb on a Mac). Your argument list will get way too big after a 3k-4k commits (6k - 8k on a Mac). That's not at all unreasonably on a fair sized repository. – stuckj Jun 12 '18 at 15:10
  • To find the branch name (having located the commit hash), you can use `git branch -a --contains ` – Russ Oct 01 '18 at 11:40
  • This gives `Wed Oct 17 02:39 PM liminex: git grep '["]Welcome' $(git rev-list --all)` -> `bash: /usr/bin/git: Argument list too long` – fIwJlxSzApHEZIl Oct 17 '18 at 21:41
  • 1
    Hi, i'm developing a tool to search in all remote and local repos using this command : https://github.com/GaetanoPiazzolla/git-search if you want, take a look. – Gaetano Piazzolla Sep 14 '20 at 07:36
  • If this only searches one branch, then this doesn't really answer the question. – BrainSlugs83 Nov 10 '21 at 00:26
175

If you use @manojlds Git grep command and get an error:

-bash: /usr/bin/git: Argument list too long" 

then you should use xargs:

git rev-list --all | xargs git grep "string/regexp"

Also see How to grep (search) committed code in the Git history

Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
teastburn
  • 2,560
  • 1
  • 18
  • 12
  • Also, this seems to be more compatible with other kind of consoles like fishshell – Daniel Aug 17 '16 at 14:18
  • 4
    Thanks!!! using ZSH and this worked while @manojlds command gave the error you mentioned! But warning, this can take VERY long time for a large repo with a long history. – lacostenycoder Feb 28 '18 at 16:29
  • 1
    If you are looking in a specific file, you can use: `git rev-list --all | xargs -J % git grep "string/rexexp" % -- filename`. This will run `git grep` on blocks of commits that are inserted where the `%` appears. – rgov Sep 29 '21 at 19:46
102

In many cases git rev-list --all can return a huge number of commits, taking forever to scan. If you, instead of searching through every commit on every branch in your repository history, just want to search all branch tips, you can replace it with git show-ref -s --heads. So in total:

git grep "string" `git show-ref -s --heads`

or:

git show-ref -s --heads | xargs git grep "string"

Tip: You can write output in file to view in an editor:

nano ~/history.txt
git show-ref -s --heads | xargs git grep "search string here" >> ~/history.txt
Dan Berindei
  • 6,779
  • 3
  • 39
  • 48
Zitrax
  • 17,576
  • 17
  • 83
  • 103
  • 9
    `git show-ref --heads` lists the hash and the ref name so it (2nd line) will search twice. so `git show-ref --heads | cut -d' ' -f2` is better as it will only list the ref names. – hIpPy May 25 '17 at 06:27
  • 7
    I can't believe how many times this question has been asked and answered, yet you're the only one with the *correct* answer. – Sammitch Sep 29 '17 at 19:39
  • 4
    `git show-ref --heads -s` outputs the SHA1 hash only. Also, if there are multiple branches pointing to the same commit, you'll have duplicates. You can remove them with `sort -u`, like so `git show-ref --heads -s | sort -u | xargs git grep ...` – Steve Feb 23 '18 at 16:27
  • 2
    Here's the function I added to my bashrc. Hope it helps someone: `function gsearch { git grep $1 $(git show-ref --heads) | grep "refs/heads" | grep $1 } # last grep to keep grep color highlight` – AFP_555 Feb 05 '19 at 02:27
  • 4
    This should be the accepted answer. Grepping a string across all branches *but for the latest content only* is a very common use case. – dr_ Jul 11 '19 at 11:48
34

There are a few issues with the solutions listed here (even accepted).

You do not need to list all the hashes as you'll get duplicates. Also, it takes more time.

It builds on this where you can search a string "test -f /" on multiple branches master and dev as

git grep "test -f /" master dev

which is same as

printf "master\ndev" | xargs git grep "test -f /"

So here goes.

This finds the hashes for the tip of all local branches and searches only in those commits:

git branch -v --no-abbrev | awk -F' *' '{print $3}' | xargs git grep "string/regexp"

If you need to search in remote branches too then add -a:

git branch -a -v --no-abbrev | awk -F' *' '{print $3}' | xargs git grep "string/regexp"

Further:

# Search in local branches
git branch | cut -c3- | xargs git grep "string"

# Search in remote branches
git branch -r | cut -c3- | xargs git grep "string"

# Search in all (local and remote) branches
git branch -a | cut -c3- | cut -d' ' -f 1 | xargs git grep "string"

# Search in branches, and tags
git show-ref | grep -v "refs/stash" | cut -d' ' -f2 | xargs git grep "string"
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
hIpPy
  • 4,110
  • 5
  • 45
  • 59
  • 12
    at least for the search in all branches should be: `git branch -a | cut -c3- | cut -d' ' -f 1 | xargs git grep "string"` or it will fail with `->` symbol in files list, which denotes local to remote branches relation – Ilya Sheershoff Feb 01 '18 at 11:38
  • 1
    This comment, right here above my comment, is the correct answer! It's the only one that didn't error out. – user3147973 Jan 10 '21 at 17:47
  • Is there a way to get the branch names from this? – Stefan Mar 24 '21 at 15:36
  • Thanks @IlyaSheershoff ! You need to add `cut -d' ' -f 1` to the remote branch search as well. – Anurag Pande Apr 21 '22 at 20:56
16

You can try this:

git log -Sxxxx  # Search all commits
git log -Sxxxx  --branches[=<pattern>]   # Search branches
Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Victor Choy
  • 3,730
  • 23
  • 32
2

Following @peter-mortensen & manojlds's solution, I use git for-each-ref as subcommand to list only branches with name.

git grep "string/regexp" $(git for-each-ref --format='%(refname:short)' refs/heads)

This accomplish a better visualization, showing only named braches and making only one result for each branch.

Pierre.Vriens
  • 2,101
  • 75
  • 28
  • 42
0

To ignore case use -i:

git log -i --all --grep='word1 Word2'
joydeba
  • 490
  • 1
  • 6
  • 18