繁体   English   中英

我可以配置git blame总是忽略某些提交吗?想要一劳永逸地修复git责备

[英]Can I configure git blame to always ignore certain commits? Want to fix git blame once and for all

我在一个存储库中git blame被有效破坏了。

在git blame中我想忽略两个提交。

  • 提交1摧毁了很多文件。
  • 提交2立即恢复提交1。

现在每次我责备一行,我都会看到[提交2]的作者,而不是真正的逻辑作者。

我最终不得不做一个git log [file in question] ,或者这个问题中列出的另一个解决方案。

每当我在Intellij中使用Annotate功能时,这两个提交都让我很难过(基本上是git blame)。

有没有人在没有重写历史记录之前修复过这个问题?

每当我在Intellij中使用Annotate功能时,这两个提交都让我很难过(基本上是git blame)。
有没有人在没有重写历史记录之前修复过这个问题?

在2019年第三季度之前,没有。
但是使用Git 2.23,你将能够指示git责备忽略那两个有问题的提交。 (IntelliJ“注释”功能可能需要一段时间才能赶上)

Michael Platings 评论说

git blame --ignore-rev工作原理是指定的提交做出了无趣的改变(例如重新格式化)。
不幸的是,删除和添加文件都是非常剧烈的变化,因此--ignore-rev在这里无济于事。

话虽如此, git blame现在可以忽略提交(甚至可能不在这种特殊情况下)。

一般来说,自Git 2.23:

git blame ”学会了“ 忽略 ”历史上的承诺,其效果(以及它们的存在)被忽略了。

你可以在你的git config注册它! 你甚至不需要在每个git blame调用中传递参数中的那些提交。

请参阅提交78fafbb (2019年6月30日),并由Michael Platings(``) 提交1d028dc (2019年6月20日
Jeff King( peff )的 提交07a54dc (2019年6月28日
请参阅提交f0cbe74提交a07a977 (2019年6月20日),并提交1fc7338提交8934ac8提交ae3f36d提交55f808f提交f93895f提交24eb33e (2019年5月15日),由Barret brhobrhobrho
(由Junio C gitster合并- gitster -提交209f075 ,2017年7月19日)

blame :添加忽略提交及其更改的能力

提交格式更改或函数重命名通常在归咎于文件时并不重要。
用户可能认为这样的提交“不感兴趣”并且想要忽略并且在分配责任时将其更改。

例如,假设一个文件具有以下git history / rev-list:

 ---O---A---X---B---C---D---Y---E---F 

提交XY都触及特定行,而其他提交不:

 X: "Take a third parameter" -MyFunc(1, 2); +MyFunc(1, 2, 3); Y: "Remove camelcase" -MyFunc(1, 2, 3); +my_func(1, 2, 3); 

git-blame会责怪Y的改变。
我希望能够忽略Y :提交的存在以及它所做的任何更改。
这与-S rev-list ,后者指定要处理责任的提交列表。
我们仍然会处理Y ,但不要责怪“坚持”。

此补丁为用户添加了忽略修订的能力--ignore-rev=rev ,可以重复
他们可以指定一组转速的完整对象名称文件,例如SHA-1哈希值,每行一个。
可以使用blame.ignoreRevFile配置选项或--ignore-rev-file=file指定单个--ignore-rev-file=file
config选项和命令行选项都可以重复多次。

空文件名""将清除以前处理过的文件的转速列表。
在命令行选项之前处理配置选项。

对于典型用例,项目将维护包含执行批量重新格式化的提交的修订的文件,并且用户可以选择忽略该文件中的所有提交。

此外,用户可以使用--ignore-rev选项进行一次性调查。
回到上面的例子, X是对函数的实质性改变,但不是用户感兴趣的改变。
用户检查了X ,但希望找到之前对该行的更改 - 可能是引入该函数调用的提交。

为了使这项工作,我们不能简单地从rev-list中删除所有被忽略的提交。
我们需要区分Y引入的变化,以便我们可以忽略它们。
我们让责任传递给Y ,就像正常处理一样。
Y是目标时,我们确保Y不会留下任何责任。
Y负责的任何更改都会传递给其父级。 请注意,我们通过所有的替罪羊(父母)试图通过正常的责任; 在我们检查所有父母之前,我们不知道是否需要忽略提交。

blame_entry将被传递到树上,直到我们找到一个具有影响这些行的diff块的提交。

一个问题是被忽略的提交确实做了一些更改,并且没有通用的解决方案来查找父提交中与被忽略的提交中的给定行相对应的行。
这使得很难在被忽略的提交的diff中正确地定义特定行。

例如,被忽略的提交的父级有这个,比如第11行:

 commit-a 11) #include "ah" commit-b 12) #include "bh" 

提交X ,我们将忽略,交换这些行:

 commit-X 11) #include "bh" commit-X 12) #include "ah" 

