36

我正在尝试生成在特定提交中更改的文件的列表。问题是,每个文件在文件顶部的注释中都有版本号 - 由于此提交引入了新版本,这意味着每个文件都已更改

我不关心更改后的评论,所以我想git diff忽略所有匹配的行^\s*\*.*$,因为这些都是评论(/* */ 的一部分)。

我找不到任何方法来告诉git diff忽略特定的行。

我已经尝试设置一个 textconv 属性以使 Git 在对它们进行比较之前将文件传递给 sed,以便 sed 可以去除有问题的行 - 问题在于git diff --name-status它实际上并没有区分文件,只是比较了哈希值,当然,所有的哈希都改变了。

有没有办法做到这一点?

4

8 回答 8

21

这是一个对我来说效果很好的解决方案。我已经编写了解决方案以及有关该git (log|diff) -G<regex>选项的一些其他缺失文档。

它基本上使用与先前答案相同的解决方案,但专门针对以 a*或 a开头的注释,#有时在*... 之前有一个空格,但它仍然需要允许#ifdef,#include等更改。

-G该选项似乎不支持向前看和向后看,通常也不支持?,而且我在使用时也遇到了问题*+不过,似乎运作良好。

(注意,在 Git v2.7.0 上测试过)

多行注释版

git diff -w -G'(^[^\*# /])|(^#\w)|(^\s+[^\*#/])'
  • -w忽略空格
  • -G仅显示与以下正则表达式匹配的差异行
  • (^[^\*# /])任何不以星号、哈希或空格开头的行
  • (^#\w)任何#以字母开头的行
  • (^\s+[^\*#/])任何以空格开头后跟注释字符的行

基本上一个 SVN 钩子现在修改每个文件进出,并修改每个文件上的多行注释块。现在我可以将我的更改与 SVN 进行比较,而无需 SVN 在评论中删除的 FYI 信息。

从技术上讲,这将允许#TODO在 diff 中显示 Python 和 Bash 注释,如果除法运算符在 C++ 中的新行上开始,则可以忽略它:

a = b
    / c;

Git中的文档-G似乎也很缺乏,所以这里的信息应该会有所帮助:

git diff -G<regex>

-G<regex>

查找补丁文本包含匹配的添加/删除行的差异<regex>

为了说明 和 之间的区别-S<regex> --pickaxe-regex-G<regex>请考虑在同一文件中具有以下差异的提交:

+    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
...
-    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);

虽然git log -G"regexec\(regexp"将显示此提交, git log -S"regexec\(regexp" --pickaxe-regex但不会(因为该字符串的出现次数没有改变)。

有关更多信息,请参阅gitdiffcore (7)中的条目。

(注意,在 Git v2.7.0 上测试过)

  • -G使用基本的正则表达式。
  • 不支持?, *, !, {,}正则表达式语法。
  • 分组()和 OR-ing 组适用于|.
  • 支持通配符,例如\s,\W等。
  • 支持前瞻和后瞻。
  • 开始和结束线锚^$工作。
  • 自 Git 1.7.4 起该功能已可用。

排除的文件 v 排除的差异

请注意,该-G选项过滤将被区分的文件。

但是如果一个文件被“差异化”,那些之前被“排除/包含”的行都将显示在差异中。

例子

仅显示至少有一行提及foo.

git diff -G'foo'

显示除以 a 开头的行之外的所有内容的文件差异#

git diff -G'^[^#]'

显示有差异的文件提及FIXMETODO

git diff -G`(FIXME)|(TODO)`

另见git log -G, git grep, git log -S, --pickaxe-regex, 和--pickaxe-all

更新:-G 选项正在使用哪个正则表达式工具?

https://github.com/git/git/search?utf8=%E2%9C%93&q=regcomp&type=

https://github.com/git/git/blob/master/diffcore-pickaxe.c

if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
    int cflags = REG_EXTENDED | REG_NEWLINE;
    if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE))
        cflags |= REG_ICASE;
    regcomp_or_die(&regex, needle, cflags);
    regexp = &regex;

// and in the regcom_or_die function
regcomp(regex, needle, cflags);

http://man7.org/linux/man-pages/man3/regexec.3.html

   REG_EXTENDED
          Use POSIX Extended Regular Expression syntax when interpreting
          regex.  If not set, POSIX Basic Regular Expression syntax is
          used.

// ...

   REG_NEWLINE
          Match-any-character operators don't match a newline.

          A nonmatching list ([^...])  not containing a newline does not
          match a newline.

          Match-beginning-of-line operator (^) matches the empty string
          immediately after a newline, regardless of whether eflags, the
          execution flags of regexec(), contains REG_NOTBOL.

          Match-end-of-line operator ($) matches the empty string
          immediately before a newline, regardless of whether eflags
          contains REG_NOTEOL.
于 2016-02-09T20:11:07.063 回答
15
git diff -G <regex>

并指定一个与您的版本号行匹配的正则表达式。

于 2014-02-10T10:19:35.230 回答
10

git difftool我发现启动外部差异工具最容易使用:

git difftool -y -x "diff -I '<regex>'"
于 2015-06-23T13:30:50.430 回答
4

我找到了解决方案。我可以使用这个命令:

git diff --numstat --minimal <commit> <commit> | sed '/^[1-]\s\+[1-]\s\+.*/d'

显示在提交之间更改了多行的文件,这消除了唯一更改是注释中的版本号的文件。

于 2013-05-13T17:05:53.457 回答
2

在“git diff”输出上使用“grep”,

git diff -w | grep -c -E "(^[+-]\s*(\/)?\*)|(^[+-]\s*\/\/)"

可以单独计算注释行更改。(一个)

使用 'git diff --stat' 输出,

git diff -w --stat

可以计算所有线路变化。(乙)

要获得非注释源行更改 (NCSL) 计数,请从 (B) 中减去 (A)。

解释:

在 'git diff' 输出中(其中空白更改被忽略),

  • 寻找以“+”或“-”开头的行,这意味着修改的行。
  • 在此之后可以有可选的空白字符。'\s*'
  • 然后寻找注释行模式'/*'(或)只是'*'(或)'//'。
  • 由于 grep 给出了“-c”选项,因此只需打印计数。删除“-c”选项以在差异中单独查看评论。

注意:由于以下假设,注释行数可能会出现小错误,结果应视为大致数字。

  • 1.) 源文件基于 C 语言。Makefile 和 shell 脚本文件有不同的约定,'#',表示注释行,如果它们是 diffset 的一部分,它们的注释行将不被计算在内。

  • 2.) 换行的 Git 约定:如果修改了一行,Git 会将其视为删除了该特定行并在其中插入了新行,并且看起来像是更改了两行,而实际上修改了一行。

     In the below example, the new definition of 'FOO' looks like a two-line change.
    
     $  git diff --stat -w abc.h
     ...
     -#define FOO 7
     +#define FOO 105
     ...
     1 files changed, 1 insertions(+), 1 deletions(-)
     $
    
  • 3.) 与模式不匹配的有效注释行(或)与模式匹配的有效源代码行可能会导致计算错误。

