I would like to search git log for a specific string to find where it was modified/deleted/added.
I knew that I can do this using pickaxe
(i.e., git log -S"Text"
)
When I run this command, only the names of the commits where the change has occurred are shown. I also tried git log -G"Text"
but no difference.
When I tried git log -p -S"Text"
, it showed me full comparision between two versions of files.
Is there a way to spot the exact location (line) where the text has been changed/added/deleted?
I checked related posts 1, 2 but didn't answer my question. I'm working on Windows OS.
Git doesn't provide a built-in way to do this, but you could use the capabilities of your pager to find the text. For example, if you're using less, you can run git log -p -G"Text"
. Then you can search for "Text" in the output of this command by typing /Text
. This is by far the easiest way to find what you're looking for.
There is an alternative, rather ugly way to hack around this which might meet your needs. If you use --word-diff-regex=Text
, Git will produce an alternative word-diff format which will highlight from one instance of "Text" to the next, or just the single instance if only one exists. Again, using your pager is going to be a lot nicer and easier to read.
Another way to achieve your goal would be to use an external diff driver, and the grepdiff
utility from the patchutils
package. (I answered a similar question for Linux here).
Caveat: I don't use Windows, and it seems patchutils
is not available in the usual Windows package managers. So a prerequisite to this answer would be to compile grepdiff
yourself (which might be too much :P). If you can do that, and find a way to install it inside Git Bash, then this approach might work.
First create a script under C:\Program Files\Git\usr\bin
and name it pickaxe-diff
(Note: again, I don't use Windows, but I'm reading on SO that this is an easy way to add stuff to your Git Bash PATH
) :
#!/bin/bash
# pickaxe-diff : external diff driver for Git.
# To be used with the pickaxe options (git [log|show|diff[.*] [-S|-G])
# to only show hunks containing the searched string/regex.
path=$1
old_file=$2
old_hex=$3
old_mode=$4
new_file=$5
new_hex=$6
new_mode=$7
filtered_diff=$(diff -u -p $old_file $new_file | \
grepdiff "$GREPDIFF_REGEX" --output-matching=hunk | \
grep -v -e '+++ ' -e '--- ')
a_path="a/$path"
b_path="b/$path"
echo "diff --git $a_path $b_path"
echo "index $old_hex..$new_hex $old_mode"
echo "--- $a_path"
echo "+++ $b_path"
echo "$filtered_diff"
Call git log -G
and tell Git to use the pickaxe-diff
script as an external diff driver:
export GREPDIFF_REGEX=<string>;
GIT_EXTERNAL_DIFF=pickaxe-diff git log -p --ext-diff -G $GREPDIFF_REGEX
This will use the pickaxe-diff script just to generate the diffs, so the rest of the git log
output (commit hash, message, etc) will be untouched.
Caveat
The way that the Git pickaxe work is that it limits the output to the files whose hunks change the given string/regex. This means that if another hunk in these files also contain the search string/regex, but does not change it, it will still be displayed with the above script. This is a limitation of grepdiff
(up to version 0.3.4). A recently merged pull request at the patchutils project added an --only-match
flag to grepdiff
, which should provide the needed functionality to correctly filter out these hunks (you would need patchutils 0.4.0 or higher).
I did a write-up of my solution in this gist.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With