我在一个存储库中,git blame已被有效破坏。
我想在git blame中忽略两个提交。
现在每次我责备一行,我都会看到[提交2]的作者,而不是真正的逻辑作者。
我最终不得不使用git log [file in question]
或this question中列出的其他解决方案。
每当我在Intellij中使用Annotate功能时(这基本上都是git blame),这两个提交让我很难过。
有没有人在没有重写历史记录之前修复过这个问题?
答案 0 :(得分:1)
如果真的立即恢复,你可以使用git replace --edit $comment2
伪造commit1的父级作为其父级。
答案 1 :(得分:0)
每当我使用Intellij中的Annotate功能(基本上是git blame)时,这两个提交使我感到很难过。
有没有人在不重写历史记录的情况下解决此问题?
2019年第三季度之前,没有。
但是使用Git 2.23,您将能够指示git blame 忽略这两个有问题的提交。 (IntelliJ“注释”功能可能需要一段时间才能赶上)
“ git blame
”学会了在历史中“ 忽略”的提交,其影响(及其存在)也被忽略了。
您可以在git config
中注册它!您甚至不需要在每次git blame
调用中在参数中传递这些提交。
请参见commit 78fafbb的commit 1d028dc(2019年6月30日)和Michael Platings (``)(2019年6月20日)。
请参见commit 07a54dc的Jeff King (peff
)(2019年6月28日)。
请参见commit f0cbe74,commit a07a977(2019年6月20日)和commit 1fc7338,commit 8934ac8,commit ae3f36d,commit 55f808f,commit f93895f,{ {3}}(2019年5月15日),作者为commit 24eb33e。
(由Barret Rhoden (brho
)在Junio C Hamano -- gitster
--中合并,2019年7月19日)
blame
:添加了忽略提交及其更改的功能归咎于文件时,进行格式更改或函数重命名的提交通常并不有趣。
用户可能会认为这样的提交“不有趣”,并希望在分配责任时忽略其更改。例如,假设文件具有以下git历史记录/修订列表:
---O---A---X---B---C---D---Y---E---F
提交
X
和Y
都触及特定行,而其他提交 不是: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
的修订,该修订可以重复。
他们可以指定一组完整对象名称为revs的文件,例如SHA-1哈希,每行一个。
可以使用blame.ignoreRevFile
config选项指定一个文件 或使用--ignore-rev-file=file
。
config选项和命令行选项都可以重复多次。空文件名
""
将清除先前处理过的文件的转速列表。
在命令行选项之前先处理配置选项。对于典型的用例,项目将维护包含执行批量重新格式化的提交的修订的文件,其用户可以选择忽略该文件中的所有提交。
此外,用户可以使用
--ignore-rev
选项进行一次性调查。
回到上面的示例,X
是对该功能的实质性更改,但不是用户感兴趣的更改。
用户检查了X
,但想查找对该行的先前更改-可能是引入了该函数调用的提交。要实现此目的,我们不能简单地从rev-list中删除所有忽略的提交。
我们需要比较Y
引入的更改,以便我们可以忽略它们。
就像正常处理时一样,我们将责备传递给Y
。
以Y
为目标时,我们确保Y
不会保持任何责备。
Y
负责的所有更改都将传递给其父级。注意,我们一次通过了所有替罪羊(父母)以试图正常地怪罪;在我们检查了所有的父母之后,我们才知道是否需要忽略提交。blame_entry将一直沿树向上传递,直到我们找到具有影响这些行的差异块的提交为止。
一个问题是被忽略的提交 did 进行了一些更改,并且没有通用的解决方案来找到与该被忽略的提交中的给定行相对应的父提交中的行。
这使得很难在忽略的提交的差异中添加特定行 正确。例如,被忽略的提交的父级具有此内容,例如在第11行:
commit-a 11) #include "a.h" commit-b 12) #include "b.h"
提交
X
(我们将忽略)交换以下行:commit-X 11) #include "b.h" commit-X 12) #include "a.h"
我们可以将此归罪条目传递给父级,但是即使“ include b.h”来自提交
B
,第11行也将归因于提交A。
怪罪机制将在第11行的父文件视图中进行。
ignore_blame_entry()
已设置为允许使用其他算法来猜测每行的责任。
没有归于父级的任何行都将继续归咎于忽略的提交,就像未忽略该提交一样。
即将发布的补丁可以检测这些行并将其标记在非正常输出中。现有算法很简单:将每一行归咎于父级的diff块中的相应行。
超出该范围的任何行均与目标保持一致。例如,被忽略的提交的父级具有此内容,例如在第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
,尽管B
是new_func_2()
而不是new_func_1()
的提交。
即使guess_line_blames()
在父级中找到一行,它仍然可能不正确。
--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
中列出的修订,这些修订必须在git blame
new documentation中。
可以重复执行此选项,并且在使用blame.ignoreRevsFile
config选项指定的任何文件之后,将处理这些文件。
空文件名""
将清除以前处理过的文件的转速列表。
same format as an fsck.skipList
:
blame.ignoreRevsFile:
忽略文件中列出的修订,
git blame
中每行一个未经缩写的对象名称。
空格和以#
开头的注释将被忽略。
此选项可以重复多次。
空文件名将重置忽略的修订列表。
该选项将在命令行选项--ignore-revs-file
之前处理。
由于行检测并不总是完美的:
blame
:添加配置选项以输出被忽略或不可约束的行当忽略提交时,由于我们的启发式方法的不准确性,被指责的提交可能不负责更改。
用户可能想知道特定行何时有可能不正确的责任。此外,
guess_line_blames()
可能找不到任何父提交 给定的行被忽略的提交触及了。
这些“不可指责”的行仍然归咎于被忽略的提交。
用户可能想知道行是否不可指责,以免他们花时间调查他们不感兴趣的提交。此补丁添加了两个配置选项,以标记这两种类型的行 怪罪的结果。
第一个选项可以通过指定
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
new documentation现在具有:
blame.markUnblamables:
标记被忽略的修订所更改的行,我们无法将其归因于
git blame
输出中带有'*'的另一个提交。blame.markIgnoredLines:
在
?
的输出中标记被忽略的修订所更改的行,我们将其归因于另一个提交,并带有'git blame
'。
最后,要改善git blame
行检测:
blame
:添加指纹试探法以匹配被忽略的行此算法将用一种查找在父级文件版本中可能的候选行的启发式算法替换用于从忽略的提交中识别行的启发式算法。
实际的替换发生在即将到来的提交中。旧的启发式方法只是简单地将目标中的行分配给父级中的相同行号(加上偏移量)。该新功能使用指纹算法来检测线之间的相似性。
新的启发式设计旨在准确匹配通过clang-format和clang-tidy等格式化工具进行的机械更改。
这些工具会进行更改,例如将行拆分以适合字符限制,或者更改标识符以符合命名约定。
启发式方法无意匹配更广泛的重构更改,在这种情况下可能会产生误导性的结果。在大多数情况下,格式化工具会保留行顺序,因此针对此类情况优化了启发式方法。 (某些类型的更改会对行进行重新排序,例如,排序使行内容相同,
git blame -M
选项已经可以用来解决此问题。)
依赖排序是有利的原因是由于源代码经常重复相同的字符序列,例如。在一行上声明一个标识符,并在随后的几行中使用该标识符。
这意味着线条看起来可能非常相似,这在进行模糊匹配时会出现问题。依靠订购为我们提供了更多线索来指出 完全匹配。试探法一次处理单个差异块。
它将为更改的每一行的每一行创建一个“指纹”。详细描述了指纹
git config
man page,但本质上是一行中字符对的多集。
- 启发式方法首先在目标条目中标识其指纹与父条目中的行指纹最匹配的行。
在指纹完全匹配的情况下,将线条的位置用作平局。 -试探法锁定最佳匹配,并从父条目中的行的指纹中减去目标条目中的行的指纹,以防止其他行与该行的相同部分匹配。- 然后,它在比赛之前的区块部分然后在比赛之后的区块部分上递归地重复该过程。
这是指纹识别产生差异的一个例子。
考虑一个具有两次提交的文件: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-a
或commit-b
匹配 与这两行相同,但与commit-a
匹配,因为 位置(占新行范围的一部分)与commit-a
相似,占旧行范围的一部分。
第4行也与这两行类似,但是它出现在第3行之后,该行将首先匹配,因此无法与较早的行匹配。有关更多示例,请参见in the comment for
struct fingerprint
,其中包含 示例父文件和目标文件以及父文件中的行号 必须匹配。