我可以配置git blame使其始终忽略某些提交吗?想要一劳永逸地修复git怪 [英] Can I configure git blame to always ignore certain commits? Want to fix git blame once and for all

查看:134
本文介绍了我可以配置git blame使其始终忽略某些提交吗?想要一劳永逸地修复git怪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在有效地破坏了git怪的存储库中.

I am in a repository where git blame has effectively been broken.

在git blame中有两个我想忽略的提交.

There are two commits I want to ignore in git blame.

  • 提交1个销毁了很多个文件.
  • 提交2立即还原提交1.
  • Commit 1 destroyed a lot of files.
  • Commit 2 immediately reverted commit 1.

每次我怪罪于一行时,我都会看到[commit 2]的作者,而不是真正的逻辑作者.

Every time I git blame a line now I see the author of [commit 2] instead of the true logical author.

我最终不得不执行git log [file in question]此列表中列出的另一种解决方案问题.

每当我使用Intellij中的Annotate功能(基本上是git blame)时,这两个提交使我感到很难过.

These two commits make me sad whenever I use the Annotate feature in Intellij (which is basically git blame).

有人在不重写历史记录的情况下解决了这个问题吗?

Has anybody ever fixed this problem before without rewriting history?

推荐答案

每当我使用Intellij中的Annotate功能(基本上是git blame)时,这两个提交使我感到很难过.
有没有人在不重写历史记录的情况下解决此问题?

These two commits make me sad whenever I use the Annotate feature in Intellij (which is basically git blame).
Has anybody ever fixed this problem before without rewriting history?

