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

如何使 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 跟踪并提交。

相关问题: 如何反转 ‘git log -grep = ’或者如何显示不匹配模式的 git log这是相同的问题,除了提交消息而不是路径。

2008年论坛关于这个话题的讨论: Re: 从 git-diff 中排除文件这看起来很有希望解决我的问题,但对我而言还是缺少一些线索

37942 次浏览

您可以使用以下命令忽略文件中的更改:

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

接下来,对这些文件的所有更改将被 git statusgit commit -a等忽略。当您准备提交这些文件时,只需反向执行:

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

像往常一样承诺。

医生: shopt -s extglob && git log !(unwanted/glob|another/unwanted/glob)

如果你使用的是 巴斯,你应该能够使用 延伸的 globbing的功能,只获得你需要的文件:

$ 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结合起来进行递归操作。

它现在已经实现(git 1.9/2.0,Q12014) ,在 承诺79b1f提交1649612中引入了 Pathspec magic ABC0及其简式 :! 文件可以在 给你中找到。

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

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'

作为 肯尼 · 埃维特注意到了

如果在 bash shell 中运行 git,例如 ':!sub'":\!sub",不要忘记使用单引号或正确的双引号转义。否则你会遇到 bash: ... event not found错误


注意: Git 2.13(2017年第二季度)将在 !中添加一个同义词 ^

提交859b7f1承诺42ebeb9(2017年2月8日) by Linus Torvalds (torvalds)
(由 朱尼奥 · C · 哈马诺 gitster于2017年2月27日在 提交015fba3合并)

Pathspec magic: 添加‘ ^’作为‘ !’的别名

选择“ !”作为负的路径规范不仅不匹配 我们对于修订所做的工作,对于 shell 来说也是一个可怕的字符 因为它需要报价。

因此,添加‘ ^’作为排除 pathspec 条目的替代别名。


请注意,在 Git 2.28(Q32020)之前,负路径规范的使用在收集路径(包括工作树中未跟踪的路径)时已经中断。

犯罪(2020年6月5日) by Elijah Newren (newren)
(由 朱尼奥 · C · 哈马诺 gitster提交64efa11合并,2020年6月18日)

dir : 固定治疗否定的路径规格

作者: John Millikin
由 Elijah Newren 签名

do_match_pathspec()match_pathspec_depth_1()的形式开始生命,为了正确起见,只能从 match_pathspec_depth()调用 do_match_pathspec() match_pathspec_depth()后来被重命名为 match_pathspec(),所以我们今天期望的不变条件是 do_match_pathspec()match_pathspec()之外没有直接调用方。

不幸的是,这个意图随着这两个函数的重命名而丢失,并且在提交 75a6315f74时添加了对 do_match_pathspec()的额外调用(“ ls-files: add pathspec match for submodule”,2016-10-07,Git v2.11.0-rc0—— 第11批中列出的 合并)和 89a1f4aaf7(“ dir: 如果我们的 pathspec 可能匹配 dir 下的文件,则递归到它”,2019-09-17,Git v2.24.0-rc0)。

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

此外,虽然直接调用 do_match_pathspec()是不正确的,但是可观察到的最终输出可能没有任何差异,因为这个 bug 只是意味着 fill_diretory()会递归到不需要的目录中。

由于随后对目录下的单个路径进行的 -this-path-match 检查将导致过滤掉那些额外的路径,因此与使用错误函数的唯一区别就是不必要的计算。

do_match_pathspec()的第二个错误调用——通过直接移动或通过复制 + 编辑——涉及到后来的一些重构。

参见提交 777b420347(“ dir: sync treat_leading_path() and read_directory_recursive()”,2019-12-19,Git v2.25.0-rc0—— 合并) ,8d92fb2927(“ dir: 用线性算法替换指数算法”,2020-04-01,Git v2.27.0-rc0—— 第五批中列出的 合并)和 treat_leading_path()0(“修复易出错的 fill_directory() API; 使其只返回匹配”,2020-04-01,Git v2.27.0-rc0—— 第五批中列出的 合并)。

最后一个引入了 do_match_pathspec()在单个文件上的使用,从而导致返回不应该返回的单个路径。

调用 do_match_pathspec()而不是 match_pathspec()的问题在于,任何否定的模式,比如“‘ : ! wan _ path”,都将被忽略。

添加一个新的 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的关键在于,如果我们有一个像

*/Makefile

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

src/module/component

我们希望将其视为匹配,这样我们就可以递归到该目录中,因为它的 _might _ 在下面的某个地方有一个名为 Makefile的文件。

但是,当我们使用排除模式时,也就是说,我们有一个路径规范,比如

:(exclude)*/Makefile

我们不想说一个目录路径像

src/module/component

是(否定的)匹配。

虽然在那个目录下面的某个地方有一个名为“ Makefile”的 也许吧文件,但也可能存在其他文件,我们不能预先排除该目录下的所有文件; 我们需要递归,然后检查单个文件。

调整 DO_MATCH_LEADING_PATHSPEC逻辑,只激活正路径规格。