`git diff --patience` 和 `git diff --histogram` 有啥区别?

Posted

技术标签:

【中文标题】`git diff --patience` 和 `git diff --histogram` 有啥区别?【英文标题】:What's the difference between `git diff --patience` and `git diff --histogram`?`git diff --patience` 和 `git diff --histogram` 有什么区别? 【发布时间】:2015-11-28 16:14:50 【问题描述】:

This earlier question 询问了 4 种不同 Git diff 策略之间的区别,但唯一解释的区别是 myerspatience 之间的区别,elsewhere 解释得很好。

histogram 策略如何运作?它与patience 有什么区别? git-diff man page 只是说它“将耐心算法扩展为“支持低出现的常见元素”。其他页面提到它更快,并且来自 JGit,但他们没有解释 它的算法或结果在哪里或如何不同于 patience

我在哪里可以找到与patience 算法相关的histogram 算法的描述,其详细程度与Bram Cohen's original description of the patience algorithm 相同?

(如果只是实现性能的问题,没有任何情况会产生不同的结果,那为什么不直接作为patience的新后端实现?)

【问题讨论】:

虽然this paper 只比较了两种算法(Myers 和 Histogram),但我认为它可以提供帮助。 【参考方案1】:

这个直方图策略是在git 1.7.7 (Sept 2011)中引入的,描述如下(如OP所述)

git diff”学习了一个“--histogram”选项来使用从jgit窃取的不同差异生成机制,这可能会提供更好的性能。

JGit 包括src/org/eclipse/jgit/diff/HistogramDiff.javatst/org/eclipse/jgit/diff/HistogramDiffTest.java

那里的描述相当完整:

直方图差异

Bram Cohen 的耐心差异算法的扩展形式。

此实现是通过使用 4 条规则得出的 Bram Cohen's blog,然后进一步扩展以支持低出现的常见元素。

该算法的基本思想是为序列 A 的每个元素创建一个出现次数的直方图。然后依次考虑序列 B 的每个元素。如果该元素也存在于序列 A 中,并且出现次数较少,则将这些位置视为最长公共子序列 (LCS) 的候选位置。 在 B 的扫描完成后,选择出现次数最少的 LCS 作为分割点。区域围绕 LCS 进行分割,并将算法递归应用于 LCS 之前和之后的部分。

通过始终选择出现次数最少的 LCS 位置,只要两个序列之间存在唯一的公共元素,此算法的行为就与 Bram Cohen 的耐心差异完全一样。 当不存在唯一元素时,将选择出现次数最少的元素。 这提供了比简单地使用标准 Myers 的O(ND) 算法产生的更具可读性的差异。

为防止算法出现O(N^2) 的运行时间,直方图桶中唯一元素的数量上限由#setMaxChainLength(int) 配置。 如果序列 A 有多个元素散列到同一个散列桶中,则算法将该区域传递给 #setFallbackAlgorithm(DiffAlgorithm)。 如果未配置回退算法,则该区域将作为替换编辑发出。

在扫描序列 B 期间,A 中出现超过#setMaxChainLength(int) 次的任何元素都不会被视为 LCS 匹配位置,即使它在两个序列之间是常见的。这限制了序列 A 中必须考虑找到 LCS 的位置数量,并有助于保持较低的运行时间限制。

只要#setMaxChainLength(int)是一个很小的常数(比如64),算法在O(N * D)的时间内运行,其中N是输入长度的总和,D是编辑的数量结果EditList. 如果提供的 SequenceComparator 具有良好的哈希函数,则此实现通常优于 MyersDiff,即使其理论运行时间相同。

此实现有一个内部限制,阻止它处理超过 268,435,456 (2^28) 个元素的序列


请注意,这种算法是already used for pack_check, back in 2006 (git 1.3),对应于git-verify-pack -v。是reused for index-pack in git 1.7.7


Commit 8c912ee实际上是把--histogram引入到diff中:

将 JGit 的 HistogramDiff 算法移植到 C. 粗略数字 (TODO) 显示 它比它的 --patience 表亲以及默认的 Meyers 算法更快。

实现已被重新设计为使用结构和指针, 而不是位掩码,从而消除了 JGit 的 2^28 行限制

