在时间间隔内迭代git commit历史时缓慢的git rev-list

时间:2017-06-07 04:19:36

标签: git

我编写了一个使用git rev-list -n1 --before=X来使用固定时间间隔迭代Git提交历史记录的工具,以便我看到每年,每月等的最新修订版。

问题在于rev-list在每次通话时开始新的修改步行,而且我回去的父亲需要更长的时间。以下是Linux内核源代码中的一些示例。

$ time git rev-list -n1 --before="Jan 1 2016" HEAD
a889331d759453fa7f424330f75ae4e2b9e02db4

real    0m1.395s
user    0m1.367s
sys 0m0.024s

$ time git rev-list -n1 --before="Jan 1 2015" HEAD
5b5e76218fbdbb71a01d5480f289ead624232876

real    0m2.349s
user    0m2.306s
sys 0m0.036s

$ time git rev-list -n1 --before="Jan 1 2005" HEAD

real    0m5.556s
user    0m5.435s
sys 0m0.105s

如果我想在N个减少日期的循环中调用rev-list,那么该循环将运行N次,每次迭代需要更长时间。文档讨论了位图和对象遍历策略以加快历史,但我无法理解它们。我尝试git repack -ab后跟git rev-list --use-bitmap-index,但这并没有改善结果。

我唯一的要求是,给定HEAD的任何位置,我可以准确地确定在给予--before的日期之前出现的第一个修订版,如果需要,可以跟踪祖先的路径。

对于此用例,使rev-list更快的最佳方法是什么?

3 个答案:

答案 0 :(得分:2)

在一般情况下,除了这两个选项之外别无选择:

  • 支付步行的全部费用,或
  • 存储的信息比git rev-list只需要一个节点时发出的信息

因为图形行走有效地线性化(通过优先级队列,以日期作为优先级)一个任意浓密的树(这个“树”是通过切断重新加入形成的,只是不再重新访问任何已访问过的节点DAG)。例如,假设我们有一个看起来很像这样的提交DAG。假设所有方向弧都指向left-ish(直接左,或上左,或左下)。

       A-....--o
      /         \
...--o----...----o   <-- HEAD
      \         /
       B--...--o

任何地方都有任意数量的节点,有两个或三个点。您的一个选择可能会选择节点A作为第一个符合--before条件的访问者。节点B可能早于A,因此可能是从--before开始的早期HEAD选择的节点(因为节点B可以从{{}到达1}})。但是节点B无法从节点HEAD访问,因此只需从节点A开始,您就永远不会找到节点A

如果你以某种方式让B转储其当前的优先级队列内容(加上它迄今为止访问过的所有节点的信息 - 虽然这不是最终需要获得相同的结果;这只是一个可能加速某些节点修剪的选项)你可以从那些点重新启动它的图形遍历操作,以便搜索git rev-list,从而避免重新访问许多节点在上面的大网格区域。

如果您强烈限制图形遍历,例如,使用B,您可以简单地从最近找到的提交重新开始遍历,因为您知道队列深度始终为1,因此要访问的下一个节点是您之前找到的节点的第一个父节点(您可以将其留给--first-parent)。

答案 1 :(得分:2)

重复扫描列表以选择连续元素是O(N ^ 2)。扫描的效率并不重要,N ^ 2会咬人。

生成一个包含提交ID和日期的列表,然后删除您不想要的内容,并从所选择的sha中生成真实的日志消息。那总共三次,而不是N.

git log --first-parent --pretty=%H\ %cd --date=short \
| awk '$2$3 != last { last=$2$3; print $1}' 'FS=[- ]' \
| git log --no-walk --stdin

在linux repo上花了十五秒冷缓存,带有一个spinny-things hdd,列出了147个提交。重播不到一秒钟。

编辑:在--date-order中为--first-parent进行修改,以考虑所有路径需要25.1秒冷缓存,7.9秒热,以列出782次提交。

答案 2 :(得分:0)

您可以再次尝试相同的命令,添加新的--filter=tree:0选项。这样可以加快所需通缉名单的编制。

这是因为在Git 2.20(2018年第四季度)中,“ rev-list --filter”功能学会了通过“ tree:0”过滤器排除所有树木。

请参见commit 8b10a20(2018年10月18日),commit d9e6d09(2018年10月12日),commit bc5975dcommit cc0b05acommit 696aa73commit 99c9aa9commit 7c0fe33commit f1d02da(2018年10月5日),commit 9202489(2018年8月15日)和commit f447a49Matthew DeVore (matvore)(2018年8月13日)。
(由Junio C Hamano -- gitster --commit 77d5037中合并,2018年10月30日)

  