在下面的示例中,不以“*”开头的“+ blah blah”行不会被检测为注释行。

           + /*
           +  blah blah
           + *
           + */

在下面的示例中,“+ *ptr”行将被视为注释行,因为它以 * 开头,尽管它是有效的源代码行。

            + printf("\n %p",
            +         *ptr);
于 2016-08-22T11:03:25.977 回答
1

对于大多数语言,要正确执行此操作,您必须解析原始源文件/ast,并以这种方式排除注释。

一个原因是多行注释的开头可能没有被 diff 覆盖。另一个原因是语言解析不是微不足道的,而且经常有一些事情会绊倒一个幼稚的解析器。

我打算为 python 做这件事,但字符串黑客足以满足我的需要。

对于 python,您可以使用自定义过滤器忽略注释和尝试忽略文档字符串,例如:

https://gist.github.com/earonesty/f76dec337ee64c5ae23c2be1557535a4

可以对该代码进行简单的修改以生成文件名,而不是计数。

但它当然会错误地将部分文档字符串计为“代码”(这不适用于覆盖等)。

于 2020-12-09T19:49:56.517 回答
0

也许是这样的 Bash 脚本:

#!/bin/bash
git diff --name-only "$@" | while read FPATH ; do
    LINES_COUNT=`git diff --textconv "$FPATH" "$@" | sed '/^[1-]\s\+[1-]\s\+.*/d' | wc -l`
    if [ $LINES_COUNT -gt 0 ] ; then
        echo -e "$LINES_COUNT\t$FPATH"
    fi
done | sort -n
于 2013-11-07T18:50:52.773 回答
0

我使用 meld 作为工具通过设置其选项来忽略注释,然后使用 meld 作为 difftool:

git difftool --tool=meld -y
于 2022-01-04T08:16:04.067 回答