我编写了一个使用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
更快的最佳方法是什么?
答案 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 bc5975d,commit cc0b05a,commit 696aa73,commit 99c9aa9, commit 7c0fe33的commit f1d02da(2018年10月5日),commit 9202489(2018年8月15日)和commit f447a49,Matthew 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 f06ab02的Jeff 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-objects
或push
中的fetch
将发送空树(即使 虚拟在发送方)。
但是,在某些情况下 物。我在野外发现的一个:
通过删除所有文件而不使用索引,提交的根树变为空。在这种情况下,它是使用libgit2的树构建器API完成的,但是如所包含的测试所示,可以使用哈希对象通过常规git轻松完成。
生成的存储库可以正常运行,因为我们可以避免遍历我们自己可到达的提交以进行连接检查。用
--reference
指向(1)中的存储库会触发此问题,因为我们告诉另一端我们已经有了该提交(因此是空树),然后在其上遍历在进行连接检查时(我们抱怨它丢失了)。可以说,步骤(1)中的工作流程在编写时应更加小心 空树对象(如果要引用它)。但是这个工作流程确实 在7c0fe33之前工作,因此我们将其还原。
Git 2.22中修复的另一回归:在对象所在的位置出现了“错误”的对象 期望使用其他类型的数据,而不是盲目地假设 对象之间的连接正确。
请参见commit b49e74e的commit 23c2044,commit 0616617,commit 5c07647和Taylor Blau (ttaylorr
)(2019年4月5日)。
请参见commit 97dd512的commit ee4dfee,commit 8348766,Jeff King (peff
)(2019年4月10日)。
(由Junio C Hamano -- gitster
--在commit ea2dab1中合并,2019年5月8日)
rev-list:在不使用--missing时让遍历消失
Commit 7c0fe33(
rev-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不太重要, 因为我们通常只是将对象散列并存储)。