git DAG 的增量线性化

Posted

技术标签:

【中文标题】git DAG 的增量线性化【英文标题】:Incremental linearizing of git DAG 【发布时间】:2010-10-16 15:09:07 【问题描述】:

我是GitX 的作者。 GitX 的功能之一是分支的可视化,如here 所示。

这个可视化目前是通过读取从 git 以正确顺序发出的提交来完成的。对于每个提交,父母都是已知的,因此以正确的方式建立通道相当容易。

我想通过使用我自己的提交池并自己线性化提交来加快这个过程。这允许我重用现有的加载提交,并允许 git 更快地发出提交,因为它不必以正确的顺序发出它们。

但是,我不确定要使用什么算法来完成此操作。重要的是构建是增量的,因为提交的加载可能需要很长时间(100,000 次提交超过 5 秒,应该全部显示)。

Gitk 也是如此,有一个补丁 here 显示了它是如何实现的,但是我的 TCL 技能很弱,补丁没有很彻底的注释并且有点难以理解。

我还希望该算法高效,因为它必须处理数十万次提交。它还必须显示在表格中,因此快速访问特定行很重要。

我将描述到目前为止的输入、我想要的输出以及一些观察结果。

输入:

我有一个哈希表形式的当前提交池,它将提交 ID 映射到提交对象。此池不必是完整的(具有所有必要的提交) 我在 git 的新提交中加载了一个单独的线程,每次加载新提交时都可以调用一个回调。没有保证提交的顺序,但在大多数情况下,下一个提交是上一个提交的父级。 提交对象有自己的修订 ID 和所有其父对象的修订 ID 我有一个应该列出的分支头列表。也就是说,没有应该显示的 DAG 的单个“顶部”。也不必只有一个图根。

输出:

我需要按拓扑顺序线性化这些提交。也就是说,一个提交在其父项被列出之后不能被列出。 我还需要上面屏幕截图中可以看到的“分支线”。这些可能需要预先计算,因为它们中的大多数都依赖于他们的孩子。

几点说明:

有必要重新定位提交列表。例如,我们可能必须提交不相关的提交(分支头),直到出现使一个头成为另一个头的祖先的提交。 必须显示多个分支提示 重要的是这个过程是增量的,以便在数据仍在加载时至少有部分视图可用。这意味着必须在中途插入新数据,并且必须重新调整分支线。

【问题讨论】:

【参考方案1】:

我没有使用 GitX,所以也许我遗漏了一些东西,但看起来你可以从每个当前分支的头部从孩子走到父母,直到你可以绘制几个屏幕的图表.

这可能无法为您提供较早扎根的分支的最佳视觉布局。但似乎响应能力比等待绘制具有最少交叉点的图表更重要,因为大多数用户可能对最近的活动感兴趣。

【讨论】:

是的,你描述了我想做的基本过程,但不要回答我的问题,即如何做以及如何有效地做:)。走 DAG 只是你必须做的事情之一,如果你想线性化它,你还必须做一些重定位。 我认为只处理最近的提交(可能是最后 200 个提交)来决定从左到右的顺序在显示可能 25 个提交的页面上绘制分支是有效的。这会将问题简化为 6-10 个案例。还是有另一个理由“线性化”整个回购? 是的,它确实应该占用整个存储库。我希望能够一直向下滚动,分页是行不通的。此外,它目前已经显示了所有提交。我不想为此失去功能。 看起来不错的项目;祝你好运。如果我不能简化问题,它看起来可能是 300 或 400 行 Objective C,对我来说超出了 *** 响应的范围。感谢您澄清事情。【参考方案2】:

标准topological sort 是 O(n)(OK,O(V+E)),即您应该能够在几分之一秒内对内存中的一百万次提交进行排序。不需要像 Tcl 中那样的增量 hack。

顺便说一句,我每天都使用 GitX(在 OS X 上看起来比 Gitk 好得多)并且没有任何问题(可能是因为我的存储库中没有那些疯狂的合并):)

【讨论】:

有可能,我去看看。我认为计算我现在缓存的图形线更昂贵。这些行的计算需要相当长的时间(10 万次提交约 1 秒),所以我无法每次都重新计算。我仍然需要一些增量更新。【参考方案3】:

好的,所以我在阅读整个补丁时也遇到了同样的困难,但是让我们看看我是否可以根据我的理解将它拼凑起来。

首先,gitk 通过将一串提交压缩成一个弧来简化事情,其中​​包含一系列提交,每个提交只有一个父节点和一个子节点。除了其他任何事情之外,这样做应该会大大减少您必须为排序考虑的节点数量,这将有助于您使用的任何算法。作为奖励,相关的提交最终会组合在一起。

这确实会在您阅读新提交时在查找弧方面引入一些复杂性。有几种情况:

新的提交有一个单亲,或者没有双亲。它延伸了一个(可能是空的)弧。大多数情况下,您只需扩展最近的弧。有几个有趣的子案例: 如果其父级已经有一个子级(即其父级是一个分支点,我猜你事先并不知道),它可能会导致现有弧被拆分。 它可能是将两条弧线连接在一起的“缺失环节”。 你可能已经知道这个提交有多个子节点 新提交有多个父提交(合并提交)。

您可能希望在弧中包含多子或多父提交,或者将它们分开可能更有意义。不管怎样,逐步建立这组弧应该不会太难。

一旦你有了这些弧线,你仍然需要尝试将它们线性化。在您的情况下,上述Wikipedia page 中描述的第一个算法听起来很有用,因为您有一组已知的分支点可用作初始集 S。

其他说明:

重定位提交应该是可管理的。首先,你只需要在连接两条弧时关心,要么通过新的合并提交,新发现的分支点,要么将两条弧合二为一。任何给定的弧都可以轻松地维持其当前的行号范围(假设您可以将弧放在连续的行上),因此遍历树检查所有新祖先是否稍后出现应该很快。 关于绘制图形线我知道的不多,但我想这与您现在所做的不会有太大的不同。

无论如何,我希望这会有所帮助。至少想想很有趣。

【讨论】:

【参考方案4】:

您真的需要一次显示 100k 提交吗?什么样的用户可以吸收这种信息?

你考虑过分页吗?即只计算〜100次提交或其他东西。如果分支线返回很远(离页),您可以使用 Github 的返回箭头之类的东西来显示。

【讨论】:

是的,当前系统运行良好,例如,您可以快速滚动到特定日期。使用分页既乏味又烦人。另外,我不会回到功能较弱的系统(分页)。如果我找不到比当前更好的解决方案,我会坚持下去。

以上是关于git DAG 的增量线性化的主要内容,如果未能解决你的问题,请参考以下文章

非线性合并后如何恢复线性git历史?

第一节 线性表

DAG及拓扑排序

拓扑排序

线性表

数据结构实验 —— 线性表