【问题标题】:Making 'git log' ignore changes for certain paths使“git log”忽略某些路径的更改
【发布时间】:2011-08-06 19:12:24
【问题描述】:

如何让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 忽略这些文件。这些文件应该被跟踪和提交。只是,大多数时候,我对看到它们不感兴趣。

相关问题:How to invert `git log --grep=<pattern>` or How to show git logs that don't match a pattern 除了提交消息而不是路径之外,这是同一个问题。

2008 年有关此主题的论坛讨论:Re: Excluding files from git-diff 这看起来很有希望,但线程似乎已经干涸。

【问题讨论】:

  • 我不确定是否有内置方式,而且您的 perl 解决方案看起来还不错。如果你修改它以接受路径作为命令行参数,你可以创建一个类似 !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; f 的别名,或者甚至将该管道部分包装到脚本中。
  • 作为一种解决方法,我使用find 过滤掉我不想看到的提交的目录。如果我想忽略提交到根级目录SiteConfig 的日志条目,那么我会说:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
  • 对于 Git 1.9/2.0(2014 年第一季度),请参阅 my answer belowgit log --oneline --format=%s -- . ":!sub" 将起作用(使用 pathspec 魔术 :(exclude) 及其缩写形式 :! )

标签: git


【解决方案1】:

现在实现(git 1.9/2.0,2014 年第一季度),在 commit ef79b1fcommit 1649612 中引入了 pathspec magic :(exclude) 及其缩写形式 :!,由 Nguyễn Thái Ngọc Duy (pclouds),文档可以在here找到。

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

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 noted

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


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

参见Linus Torvalds (torvalds)commit 859b7f1commit 42ebeb9(2017 年 2 月 8 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 015fba3,2017 年 2 月 27 日)

pathspec 魔术:添加 '^' 作为 '!' 的别名

为否定路径规范选择“!”最终不仅不匹配 我们为修订所做的事情,对于shell来说也是一个可怕的角色 扩展,因为它需要引用。

所以添加“^”作为排除路径规范条目的替代别名。


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

Elijah Newren (newren)commit f1f061e(2020 年 6 月 5 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 64efa11,2020 年 6 月 18 日)

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

报告人:John Millikin
签字人:Elijah Newren

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

不幸的是,这两个函数的重命名失去了这个意图,并且在提交75a6315f74 中添加了对do_match_pathspec() 的额外调用(“ls-files:为子模块添加路径规范匹配”,2016-10-07, Git v2.11.0-rc0 -- merge 列在 batch #11) 和 89a1f4aaf7 ("dir: 如果我们的路径规范可能匹配目录下的文件,递归到它",2019-09-17,Git v2 .24.0-rc0)。

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

另外,虽然直接调用do_match_pathspec() 是不正确的,但可观察到的最终输出可能没有任何区别,因为该错误只是意味着fill_diretory() 会递归到不需要的目录中。

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

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

查看提交 777b420347 ("dir: 同步 treat_leading_path()read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 -- merge), 8d92fb2927 ("@987654393 @: 用线性算法替换指数算法”, 2020-04-01, Git v2.27.0-rc0 -- merge 列在batch #5), 和95c11ecc73 ("Fix-prone fill_directory() API;让它只返回匹配项”,2020-04-01,Git v2.27.0-rc0 -- merge 列在 batch #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 的意义在于,如果我们有类似的路径规范

*/Makefile

我们正在检查类似的目录路径

src/module/component

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

但是,当我们使用排除模式时,即我们有一个类似的路径规范

:(exclude)*/Makefile

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

src/module/component

是一个(否定的)匹配。

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

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

【讨论】:

  • 你能做多个文件吗?
  • @JustinThomas 我相信(尚未测试)您可以多次重复该路径排除模式":(exclude)pathPattern1" ":(exclude)pathPattern2",从而忽略多个文件夹/文件。
  • 如果您在 Bash shell 中运行 Git,请使用 ':!sub' 代替 avoid bash: ... event not found errors":\!sub" 不起作用。
  • @KennyEvitt 感谢您的编辑和评论。我已将后者包含在答案中以获得更多可见性。
  • 对于那些想知道有关此功能的官方文档在哪里的人,请参阅git help glossary(我在git help -g 中找到了[我在git help 中找到了建议])。
【解决方案2】:

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

如果您使用的是 Bash,您应该能够使用 extended 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 结合使用以进行递归操作。

【讨论】:

  • 这不会显示影响不再存在的文件的提交。非常接近和很好的 hack 都是一样的。
【解决方案3】:

您可以暂时忽略文件中的更改:

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

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

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

并正常提交。

【讨论】:

  • 这似乎解决了稍微不同的情况。 git update-index --skip-worktree 不会导致 git log 过滤已提交的提交。
猜你喜欢
  • 2013-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 2020-05-22
  • 1970-01-01
  • 2018-06-26
  • 1970-01-01
相关资源
最近更新 更多