使“ git log”忽略某些路径的更改


121

如何git log仅显示更改后的提交而不是指定的提交?

使用git log,我可以过滤我看到的那些接触给定路径集的提交。我想要的是反转该过滤器,以便仅列出触摸路径而不是指定路径的提交。

我可以得到想要的东西

git log --format="%n/%n%H" --name-only | ~/filter-log.pl | git log --stdin --no-walk

在哪里filter-log.pl

#!/usr/bin/perl
use strict;
use warnings;

$/ = "\n/\n";
<>;

while (<>) {
    my ($commit, @files) = split /\n/, $_;

    if (grep { $_ && $_ !~ m[^(/$|.etckeeper$|lvm/(archive|backup)/)] } @files) {
        print "$commit\n";
    }
}

除了我想要比这更优雅的东西。

请注意,我并不是在问如何使git忽略文件。这些文件被跟踪并提交。只是大多数时候,我对看到它们都不感兴趣。

相关问题:如何反转git log --grep = <pattern>或如何显示与模式不匹配的git日志除了提交消息而不是路径以外,这是相同的问题。

从2008年开始就此主题进行的论坛讨论:Re:从git-diff中排除文件这看起来很有希望,但是线程似乎已经枯竭。


我不确定是否有内置方法,您的perl解决方案看起来还不错。如果您修改它以接受路径作为命令行参数,则可以创建一个类似的别名!f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; f,甚至将该管道部分也包装到脚本中。
卡斯卡贝尔2011年

作为一种解决方法,我使用find筛选出不想看到其提交的目录。如果我想忽略对根目录SiteConfig进行的提交中的日志条目,那么我会说:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman 2012年