2019年第三季度之前,没有.
但是使用Git 2.23,您将能够指示git blame 忽略这两个有问题的提交. (IntelliJ注释"功能可能需要一段时间才能赶上)

Before Q3 2019, no.
But with Git 2.23, you will be able to instruct git blame to ignore those two problematic commits. (IntelliJ "annotate" feature might take a while before catching up)

Michael Plates

git blame --ignore-rev的工作条件是指定的提交进行了无意义的更改(例如重新格式化).
不幸的是,删除和添加文件都是相当大的更改,因此--ignore-rev在这里无济于事.

git blame --ignore-rev works on the assumption that the specified commit made an uninteresting change (e.g. reformatting).
Unfortunately both removing and adding a file are quite drastic changes so --ignore-rev won't help here.

话虽这么说,git blame现在可以忽略提交(即使在这种情况下也可以).

That being said, git blame can now ignore commits (even maybe not in this particular case).

通常,从Git 2.23开始:

In general, since Git 2.23:

" git blame "学会了"忽略 "提交到历史中,其影响(以及它们的存在)将被忽略.

"git blame" learned to "ignore" commits in the history, whose effects (as well as their presence) get ignored.

您可以在git config中进行注册!您甚至不需要在每次git blame调用时在参数中传递这些提交.

And you can register that in your git config! You don't even need to pass those commits in parameters on every git blame call.

请参见提交78fafbb (2019年6月30日)和提交07a54dc (2019年6月28日),作者为提交f0cbe74 提交1fc7338 提交8934ac8 提交55f808f 提交f93895f Barret Rhoden(brho).
(由 Junio C Hamano-gitster-合并在

See commit 78fafbb (30 Jun 2019), and commit 1d028dc (20 Jun 2019) by Michael Platings (``).
See commit 07a54dc (28 Jun 2019) by Jeff King (peff).
See commit f0cbe74, commit a07a977 (20 Jun 2019), and commit 1fc7338, commit 8934ac8, commit ae3f36d, commit 55f808f, commit f93895f, commit 24eb33e (15 May 2019) by Barret Rhoden (brho).
(Merged by Junio C Hamano -- gitster -- in commit 209f075, 19 Jul 2019)

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

归咎于文件时,进行格式更改或函数重命名的提交通常并不有趣.
用户可能会认为这样的提交不有趣",并希望在分配责任时忽略它并对其进行更改.

blame: add the ability to ignore commits and their changes

Commits that make formatting changes or function renames are often not interesting when blaming a file.
A user may deem such a commit as 'not interesting' and want to ignore and its changes it when assigning blame.

例如,假设一个文件具有以下git历史记录/修订列表:

For example, say a file has the following git history / rev-list:

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

提交XY都触摸特定的一行,而其他提交则执行 不是:

Commits X and Y both touch a particular line, and the other commits do not:

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,但不要让责任归咎于坚持".

git-blame will blame Y for the change.
I'd like to be able to ignore Y: both the existence of the commit as well as any changes it made.
This differs from -S rev-list, which specifies the list of commits to process for the blame.
We would still process Y, but just don't let the blame 'stick.'

此修补程序为用户提供了使用--ignore-rev=rev忽略修订的功能,该修订可能会重复.
他们可以指定一组完整对象名称为revs的文件,例如SHA-1哈希,每行一个.
可以使用blame.ignoreRevFile config选项指定单个文件 或使用--ignore-rev-file=file.
config选项和命令行选项都可以重复多次.

This patch adds the ability for users to ignore a revision with --ignore-rev=rev, which may be repeated.
They can specify a set of files of full object names of revs, e.g. SHA-1 hashes, one per line.
A single file may be specified with the blame.ignoreRevFile config option or with --ignore-rev-file=file.
Both the config option and the command line option may be repeated multiple times.

空文件名""将从先前处理的文件中清除转速列表.
配置选项在命令行选项之前处理.

An empty file name "" will clear the list of revs from previously processed files.
Config options are processed before command line options.

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

For a typical use case, projects will maintain the file containing revisions for commits that perform mass reformatting, and their users have the option to ignore all of the commits in that file.

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

Additionally, a user can use the --ignore-rev option for one-off investigation.
To go back to the example above, X was a substantive change to the function, but not the change the user is interested in.
The user inspected X, but wanted to find the previous change to that line - perhaps a commit that introduced that function call.

要实现此目的,我们不能简单地从rev-list中删除所有忽略的提交.
我们需要区分Y引入的更改,以便我们可以忽略它们.
就像处理正常时一样,我们将责备传递给Y. 当以Y为目标时,我们确保Y不会保持任何责备.
Y负责的任何更改都将传递给其父级.请注意,我们一次通过了所有替罪羊(父母)以试图正常地怪罪他人.在我们检查了所有父母之前,我们不知道是否需要忽略提交.

To make this work, we can't simply remove all ignored commits from the rev-list.
We need to diff the changes introduced by Y so that we can ignore them.
We let the blames get passed to Y, just like when processing normally.
When Y is the target, we make sure that Y does not keep any blames.
Any changes that Y is responsible for get passed to its parent. Note we make one pass through all of the scapegoats (parents) to attempt to pass blame normally; we don't know if we need to ignore the commit until we've checked all of the parents.

blame_entry将一直沿树向上传递,直到我们找到具有影响这些行的差异块的提交为止.

The blame_entry will get passed up the tree until we find a commit that has a diff chunk that affects those lines.

一个问题是被忽略的提交 did 进行了一些更改,并且没有通用的解决方案来找到与该被忽略的提交中的给定行相对应的父提交中的行.
这使得很难在忽略的提交的差异中添加特定行 正确.

One issue is that the ignored commit did make some change, and there is no general solution to finding the line in the parent commit that corresponds to a given line in the ignored commit.
That makes it hard to attribute a particular line within an ignored commit's diff correctly.

例如,在第11行中,被忽略的提交的父级具有以下内容:

For example, the parent of an ignored commit has this, say at line 11:

commit-a 11) #include "a.h"
commit-b 12) #include "b.h"

提交X(我们将忽略)交换以下行:

Commit X, which we will ignore, swaps these lines:

commit-X 11) #include "b.h"
commit-X 12) #include "a.h"

我们可以将此归罪条目传递给父级,但是即使"include b.h"来自提交B,第11行也将归因于提交A. 怪罪机制将在第11行的父文件视图中进行.

We can pass that blame entry to the parent, but line 11 will be attributed to commit A, even though "include b.h" came from commit B.
The blame mechanism will be looking at the parent's view of the file at line number 11.

ignore_blame_entry()设置为允许使用替代算法来猜测每行的责任.
任何未归属到父级的行将继续被归咎于忽略的提交,就像未忽略该提交一样.
即将发布的补丁可以检测这些行并将其标记在非正常输出中.

