如何使用grep查找git diff的差异?

113
有没有一种方法可以按给定模式显示经过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} "

但愿能找到一个命令来实现这个功能。

3
据称,名为git-diff-grep的Github项目做了完全不同的事情。 - Kuba
请参见 https://dev59.com/XWsy5IYBdhLWcg3w8CfJ#35434714。 - cambunctious
https://unix.stackexchange.com/questions/628695/grep-within-a-git-diff-but-show-file-and-line-number 相关。 - Marc Durdin
这个链接是关于在git diff中使用grep命令并显示文件和行号的问题。 - undefined
12个回答

139

不确定,但是 git diff -G <regex> 命令行参数应该没问题吧?

-G < regex>

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

23
并不完全符合我的要求,我只想看到与该模式匹配的行,而不是包含与该模式更改的整个文件差异。 - Kuba
那么我猜你的解决方案就是最简单的了。 - CharlesB
至少 git diff -G 比完整的 git diff 更好作为第一步。 - Kuba
1
我的版本没有-G标志。它被移除了吗? - RedX
8
使用以下命令可以查看受正则表达式影响的文件:git diff -G <regex> --raw - Raman
显示剩余3条评论

20

你尝试过 git diff -S<string> 或者 git diff -G".*string.*" 吗?请注意它们并不等同,具体细节请查看pickaxe相关文档以了解-S选项的作用。


git diff -S<string> 对我很有用,因为它只返回与给定字符串匹配的更改的文件差异。谢谢! - ethaning

12

另一个可能的方法是查看整个差异并使用正常的less命令搜索输出(键入/然后输入模式)。

当您将less配置为在匹配项之前显示一些行时,可以使用--jump-target=N非常有用。尝试像这样使用它:

PAGER="/usr/bin/less --jump-target=10" git diff
这意味着匹配项应该显示在第10行(向上显示9行上下文),这可能足以看到文件名。
您还可以使用例如--jump-target=.5,使其将匹配项定位在屏幕中间。

7

我使用git log -p,它会打开一个可配置的 less,可以使用/进行搜索。还可以使用git log -S<searchword>


我熟悉这些 - 但我不是在看历史记录,而是在关注当前的工作变化。 - Kuba
好的,那么可以使用与robinst引用相同的方法。如果您想要输出,您可能可以使用任何使用libgit2或gitgui/gitk的库。 - chelmertz

5

我认为您对“grep”diff输出的处理方式是最好的解决方法。

您可以通过使用sed改进awk脚本:

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: 用于匹配终端上有颜色的行的正则表达式
  • marker: 用于匹配来自不同diff块之间的分隔符,以带有颜色的"diff"开头的行为标记
  • pattern: 用于搜索以颜色为"+"或"-"开头并包含"PATTERN"的行的模式

这将打印完整的差异块,包括添加或删除了PATTERN,并保持有用的颜色输出。

请注意,在colored中的^[应该是实际的、字面的^[。您可以通过按Ctrl+VCtrl+[在bash中键入它们。


2
对于任何尝试在除了bash shell之外的其他地方输入此命令(例如在IDE中编辑.sh文件)的人来说,必须将“ ^ [”替换为像这样的内容:colored = $'(\e\[[0-9;]*[a-zA-Z])';并且在macOS上,您还需要brew install gnu-sed和将sed --> gsed进行替换。 - Anentropic
1
这些和@Leon的答案是唯一没有变通方法的答案。限制在于它会打印整个文件差异,而不仅仅是匹配的hunks。 - eddygeek

3
以下是一个自定义的差异比较工具,可以在更改中搜索(但不包括上下文):
使用方法:
GIT_EXTERNAL_DIFF="mydiff --grep foo" git diff
这将输出您更改中包含foo的行(包括因更改而消失的foo行)。任何grep模式都可以代替foo。
每个输出行都以以下前缀开头:
filename: oldlinenum: newlinenum|

该脚本也可以不使用--grep选项,此时它会像上面所述一样提供完整上下文的差异格式。 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

3
我一直使用这个工具,非常满意 :)
grep -ri <MY_PATTERN> $(git diff 790e26393d --name-only) 

1

这对我很有帮助,希望能帮到其他人:

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

1
在Windows上,一个简单的解决方案是:

git diff -U0 | findstr string

如果您想按文件名进行分组,请使用此选项。
FOR /F "usebackq delims==" %i IN (`git diff --name-only`) do git diff -U0 %~fi | findstr string

0

提供的解决方案并不完全符合我的需求,但这个解决方案解决了我的问题。

(
    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

我无法让行号起作用,但实际上我并不需要它们来使它们工作。对我来说,前置的文件名已经足够了。


我的确切问题:

查找所有添加了from __future__ import ..-*- coding: utf-8 -*-的70多个文件。

(
    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/)'

输出结果如下:

....
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
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接