我们还使用xdiff的默认哈希表实现(xdl_hash_bits() 为方便起见,使用XDL_HASHLONG())。

commit 8555123 (git 1.7.10, April 2012) 已添加:

8c912ee (teach --histogram to diff, 2011-07-12) 声称直方图差异 比迈尔斯和耐心都快。

我们已经合并了一个性能测试框架,所以添加一个 比较在真实“log -p”中执行的各种差异任务的测试 工作量。 这确实表明直方图 diff 略优于 Myers,而耐心比其他方法慢得多。

最后commit 07ab4de (git 1.8.2, March 2013)添加

config:引入 diff.algorithm 变量

一些用户或项目比其他用户更喜欢不同的算法,例如对迈尔斯或类似的耐心。 但是,每次使用 diff 时都指定适当的参数是不切实际的。此外,创建别名不能很好地与其他基于 diff 的工具配合使用(例如git-show)。

因此,需要一个能够设置特定算法的配置变量。 目前,这四个值被接受:

'myers'(相当于完全不设置config变量), 'minimal', 'patience'和 'histogram'.

Commit 07924d4 同时添加了--diff-algorithm 命令行选项。 正如 OP Stuart P. Bentley 提到的 in the comments:

您可以将 Git 配置为默认使用直方图

git config --global diff.algorithm histogram

更新:Git 2.12(2017 年第一季度)将淘汰在某些极端情况下会导致灾难性性能问题的“快速哈希”。

参见Jeff King (peff) 的commit 1f7c926(2016 年 12 月 1 日)。 (由 Junio C Hamano -- gitster -- 合并于 commit 731490b,2016 年 12 月 19 日)

xdiff:掉线XDL_FAST_HASH

xdiff 代码对差异两边的每一行进行哈希处理,然后比较这些哈希值以查找重复项。整体性能既取决于我们计算哈希的速度,也取决于我们看到多少哈希冲突。

XDL_FAST_HASH 的想法是加快哈希计算。 但是生成的散列具有更差的碰撞行为。这意味着在某些情况下它会加快 diff(在 git.git 上运行“git log -p”会提高 ~8%),但在其他情况下它会减慢速度。 One pathological case saw over a 100x slowdown.

可能有一个更好的散列函数涵盖这两个属性,但同时我们最好使用原始散列。普通情况下速度稍慢,但意外的病理情况较少。


注意:“git diff --histogram”的内存使用模式不好,它有 在 Git 2.19(2018 年第三季度)中重新安排以减少峰值使用量。

参见Stefan Beller (stefanbeller)commit 79cb2eb、commit 64c4e8b、commit c671d4b、commit 2820985(2018 年 7 月 19 日)。(由 Junio C Hamano -- gitster -- 合并于 commit 57fbd8e,2018 年 8 月 15 日)

xdiff/xhistogram:将索引分配移动到find_lcs

这解决了递归很多时的内存问题,可以重现为

seq 1   100000 >one
seq 1 4 100000 >two
git diff --no-index --histogram one two

在这个补丁之前,histogram_diff 会在之前递归调用自己 调用free_index,这意味着在此期间分配了大量内存 递归,然后才被释放。

通过将内存分配(及其免费调用)移动到find_lcs,内存在我们递归之前被释放,这样内存在递归的下一步中被重用,而不是使用新内存。

这仅解决内存压力,而不是运行时复杂性, 这对于上面概述的极端情况也很糟糕。

【讨论】:

作为参考,git config --global diff.algorithm histogram 是使用最后一次提交的命令,用于将 Git 配置为默认使用直方图。 @StuartP.Bentley 好点。我已将您的评论包含在答案中以提高知名度。 XDL_FAST_HASH 与这些有什么关系? @StuartP.Bentley 它被用来尝试和优化 diff historigram 和耐心,如 github.com/git/git/commit/… 中所述。但事与愿违,最近被撤了。

以上是关于`git diff --patience` 和 `git diff --histogram` 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

gitlog和diff区别

git diff 结果分析

如何让 diff 像 git-diff 一样工作?

git diff 命令

beyond compare 与git diff整合

Git diff 比较两个版本文件之间的差异