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

时间:2016-01-22 22:47:39

标签: git intellij-idea version-control

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

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

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

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

我最终不得不使用git log [file in question]this question中列出的其他解决方案。

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

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

2 个答案:

答案 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 78fafbbcommit 1d028dc(2019年6月30日)和Michael Platings (``)(2019年6月20日)。
请参见commit 07a54dcJeff King (peff)(2019年6月28日)。
请参见commit f0cbe74commit a07a977(2019年6月20日)和commit 1fc7338commit 8934ac8commit ae3f36dcommit 55f808fcommit 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
     

提交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的修订,该修订可以重复
  他们可以指定一组完整对象名称为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,尽管Bnew_func_2()而不是new_func_1()的提交。
  即使guess_line_blames()在父级中找到一行,它仍然可能不正确。


commit 209f075

--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-acommit-b匹配   与这两行相同,但与commit-a匹配,因为   位置(占新行范围的一部分)与commit-a相似,占旧行范围的一部分。
  第4行也与这两行类似,但是它出现在第3行之后,该行将首先匹配,因此无法与较早的行匹配。

     

有关更多示例,请参见in the comment for struct fingerprint,其中包含   示例父文件和目标文件以及父文件中的行号   必须匹配。