如何抓住机会?

是否有方法显示由给定模式过滤的 git-diff。

差不多

git grepdiff pattern


changed file
+++ some sentence with pattern
changed file 2
--- some other pattern

不幸的是,最简单的解决方案还不够好

git diff | grep pattern


+++ some sentence with pattern
--- some other pattern
# not an option as doesn't put the filename close to the match

我用 awk 解决了这个问题

git diff | awk "/\+\+\+/{f = \$2}; /PATTERN/ {print f \$0} "

但我很想知道有没有这样的命令。

42720 次浏览

Not sure but isn't git diff -G <regex> flag OK?

-G < regex>

Look for differences whose added or removed line matches the given <regex>.

I use git log -p, which opens less (configurable, though), which in turn can be searched for with /. There's also git log -S <searchword>.

Have you tried git diff -S<string> or git diff -G".*string.*"? Note that they are not equivalent, see the documentation about pickaxe for what -S does.

Another possibility would be to view the whole diff and search the output using the normal less commands (type / and then the pattern).

When you have less configured to show some lines before the match using --jump-target=N, this is pretty useful. Try it like this:

PAGER="/usr/bin/less --jump-target=10" git diff

This means that the match should be shown on line 10 (shows 9 lines of context above), which may be enough to also see the file name.

You can also use e.g. --jump-target=.5 to make it position the match in the middle of the screen.

Here is a custom diff tool that allows grepping inside changes (but not the context):

Usage

GIT_EXTERNAL_DIFF="mydiff --grep foo" git diff

This will output those lines in your changes that contain foo (including lines where foo disappeared because of your changes). Any grep pattern can be used instead of foo.

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.

mydiff

#!/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

On Windows, a simple solution is:

git diff -U0 | findstr string

If you want grouping by filename, use this

FOR /F "usebackq delims==" %i IN (`git diff --name-only`) do git diff -U0 %~fi | findstr string

This did the job for me, I hope it will help someone:

git diff | grep  -P '^\+|^\-'

I think your approach to "grep" diff output is the best workaround.

You may improve your awk script by using sed:

colored="(^[\[[0-9;]*[a-zA-Z])"
marker="^$colored+diff"
pattern="^$colored+.*(\+|\-).*PATTERN"
git diff --color | sed -rn -e "/$marker/! H; /$marker/ ba; $ ba; b; :a; x; /$pattern/ p"
  • colored: regex to match terminal colored lines
  • marker: marker to match division from differents diff hunks, lines starting with colored "diff"
  • pattern: pattern to search for, lines starting with colored "+" or "-" and containing "PATTERN"

This will print full diff hunks, with added or removed PATTERN, also maintaining useful colored output.

Note that ^[ in colored should be actual, literal ^[. You can type them in bash by pressing Ctrl + V, Ctrl + [

I have been using this with great satisfaction :)

grep -ri <MY_PATTERN> $(git diff 790e26393d --name-only)

The solutions offered didn't exactly fit my needs, this solved my issue.

(
START_DIFF=abc123
END_DIFF=123dcf


# loop over all the files that have changed inside the diff
# you can add a `| grep '<ext>$'` to the end of `--name-only`
# if you need to be more aggresive with the filtering / or
# make it go faster...
for file in $(git diff $START_DIFF $END_DIFF --name-only); do
# loop over every line of the diff FOR that file.
while IFS= read -r line; do
# prepend the file name to every line
echo "$file:$line"
done < <(git diff $START_DIFF $END_DIFF $file)
done
) | grep what-youre-looking-for

I could not get the line numbers working, but I didn't really need them to get them to work. The prepended filename was enough for me.


My exact issue:

Find all the files that added either a from __future__ import .. or a -*- coding: utf-8 -*- out of 70+ files.

(
START_DIFF=branch-a
END_DIFF=HEAD
for file in $(git diff $START_DIFF $END_DIFF --name-only); do
while IFS= read -r line; do
echo "$file:$line"
done < <(git diff $START_DIFF $END_DIFF $file)
done
) | grep ':+' | awk '(/import/ && /__future/) || (/coding/)'

The output looks like this:

....
app/tests/test_views.py:+# -*- coding: utf-8 -*-
app/tests/test_views.py:+from __future__ import absolute_import
app/tests/test_views.py:+from __future__ import division
app2/tests/test_views.py:+from __future__ import division
...

Ugly, potentially inaccurate, but might be enough to get by if you're just eyeballing it:

git diff origin/master | grep -Eo "(^diff.*|some-keyword)"

Where file1.txt contains some-keyword once, and file3.txt contains some-keyword 3 times:

Outputs:

diff --git a/path/to/file1.txt b/path/to/file1.txt
some-keyword
diff --git a/path/to/file2.txt b/path/to/file2.txt
diff --git a/path/to/file3.txt b/path/to/file3.txt
some-keyword
some-keyword
some-keyword
diff --git a/path/to/file4.txt b/path/to/file4.txt