Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search for text in git commit range

Tags:

git

I need to search for text "TEXT" in past 20 git commits (or range)
I need to see filename and line number where I have a match.

Note:
I want to search commits content, but not project on that commit as git grep does.
By commits content I mean what I can see using git diff HEAD^^^..HEAD

Closest I got is using git log --raw -GTEXT, it shows me commit which contains "TEXT" in commit content and show file names. Still no line numbers however.

And with some piping
git diff $(git log -n 20 --pretty=format:%h -GTEXT) | grep -E 'TEXT|\+\+\+|@@\s'
It is still somewhat wordy and with a lot of noise, if you have better solution, please answer.

like image 346
logcat Avatar asked Aug 01 '16 07:08

logcat


People also ask

How do I search for a word in git?

Git Grep. Git ships with a command called grep that allows you to easily search through any committed tree, the working directory, or even the index for a string or regular expression. For the examples that follow, we'll search through the source code for Git itself.

How do I find a specific commit in git?

To find out which files changed in a given commit, use the git log --raw command. It's the fastest and simplest way to get insight into which files a commit affects.

How do you check all commits of a single file?

On Linux you can use gitk for this. It can be installed using "sudo apt-get install git-gui gitk". It can be used to see commits of a specific file by "gitk <Filename>".


2 Answers

Get the last 20 commits:

git log -n 20

Get each to an associative array:

declare -A COMMITS # Declare associative array
COMMITNUMBER=0

while read -r line; do # For each line, do
    # Add 1 to COMMITNUMBER, if the current line contains "commit [0-9a-f]* (e.g., new associative array index for each commit)
    # As this'll happen straight way, our array is technically 1-indexed (starts from "${COMMITS[1]}", not "${COMMITS[0]}")
    REGEX="commit\s[0-9a-f]*"
    [[ "$line" =~ $REGEX ]] && COMMITNUMBER=$(( COMMITNUMBER+1 ))

    # Append the commit line to the index
    COMMITS[$COMMITNUMBER]="${COMMITS[$COMMITNUMBER]} $line"
done < <(git log -n 20)

Then iterate:

for i in "${!COMMITS[@]}"
do
  echo "key  : $i"
  echo "value: ${COMMITS[$i]}"
done

This gives similar output to below:

key  : 1
value:  commit 778f88ec8ad4f454aa5085cd0c8d51441668207c Author: Nick <[email protected]> Date:   Sun Aug 7 11:43:24 2016 +0100  Second commit 
key  : 2
value:  commit a38cd7b2310038af180a548c03b086717d205a61 Author: Nick <[email protected]> Date:   Sun Aug 7 11:25:31 2016 +0100  Some commit

Now you can search each in the loop using grep or anything, and match what you need:

for i in "${!COMMITS[@]}"
do
    REGEX="Date:[\s]*Sun\sAug\7"
    if [[ "${COMMITS[$i]}" =~ $REGEX ]]; then
        echo "The following commit matched '$REGEX':"
        echo "${COMMITS[$i]}"
    fi
done

Altogether:

search_last_commits() {
    [[ -z "$1" ]] && echo "Arg #1: No search pattern specified" && return 1
    [[ -z "$2" ]] && echo "Arg #2: Number required for number of commits to return" && return 1

declare -A COMMITS # Declare associative array
COMMITNUMBER=0

    while read -r line; do # For each line, do
        # Add 1 to COMMITNUMBER, if the current line contains "commit [0-9a-f]* (e.g., new associative array index for each commit)
        # As this'll happen straight way, our array is technically 1-indexed (starts from "${COMMITS[1]}", not "${COMMITS[0]}")
        REGEX="commit\s[0-9a-f]*"
        [[ "$line" =~ $REGEX ]] && COMMITNUMBER=$(( COMMITNUMBER+1 ))

        # Append the commit line to the index
        COMMITS[$COMMITNUMBER]="${COMMITS[$COMMITNUMBER]} $line"
    done < <(git log -n $2)

    for i in "${!COMMITS[@]}"
    do
        REGEX="$1"
        if [[ "${COMMITS[$i]}" =~ $REGEX ]]; then
            echo "The following commit matched '$REGEX':"
            echo "${COMMITS[$i]}"
        fi
    done
}

EDIT:

Usage:

search_last_commits <search-term> <last-commits-quantity>

Example:

search_last_commits "Aug" 20
like image 129
Nick Bull Avatar answered Sep 19 '22 15:09

Nick Bull


If you want to search through modified files in the range (and don't care about matching lines of the files that weren't actually modified in those commits) you can run:

git grep TEXT -- $(git diff --name-only RANGE)
like image 35
sdeleon28 Avatar answered Sep 17 '22 15:09

sdeleon28