我们可以将该责任条目传递给父级,但第11行将归于提交A,即使“包含bh”来自提交B
责备机制将在第11行查看父文件的文件视图。

ignore_blame_entry()被设置为允许用于猜测每行blames的替代算法。
任何未归属于父级的行都将继续归咎于忽略的提交,就好像该提交未被忽略一样。
即将到来的补丁能够检测这些线并在非责任输出中标记它们。

现有算法很简单:将父线的差异块中相应行上的每一行都归咎于。
除了目标之外的任何线路。

例如,被忽略的提交的父级有这个,比如第11行:

 commit-a 11) void new_func_1(void *x, void *y); commit-b 12) void new_func_2(void *x, void *y); commit-c 13) some_line_c commit-d 14) some_line_d 

提交'X'后,我们有:

 commit-X 11) void new_func_1(void *x, commit-X 12) void *y); commit-X 13) void new_func_2(void *x, commit-X 14) void *y); commit-c 15) some_line_c commit-d 16) some_line_d 

提交X另外两行:13和14。
当前的guess_line_blames()算法不会将这些属性归因于父级,其父差异块只有两行 - 而不是四行。

当我们忽略当前算法时,我们得到:

 commit-a 11) void new_func_1(void *x, commit-b 12) void *y); commit-X 13) void new_func_2(void *x, commit-X 14) void *y); commit-c 15) some_line_c commit-d 16) some_line_d 

请注意,第12行被归咎于B ,但Bnew_func_2()的提交,而不是new_func_1()
即使guess_line_blames()在父级中找到一行,它仍可能不正确。


git blame新文档

 --ignore-rev <rev>:: Ignore changes made by the revision when assigning blame, as if the change never happened. Lines that were changed or added by an ignored commit will be blamed on the previous commit that changed that line or nearby lines. This option may be specified multiple times to ignore more than one revision. --ignore-revs-file <file>: 

忽略中列出的修改file ,它必须是在相同的格式fsck.skipList
可以重复此选项,并在使用blame.ignoreRevsFile配置选项指定的任何文件之后处理这些文件。
空文件名""将清除先前处理过的文件的转速列表。

git config新文档

 blame.ignoreRevsFile: 

忽略文件中列出的修订,每行一个未缩写的对象名称,用git blame
#开头的空格和注释将被忽略。
该选项可以重复多次。
空文件名将重置忽略的修订列表。
此选项将在命令行选项--ignore-revs-file之前处理。


由于线路检测并不总是完美的:

blame :为忽略或不可控制的行的输出添加配置选项

当忽略提交时,由于我们的启发式算法的不准确性,被指责的提交可能不会对更改负责。
用户可能想知道特定行何时可能存在不准确的责任。

此外, guess_line_blames()可能无法找到被忽略的提交触及的给定行的任何父提交。
那些“不可讨论”的行仍被归咎于被忽略的提交。
用户可能想知道一条线是否是不可讨论的,这样他们就不会花时间调查他们知道无趣的提交。

此补丁添加了两个配置选项,用于在blame输出中标记这两种类型的行。

第一个选项可以通过指定blame.markIgnoredLines来识别被忽略的行。
设置此选项后,在忽略提交以外的提交中归咎于的每个责备行都标有' ? '

例如:

 278b6158d6fdb (Barret Rhoden 2016-04-11 13:57:54 -0400 26) 

显示为:

 ?278b6158d6fd (Barret Rhoden 2016-04-11 13:57:54 -0400 26) 

' ? '在提交之前放置,并且哈希少了一个字符。