对于Git 1.9 / 2.0(2014年第1季度),请参见以下答案git log --oneline --format=%s -- . ":!sub"可以工作(使用pathspec魔术:(exclude)及其简短形式:!
VonC

Answers:


214

现在(GIT 1.9 / 2.0,Q1 2014)与所述引入被实现pathspec魔术:(exclude)和其短形式:!提交ef79b1f提交1649612,通过 阮泰玉维战(pclouds,文档可以找到这里

现在,您可以记录除子文件夹内容以外的所有内容:

git log -- . ":(exclude)sub"
git log -- . ":!sub"

或者您可以排除该子文件夹中的特定元素

  • 特定文件:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • 内的任何给定文件sub

      git log -- . ":(exclude)sub/*file"
      git log -- . ":!sub/*file"
      git log -- . ":(exclude,glob)sub/*/file"

您可以使排除大小写不敏感!

git log -- . ":(exclude,icase)SUB"

正如肯尼·埃维特(Kenny Evitt)所说

如果您在Bash Shell中运行Git,请使用':!sub'":\!sub"代替以避免bash: ... event not found错误


注:Git的2.13(Q2 2017)将同义词添加^!

参见Linus Torvalds(提交859b7f142ebeb9(2017年2月8日(通过合并JUNIOÇ滨野- -提交015fba3 2月27日2017)torvalds
gitster

pathspec魔术:将“ ^” 添加为“ !”的别名

!为否定的pathspec 选择' '不仅不符合我们对修订的要求,而且对于Shell扩展也是一个可怕的特征,因为它需要引用。

因此,将' ^' 添加为排除pathspec条目的替代别名。


请注意,在Git 2.28(2020年第三季度)之前,使用负路径规范收集工作树中包括未跟踪路径在内的路径时已被打破。

参见Elijah Newren()的commit f1f061e(2020年6月5 newren
(由Junio C gitsterHamano合并--commit 64efa11中,2020年6月18日)

dir:修复否定路径规格的处理

报道人:约翰·
米利金签名人:伊利亚·纽伦

do_match_pathspec()match_pathspec_depth_1()正确的角度开始生活,只应该从正确的角度出发match_pathspec_depth() match_pathspec_depth()后来改名为match_pathspec(),因此今天我们期望不变的是do_match_pathspec()在之外没有直接的调用者match_pathspec()

不幸的是,这一意图与这两个功能的重命名丢失,以及额外的通话do_match_pathspec()是在提交加入75a6315f74(“ ls-files:添加pathspec匹配的子模块”,2016年10月7日,Git的v2.11.0-RC0 - 合并上市第11批)和89a1f4aaf7(“ dir:如果我们的pathspec可能与目录下的文件匹配,请递归到该目录中”,2019-09-17,Git v2.24.0-rc0)。

当然,do_match_pathspec()有一个重要的优势match_pathspec()- match_pathspec()将标志硬编码为两个值之一,而这些新的调用者需要为标志传递其他值。

同样,尽管do_match_pathspec()直接调用是不正确的,但是可观察到的最终输出可能没有任何区别,因为该错误仅意味着fill_diretory()可以递归到不需要的目录中。

由于对目录下各个路径的后续do-this-path-match检查会导致那些多余的路径被过滤掉,因此使用错误函数的唯一区别是不必要的计算。

那些不好的声音中的第二个do_match_pathspec()涉及到了-通过直接移动或通过复制+编辑-到许多后来的重构中。

参见commits 777b420347(“ dir:同步treat_leading_path()read_directory_recursive()”,2019-12-19,Git v2.25.0-rc0- merge),8d92fb2927(“ dir:用线性算法代替指数算法”,2020-04-01,Git v2.27.0 -rc0- 合并批次#5中列出)和95c11ecc73(“容易出错的fill_directory()API;使其仅返回匹配项”,2020-04-01,Git v2.27.0-rc0- 合并批次#5中列出) 。

其中的最后一个介绍了do_match_pathspec()在单个文件上的用法,因此导致返回不应该返回的单个路径。

调用do_match_pathspec()而不是调用的问题match_pathspec()是,诸如'`:!unwanted_pa​​th``之类的任何否定模式都将被忽略

添加一个新match_pathspec_with_flags()功能以满足指定特殊标志的需求,同时仍然正确检查取反的模式,在上面添加一个大注释do_match_pathspec()以防止其他人滥用它,并更正当前的调用方do_match_pathspec(),而不是使用match_pathspec()match_pathspec_with_flags()

最后一点是,DO_MATCH_LEADING_PATHSPEC在使用时需要特别考虑DO_MATCH_EXCLUDE

关键DO_MATCH_LEADING_PATHSPEC是如果我们有一个pathspec

*/Makefile

我们正在检查目录路径,例如

src/module/component

我们想将其视为匹配项,以便我们递归到该目录中,因为它_might_在Makefile下面的某个位置有一个文件。

但是,当我们使用排除模式时,即我们有一个pathspec

:(exclude)*/Makefile

我们不想说像这样的目录路径

src/module/component

(负)匹配。

尽管在该目录下的某个位置可能有一个名为“ Makefile”的文件,但也可能有其他文件,我们不能抢先排除该目录下的所有文件;我们需要递归然后检查单个文件。

调整DO_MATCH_LEADING_PATHSPEC逻辑以仅针对正路径规格激活。


7
你可以做多个文件吗?
贾斯汀·托马斯

12
@JustinThomas我相信(尚未测试)您可以多次重复该路径排除模式":(exclude)pathPattern1" ":(exclude)pathPattern2",从而忽略多个文件夹/文件。
VonC 2014年

7
如果您在Bash Shell中运行Git,请':!sub'改用以避免bash: ... event not founderror":\!sub"不起作用。
肯尼·埃维特

1
@KennyEvitt感谢您的编辑和评论。我将后者包括在答案中以提高可见度。
VonC

2
对于那些想知道有关此功能的正式文档在哪里的人,请参阅git help glossary(我在git help -g[我在git help]中建议的列在其中)。
ravron

4

tl; dr: shopt -s extglob && git log !(unwanted/glob|another/unwanted/glob)

如果您使用的是Bash,则应该能够使用扩展的globlobing功能仅获取所需的文件:

$ cd -- "$(mktemp --directory)" 
$ git init
Initialized empty Git repository in /tmp/tmp.cJm8k38G9y/.git/
$ mkdir aye bee
$ echo foo > aye/foo
$ git add aye/foo
$ git commit -m "First commit"
[master (root-commit) 46a028c] First commit
 0 files changed
 create mode 100644 aye/foo
$ echo foo > bee/foo
$ git add bee/foo
$ git commit -m "Second commit"
[master 30b3af2] Second commit
 1 file changed, 1 insertion(+)
 create mode 100644 bee/foo
$ shopt -s extglob
$ git log !(bee)
commit ec660acdb38ee288a9e771a2685fe3389bed01dd
Author: My Name <jdoe@example.org>
Date:   Wed Jun 5 10:58:45 2013 +0200

    First commit

您可以将其与globstar递归操作结合使用。


7
这不会显示影响文件的提交。非常接近,不错,一切都一样。
Anonymoose 2013年

-2

您可以使用以下方法暂时忽略文件中的更改:

git update-index --skip-worktree path/to/file

展望未来,对这些文件的所有更改都将被忽略git statusgit commit -a等等。当你准备好了提交这些文件,将它取反:

git update-index --no-skip-worktree path/to/file

并像往常一样提交。


9
这似乎解决了稍微不同的情况。git update-index --skip-worktree不会导致git log过滤已经做出的提交。
Anonymoose
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.