ignore_blame_entry() is set up to allow alternative algorithms for guessing per-line blames.
Any line that is not attributed to the parent will continue to be blamed on the ignored commit as if that commit was not ignored.
Upcoming patches have the ability to detect these lines and mark them in the blame output.

现有算法很简单:将每一行归咎于父级的diff块中的相应行.
超出该范围的任何行都将留在目标位置.

The existing algorithm is simple: blame each line on the corresponding line in the parent's diff chunk.
Any lines beyond that stay with the target.

例如,在第11行中,被忽略的提交的父级具有以下内容:

For example, the parent of an ignored commit has this, say at line 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"后,我们有:

After a commit 'X', we have:

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

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

Commit X nets two additionally lines: 13 and 14.
The current guess_line_blames() algorithm will not attribute these to the parent, whose diff chunk is only two lines - not four.

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

When we ignore with the current algorithm, we get:

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()在父级中找到一行,它仍然可能不正确.

Note that line 12 was blamed on B, though B was the commit for new_func_2(), not new_func_1().
Even when guess_line_blames() finds a line in the parent, it may still be incorrect.


git blame新文档:


git blame new documentation:

--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中列出的修订,这些修订必须位于将清除以前处理过的文件中的转速列表.

Ignore revisions listed in file, which must be in the same format as an fsck.skipList.
This option may be repeated, and these files will be processed after any files specified with the blame.ignoreRevsFile config option.
An empty file name, "", will clear the list of revs from previously processed files.

git config新文档:

blame.ignoreRevsFile:

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

Ignore revisions listed in the file, one unabbreviated object name per line, in git blame.
Whitespace and comments beginning with # are ignored.
This option may be repeated multiple times.
Empty file names will reset the list of ignored revisions.
This option will be handled before the command line option --ignore-revs-file.


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


Since the line detection is not always perfect:

blame:为输出被忽略或不可指责的行添加配置选项

当忽略提交时,由于我们的启发式方法的不准确性,被指责的提交可能不负责更改.
用户可能想知道特定行何时有可能不正确的责任.

blame: add config options for the output of ignored or unblamable lines

When ignoring commits, the commit that is blamed might not be responsible for the change, due to the inaccuracy of our heuristic.
Users might want to know when a particular line has a potentially inaccurate blame.

此外,guess_line_blames()可能找不到任何父提交 给定的行被忽略的提交所触及.
那些不可指责"的行仍然归咎于被忽略的提交.
用户可能想知道行是否不可指责,以便他们不花时间调查他们不感兴趣的提交.

Furthermore, guess_line_blames() may fail to find any parent commit for a given line touched by an ignored commit.
Those 'unblamable' lines remain blamed on an ignored commit.
Users might want to know if a line is unblamable so that they do not spend time investigating a commit they know is uninteresting.

此补丁添加了两个配置选项,以标记这两种类型的行 怪罪的结果.

This patch adds two config options to mark these two types of lines in the output of blame.

第一个选项可以通过指定blame.markIgnoredLines来标识被忽略的行.
设置此选项后,除被忽略的提交以外,其他所有应归因于提交的非标行均标有"?" .

The first option can identify ignored lines by specifying blame.markIgnoredLines.
When this option is set, each blame line that was blamed on a commit other than the ignored commit is marked with a '?'.

例如:

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

显示为:

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

在提交之前放置了?的位置,并且哈希值减少了一个字符.

where the '?' is placed before the commit, and the hash has one fewer characters.

有时我们甚至无法猜测祖先的承诺触及了什么 线.
这些行是不可翻译的".
第二个选项blame.markUnblamableLines将用'*'
标记该行.

Sometimes we are unable to even guess at what ancestor commit touched a line.
These lines are 'unblamable.'
The second option, blame.markUnblamableLines, will mark the line with '*'
.

例如,说我们忽略e5e8d36d04cbe,但我们不能怪 此行在另一次提交上:

For example, say we ignore e5e8d36d04cbe, yet we are unable to blame this line on another commit:

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

显示为:

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

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

When these config options are used together, every line touched by an ignored commit will be marked with either a '?' or a '*'.

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

That means the git config man page now has:

blame.markUnblamables: 

标记被忽略的修订更改的行,我们不能将其归因于在git blame输出中带有'*'的另一个提交.

Mark lines that were changed by an ignored revision that we could not attribute to another commit with a '*' in the output of git blame.