有时我们甚至无法猜测祖先提交的内容是什么。
这些线条是“不可磨损的”。
第二个选项blame.markUnblamableLines将使用' * '标记该行

例如,假设我们忽略了e5e8d36d04cbe,但我们无法在另一次提交中责怪这一行:

 e5e8d36d04cbe (Barret Rhoden 2016-04-11 13:57:54 -0400 26) 

显示为:

 *e5e8d36d04cb (Barret Rhoden 2016-04-11 13:57:54 -0400 26) 

当这些配置选项一起使用时,被忽略的提交所触及的每一​​行都将被标记为' ? '或' * '。

这意味着git config手册页现在具有:

 blame.markUnblamables: 

标记由忽略的修订更改的行,我们无法将其归因于git blame输出中带有'*'的另一个提交。

 blame.markIgnoredLines: 

标记由我们归因于另一个提交的忽略修订更改的行' ? '在git blame的输出中。


最后,改进git blame线检测:

blame :添加指纹启发式以匹配忽略的行

该算法将替换用于识别来自被忽略提交的行的启发法,其中该算法在父文件的版本中找到可能的候选行。
实际的替换发生在即将到来的提交中。

旧启发式只是将目标中的行分配给父级中的相同行号(加上偏移量)。 新功能使用指纹识别算法来检测线之间的相似性。

新的启发式设计旨在通过格式化工具(如clang-format和clang-tidy)精确匹配机械变化。
这些工具可以进行更改,例如拆分行以适应字符限制或更改标识符以符合命名约定。
启发式算法不是为了匹配更广泛的重构更改,并且在这种情况下可能会产生误导性结果。

在大多数情况下,格式化工具可以保留行排序,因此针对此类情况优化了启发式算法。 (某些类型的更改会对行重新排序,例如排序保持行内容相同, git blame -M选项已经可用于解决此问题)。
依赖于排序是有利的原因是由于源代码经常重复相同的字符序列,例如在一行上声明标识符并在若干后续行上使用该标识符。
这意味着线条看起来非常相似,这在进行模糊匹配时会出现问题。 依靠订购为我们提供了额外的线索来指向真正的匹配。

启发式操作一次只对一个差异块进行操作
它为更改的每一侧的每一行创建“指纹”

指纹在struct fingerprint的注释中有详细描述,但基本上是一行中字符对的多重集。

  • 启发式算法首先识别目标条目中的行,其指纹与父条目中的行指纹最清楚地匹配。
    在指纹匹配相同的情况下,线的位置用作打破平局。 - 启发式锁定最佳匹配,并从父条目中的行的指纹中减去目标条目中的行的指纹,以防止其他行在该行的相同部分上匹配。
  • 然后它在匹配之前在块的部分上递归地重复该过程,然后在匹配之后的块的部分重复该过程。

这是指纹识别的不同之处。
考虑一个包含两个提交的文件:

  commit-a 1) void func_1(void *x, void *y); commit-b 2) void func_2(void *x, void *y); 

提交' X '后,我们有:

  commit-X 1) void func_1(void *x, commit-X 2) void *y); commit-X 3) void func_2(void *x, commit-X 4) void *y); 

当我们用旧算法责备忽略时,我们得到:

  commit-a 1) void func_1(void *x, commit-b 2) void *y); commit-X 3) void func_2(void *x, commit-X 4) void *y); 

其中commit-b被指责为2而不是3。

使用指纹算法 ,我们得到:

  commit-a 1) void func_1(void *x, commit-a 2) void *y); commit-b 3) void func_2(void *x, commit-b 4) void *y); 

注意第2行可以与commit-acommit-b匹配,因为它与两行同样相似,但是与commit-a匹配,因为它作为新行范围的一小部分的位置更像是commit-a as旧线范围的一小部分。
第4行也同样类似于两条线,但是因为它出现在第3行之后将首先匹配,所以它不能与较早的线匹配。

有关更多示例,请参阅t/t8014-blame-ignore-fuzzy.sh ,其中包含示例父文件和目标文件以及必须匹配的父级中的行号。

如果真的立即恢复,你可以使用git replace --edit $comment2伪造commit1的父亲作为其父亲。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM