Home > Back-end >  How to `git grep` through a range of commits, all commits from the current commit back to the parent
How to `git grep` through a range of commits, all commits from the current commit back to the parent

Time:10-03

I'd like to be able to use git grep to do a regular expression search through:

  1. the current commit
  2. a given commit
  3. a list of commits
  4. a range of commits
  5. a given commit all the way back to the parent commit
  6. all commits (and branches) in the entire repo

To do 1, you just do this:

git grep -n "some regex search string"

To do 2, you do this:

git grep -n "some regex search string" commit_hash_or_branch_name

To do 3, you just list all the commits like this:

git grep -n "some regex search string" commit1 commit2 commit3 commit4

But, how do we do 4, 5, or 6?

I tried this for 4, for instance, but it does not work:

git grep -n "some regex search string" beginning_commit~..end_commit

I thought this pattern might work since it works for git cherry-picks, as I explain here, but, it doesn't work for git grep.

See also

  1. Related, but not the same thing, since it looks for a string change, not a string existence: How can I search my ENTIRE git repo's commit history for a string change?
  2. https://git-scm.com/docs/gitrevisions (thanks, @JohnKugelman)

CodePudding user response:

fwiw, a perhaps shorter way to feed "a range of commits" to git grep :

For 4. :

git grep -n -e pattern "$(git rev-list a..b)" --

For 5. :

git grep -n -e pattern "$parent" "$(git rev-list $parent..HEAD)" --

For 6. :

git grep -n -e pattern "$(git rev-list --all)" --

regarding your alternative solutions and hints :

B. : in git log, the -p option combines with -G pattern or -S pattern : only the files which contribute to the -G or -S filtering will be listed (makes for a shorter diff to scan through).

C. : beware of git log --grep pattern : it will search for the pattern in the commit messages, as opposed to git grep which searches in the content of the files in the commit.

CodePudding user response:

How to git grep through whichever branches or commits you choose

Alright, with much effort, I figured it out. Here is how to do 4, 5, and 6:

  1. a range of commits
  2. a given commit all the way back to the parent commit
  3. all commits (and branches) in the entire repo

4. git grep a range of commits

To search for "search string" within a range of commits, from commit1, inclusive, to commit2, inclusive, do this:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" commit1~..commit2)"; \
    git grep -n "search string" "${commit_array[@]}"

Here is an example that searches for "\bhey\b" (\b means "word boundary" in regular expressions) which you can run in my eRCaGuy_hello_world repo:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" f714d2fb18b00f0f423de98b6a7a418e42054cfe~..f396a4ae24153b35215335a1d6bf35ac843ad122)"; \
    git grep -n "\bhey\b" "${commit_array[@]}"

Explanation

IFS=$'\n' read -r -d '' -a myarray <<< "$multiline_string" converts a multiline string into a bash array named myarray. See my answer here: How to read a multi-line string into a regular bash "indexed" array.

To get a range of commits, you can use this trick:

git log --pretty=format:%H fist_commit~..last_commit

Source where I got the necessary hints to learn the git log --pretty=format:%H part: Get the short Git version hash.

Example usage and output, when in my eRCaGuy_hello_world repo. Here you can see the first commit I specify at the very bottom of the output, and the last commit at the very top:

eRCaGuy_hello_world$ git log --pretty=format:%H f714d2fb18b00f0f423de98b6a7a418e42054cfe~..f396a4ae24153b35215335a1d6bf35ac843ad122
f396a4ae24153b35215335a1d6bf35ac843ad122
d00f645cc56b5f4bf8b3c7b23c6ff62ca71734d7
0a795f56c2dd343e50d8c4f73f1347759ece9a08
f714d2fb18b00f0f423de98b6a7a418e42054cfe

5. git grep a given commit all the way back to the parent commit

This command is a subset of the command above, and is even simpler. To search for "search string" from commit, inclusive, all the way back to the first (most-parent) commit, do this:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" commit)"; \
    git grep -n "search string" "${commit_array[@]}"

Example from my eRCaGuy_hello_world repo:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" f396a4ae24153b35215335a1d6bf35ac843ad122)"; \
    git grep -n "\bhey\b" "${commit_array[@]}"

6. git grep a whole repository: ie: all commits (and branches) in the entire repo

Same as just above, except use --all in place of the branch name or commit hash:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" --all)"; \
    git grep -n "search string" "${commit_array[@]}"

Example from my eRCaGuy_hello_world repo:

IFS=$'\n' read -r -d '' -a commit_array <<< \
    "$(git log --pretty=format:"%H" --all)"; \
    git grep -n "\bhey\b" "${commit_array[@]}"

Alternative solutions and hints

A. To search for changes of a search pattern or word, you can do this:

  1. Search up from the current commit:
    git log -S "some string"
    
  2. Search all branches:
    git log --all -S "some string"
    

See: How can I search my ENTIRE git repo's commit history for a string change?.

B. To search through just the changes introduced by each commit, use git log -p and search interactively through the less viewer:

You can also search for a match in your history like this: git log -p, then press the / key, type your regular expression search string, and press Enter. Press n for "next match" or Shift n for "previous match". The -p in git log shows the changes in "patch" format for each commit. And, since the output of git log is viewed in the less viewer, you can use it to search the contents interactively.

C. Use git log --grep <regex> <branches>

...as described here: Search a whole Git repository.

  • Related