blame.markIgnoredLines:

标记被忽略的修订更改的行,我们将这些行归因于在git blame输出中带有'?'的另一次提交.

Mark lines that were changed by an ignored revision that we attributed to another commit with a '?' in the output of git blame.


最后,要改善git blame行检测:


Finally, to improve the git blame line detection:

blame:添加指纹试探法以匹配被忽略的行

此算法将用在父级文件版本中找到可能的候选行的启发式算法替换用于从忽略的提交中识别行的启发式算法.
实际的替换发生在即将到来的提交中.

blame: add a fingerprint heuristic to match ignored lines

This algorithm will replace the heuristic used to identify lines from ignored commits with one that finds likely candidate lines in the parent's version of the file.
The actual replacement occurs in an upcoming commit.

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

The old heuristic simply assigned lines in the target to the same line number (plus offset) in the parent. The new function uses a fingerprinting algorithm to detect similarity between lines.

新的启发式设计旨在准确匹配通过clang-format和clang-tidy等格式化工具进行的机械更改.
这些工具会进行更改,例如将行拆分以适合字符限制,或者更改标识符以符合命名约定.
启发式方法无意匹配更广泛的重构更改,在这种情况下可能会产生误导性的结果.

The new heuristic is designed to accurately match changes made mechanically by formatting tools such as clang-format and clang-tidy.
These tools make changes such as breaking up lines to fit within a character limit or changing identifiers to fit with a naming convention.
The heuristic is not intended to match more extensive refactoring changes and may give misleading results in such cases.

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

In most cases formatting tools preserve line ordering, so the heuristic is optimized for such cases. (Some types of changes do reorder lines e.g. sorting keep the line content identical, the git blame -M option can already be used to address this).
The reason that it is advantageous to rely on ordering is due to source code repeating the same character sequences often e.g. declaring an identifier on one line and using that identifier on several subsequent lines.
This means that lines can look very similar to each other which presents a problem when doing fuzzy matching. Relying on ordering gives us extra clues to point towards the true match.

试探法一次处理一个差异块的变化.
它会为更改的每一行的每一行创建一个指纹".

The heuristic operates on a single diff chunk change at a time.
It creates a "fingerprint" for each line on each side of the change.

struct fingerprint的注释中,详细描述了指纹 ,但本质上是一行中字符对的多集.

Fingerprints are described in detail in the comment for struct fingerprint, but essentially are a multiset of the character pairs in a line.

  • 启发式方法首先识别目标条目中的行,其指纹与父条目中的行指纹最清楚地匹配.
    在指纹完全匹配的情况下,将线条的位置用作平局. -试探法锁定最佳匹配,并从父条目中的行的指纹中减去目标条目中的行的指纹,以防止其他行在该行的相同部分上被匹配.
  • 然后,它在比赛之前的区块部分然后在比赛之后的区块部分上递归地重复该过程.
  • The heuristic first identifies the line in the target entry whose fingerprint is most clearly matched to a line fingerprint in the parent entry.
    Where fingerprints match identically, the position of the lines is used as a tie-break. - The heuristic locks in the best match, and subtracts the fingerprint of the line in the target entry from the fingerprint of the line in the parent entry to prevent other lines being matched on the same parts of that line.
  • It then repeats the process recursively on the section of the chunk before the match, and then the section of the chunk after the match.

这是指纹识别产生差异的一个例子.
考虑一个具有两次提交的文件:

Here's an example of the difference the fingerprinting makes.
Consider a file with two commits:

    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);

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

When we blame-ignored with the old algorithm, we get:

    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行之后,该行将首先匹配,因此无法与较早的行匹配.

Note line 2 could be matched with either commit-a or commit-b as it is equally similar to both lines, but is matched with commit-a because its position as a fraction of the new line range is more similar to commit-a as a fraction of the old line range.
Line 4 is also equally similar to both lines, but as it appears after line 3 which will be matched first it cannot be matched with an earlier line.

更多示例,请参见 ,其中包含 示例父文件和目标文件以及父文件中的行号 必须匹配.

For many more examples, see t/t8014-blame-ignore-fuzzy.sh which contains example parent and target files and the line numbers in the parent that must be matched.

这篇关于我可以配置git blame使其始终忽略某些提交吗?想要一劳永逸地修复git怪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