【问题标题】:How to make 'git diff' ignore comments如何让“git diff”忽略评论
【发布时间】:2013-05-13 16:52:29
【问题描述】:

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

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

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

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

有没有办法做到这一点?

【问题讨论】:

  • 一个疯狂的猜测......你试过git diff --name-status --textconv吗?或者git diff --name-only
  • 是的,我正在使用 --name-only,但它会返回(就像我说的那样)每个文件,因为每个文件的 cmets 都已更改。 --textconv 不起作用,因为正如我在帖子中所说,git 在不产生完整差异时会忽略它。
  • @richvdh 我认为这些问题足够相似,可以被认为是重复的,但是它们有不同的正确答案,而且这个问题还有其他答案,提出了其他 Q 没有的建议,所以我相信那里保留两者的价值。
  • 相关:Git 2.30 (Q1 2021) 将提议git diff -I<regex>

标签: git diff git-diff


【解决方案1】:

这是一个适合我的解决方案。我已经在 git (log|diff) -G<regex> 选项上编写了解决方案和一些额外的缺失文档。

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

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

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

多行注释版

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

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

从技术上讲,这将允许 Python 和 Bash cmets(如 #TODO)显示在差异中,如果除法运算符在 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'

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

git diff -G'^[^#]'

显示提及FIXMETODO 的不同文件

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

另请参阅git log -Ggit grepgit 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.

【讨论】:

  • 看起来类似于“简单正则表达式”。 en.wikibooks.org/wiki/Regular_Expressions/…
  • 这不可能完全正确,因为它接受一些不简单的语法,例如+(我刚刚测试过)。
  • 在我的回答接近尾声时查看更新。我还没有成功测试“POSIX 扩展正则表达式”。我的经验测试表明它的工作原理并不完全相同。
  • @phyatt - 这似乎不起作用:git diff -G'^[^#]'。它仍然显示以# 开头的行。
  • @MartinVegter 如果文件至少有其他差异,语法仍会显示。如果文件仅存在注释差异,则该文件将被排除在结果中。
【解决方案2】:
git diff -G <regex>

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

【讨论】:

    【解决方案3】:

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

    git difftool -y -x "diff -I '<regex>'"
    

    【讨论】:

      【解决方案4】:

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

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

      显示在两次提交之间更改了多行的文件,从而消除了唯一更改的是 cmets 中的版本号的文件。

      【讨论】:

        【解决方案5】:

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

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

        可以单独计算注释行的变化。 (一)

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

        git diff -w --stat
        

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

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

        说明:

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

        • 注意以“+”或“-”开头的行,表示修改的行。
        • 后面可以有可选的空白字符。 '\s*'
        • 然后查找注释行模式“/*”(或)只是“*”(或)“//”。
        • 由于 grep 提供了“-c”选项,因此只需打印计数即可。删除 '-c' 选项以在差异中单独查看 cmets。

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

        • 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);
        

        【讨论】:

          【解决方案6】:

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

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

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

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

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

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

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

          【讨论】:

            【解决方案7】:

            可能是这样的 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
            

            【讨论】:

              【解决方案8】:

              我使用 meld 作为工具通过设置它的选项来忽略 cmets, 然后使用 meld 作为 difftool:

              git difftool --tool=meld -y
              

              【讨论】:

                猜你喜欢
                • 2018-08-14
                • 2010-12-25
                • 1970-01-01
                • 2011-03-18
                • 1970-01-01
                • 2014-11-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多