list-objects:支持跳过树遍历

     

tree:0过滤器不需要遍历它具有的树   过滤掉,因此优化列表对象和列表对象过滤器以跳过   完全穿过树木。

     

list-objects-filter:实现过滤树:0

     

教导列表对象“ tree:0”过滤器,该过滤器可进行过滤   清除所有树和Blob对象(除非明确地指定了其他对象   由用户指定)。该补丁的目的是允许较小的   部分克隆。

     

此过滤器的名称-tree:0-未明确指定   它还可以滤除所有斑点,但这不会引起太多混乱   因为没有引用的树,blob根本没有用   他们。

     

我也把only:commits当作名字,但这是不准确的,因为   这表明已删除带注释的标签,但实际上它们是   包括在内。

     

名称“ tree:0”允许以后根据深度进行过滤,即“ tree:1”   会过滤掉除根树和斑点以外的所有内容。
  为了避免0和大写O之间的混淆,该文档的措辞有些round回,也暗示了对该功能的未来改进。


请注意,Git 2.22(2019年第二季度)将修复回归,其中“ is this object available to us?”检查 众所周知的对象,例如一棵空树(应该产生“是”, 即使没有用于空树的磁盘对象), 已纠正。

请参见commit f06ab02Jeff King (peff)(2019年3月4日)。
(由Junio C Hamano -- gitster --commit 83b13e2中合并,2019年3月20日)

  

rev-list:允许检查存在的缓存对象

     

这修复了7c0fe33中的回归(修订列表:处理丢失的树   对象正确,2018年10月5日),其中rev-list现在会抱怨   磁盘上实际不存在的空树。

     

在提交之前,我们依靠list-objects.c中的遍历代码来   穿过树林。由于它使用parse_tree(),因此我们将   对象查找,包括在“缓存的”对象集中查找   (这就是我们神奇的内部空树所在的位置)。

     

在提交之后,我们改为告诉list-objects.c   缺少树木,我们将使用has_object_file()对其进行检查。但   该函数使用OBJECT_INFO_SKIP_CACHED,这意味着我们不会使用   内部空树。

     

通常不会出现。对于大多数操作,Git将尝试   像其他任何对象一样,写出空的树对象。和   pack-objectspush中的fetch将发送空树(即使   虚拟在发送方)。
  但是,在某些情况下   物。我在野外发现的一个:

     
      
  1. 通过删除所有文件而不使用索引,提交的根树变为空。在这种情况下,它是使用libgit2的树构建器API完成的,但是如所包含的测试所示,可以使用哈希对象通过常规git轻松完成。
      生成的存储库可以正常运行,因为我们可以避免遍历我们自己可到达的提交以进行连接检查。

  2.   
  3. --reference指向(1)中的存储库会触发此问题,因为我们告诉另一端我们已经有了该提交(因此是空树),然后在其上遍历在进行连接检查时(我们抱怨它丢失了)。

  4.   
     

可以说,步骤(1)中的工作流程在编写时应更加小心   空树对象(如果要引用它)。但是这个工作流程确实   在7c0fe33之前工作,因此我们将其还原。


Git 2.22中修复的另一回归:在对象所在的位置出现了“错误”的对象 期望使用其他类型的数据,而不是盲目地假设 对象之间的连接正确。

请参见commit b49e74ecommit 23c2044commit 0616617commit 5c07647Taylor Blau (ttaylorr)(2019年4月5日)。
请参见commit 97dd512commit ee4dfeecommit 8348766Jeff King (peff)(2019年4月10日)。
(由Junio C Hamano -- gitster --commit ea2dab1中合并,2019年5月8日)

  

rev-list:在不使用--missing时让遍历消失

     

Commit 7c0fe33rev-list:正确处理丢失的树对象,   2018-10-05,Git v2.20.0-rc0)教了git-rev-list使用的遍历机制来忽略丢失的树,以便rev-list可以自己处理它们。

     

但是,只有通过oid_object_info_extended()检查   该对象完全存在。
  这可能会错过先前由rev-list检测到的几类错误:

     
      
  • 类型不匹配(例如,我们期望有一棵树但出现斑点)

  •   
  • 无法读取对象数据(例如,由于磁盘上的bitrot)

  •   
     

这尤其重要,因为我们使用“ rev-list --objects”作为我们的   连接检查以允许新对象进入存储库,它将进行   现在想念这些情况(尽管在这里bitrot不太重要,   因为我们通常只是将对象散列并存储)。