spraff
spraff

Reputation: 33385

How do I search within changes against the last Git checkout/commit?

A change in my working copy of a Git repository caused an error which mentions a variable foo and I want to find which files and lines contain a changed line in my working copy which mentions foo.

If I do git diff path/file it launches a visual editor, so I can't grep in the shell. Even if that worked, it would omit the name of the file in question.

This answer seems closer to what I want, but it is overstuffed with irrelevant examples using terminology I don't really understand.

I don't want to search through any history (i.e. commits), I want to search for foo within the changes that I currently made since the last checkout/commit.

"Hey, Git, what did I change which contains foo and hasn't been committed yet? File names and line numbers, please."

How do I do this?

(I'd also appreciate being able to ask the same question of a visual diff editor, if you happen to know one on Linux which does this.)

Upvotes: 2

Views: 63

Answers (2)

Leon
Leon

Reputation: 32444

The following script will do almost what you want:

Usage

GIT_EXTERNAL_DIFF="mygitdiff --grep foo" git diff

This will output those lines in you changes that contain foo (including lines where foo disappeared because of your changes).

Each output line starts with the following prefix:

filename: oldlinenum: newlinenum|

The script can also be used without the --grep option, in which case it simply formats the full diff (i.e. providing full context) as described above.

Note that if you have both staged and unstaged changes, to achieve the desired effect you must run git diff against HEAD:

GIT_EXTERNAL_DIFF="mygitdiff --grep foo" git diff HEAD

mygitdiff

#!/bin/bash

my_diff()
{
    diff --old-line-format="$1"':%6dn:      |-%L'     \
         --new-line-format="$1"':      :%6dn|+%L'     \
         --unchanged-line-format="$1"':%6dn:%6dn| %L' \
         $2 $3
}

if [[ $1 == '--grep' ]]
then
    pattern="$2"
    shift 2
    my_diff "$1" "$2" "$5"|grep --color=never '^[^|]\+|[-+].\+'"$pattern"'.*'
else
    my_diff "$1" "$2" "$5"
fi

exit 0

CREDITS Scott Weldon helped to test this script and found a bug in its initial version. Thank you Scott!

Upvotes: 1

Scott Weldon
Scott Weldon

Reputation: 10217

A normal git diff command will give you the file names and line numbers, so if we can limit it properly that should do the job. You could do:

git diff -G foo

But this will not work if you have staged changes. (You can use the --cached argument to only check staged changes, but that won't include unstaged changes.)

This should work better:

git diff-index -G foo -p -U0 HEAD

Both of these commands will include all changes in matching files, not just changes containing foo, but otherwise should do what you are asking.

Note also that both of these commands will include the patch output, so if you have a large set of changes it will require some extra scrolling. In addition, to get exact line numbers, you'll need to do some simple math if there are other changes in the same hunk. E.g. if you see this:

@@ -101,6 +103,8 @@
+  int bar = 1;
+  int baz = 2;
+  int foo = 3;

That means the top of the hunk was line 101, and is now line 103, and thus the foo line is now line 105.

Upvotes: 2

Related Questions