在我的回购中,cwd = getcwd()
chdir('/opt/project')
translation_unit = index.parse(source_file, args=args)
chdir(cwd)
和git diff
都在不到一秒的时间内快速运行。但是git stash
在显示第一个大块之前需要20秒。为什么会这样?
答案 0 :(得分:2)
随着Git 2.25.2(2020年3月)的改进,该功能将简化代码。
参见discussion。
请参见commit 26f924d的Elijah Newren (newren
)(2020年1月7日)。
(由Junio C Hamano -- gitster
--在commit a3648c0中合并,2020年1月22日)
unpack-trees
:如果不需要更新,请提前退出check_updates()
签名人:伊利亚·纽伦
check_updates()
有很多代码可以反复检查是否设置了o->update
或o->dry_run
。(请注意,
o->dry_run
是!o->update,
的近义词,但与commit 2c9078d05bf2不同((unpack-trees
:将dry_run
标志添加到{ {1}}”,2011年5月25日,Git v1.7.6-rc0)。)
实际上,只要条件出现,该功能几乎都会变成无操作状态unpack_trees_options
被满足。
通过在函数开始时检查此条件来简化代码,当此条件成立时,请做一些相关的事情并尽早返回。
有些事情使转换不太明显:
- 当不希望进行更新时,check_updates()实际上没有变成无操作的事实可能有点令人惊讶。
但是,提交33ecf7eb61(使用它们更新工作树后丢弃“!o->update || o->dry_run
”缓存条目,2008-02-07,Git v1.5.5-rc0)将丢弃未使用的缓存条目放入{ {1}},因此我们仍然需要保留对deleted
的呼叫。
此调用可能属于另一个函数,但肯定是必需的,因为如果删除该调用,测试将失败。- 原始文件无条件称为
check_updates()
。
从技术上讲,提交7847892716(remove_marked_cache_entries()
:介绍remove_scheduled_dirs()
,2009-02-09,Git v1.6.3-rc0)应该使该调用成为条件调用,但实际上并不重要因为当跳过对unlink_entry()的所有调用时,unlink_entry()
变为空操作。
因此,我们不需要调用它。- 在
schedule_dir_for_removal()
上,原始文件会调用remove_scheduled_dirs()
两次,围绕一堆跳过的更新。
对(o->dry_run && o->update)
的这两个调用相互抵消,因此在git_attr_set_direction()
为真时可以忽略,就像在git_attr_set_direction()
时一样。- 即使在
o->dry_run
时,该代码也会先前调用!o->update
和setup_collided_checkout_detection()
。
但是,这只是一个昂贵的无操作操作,因为report_collided_checkout()
仅清除了每个缓存项的o->dry_run
标志,并且setup_collided_checkout_detection()
报告了设置了哪些标志。
由于试运行将跳过所有CE_MATCHED
调用,因此report_collided_checkout()
将永远不会设置,因此不会报告任何冲突。
由于无论如何我们都无法检测到冲突,因此,跳过冲突检测设置和报告是一种优化。- 即使在
checkout_entry()
时,以前的代码也会调用CE_MATCHED
和get_progress()
。
这可以显示跳过所有更新花费了多长时间,这有点用处。
由于我们跳过更新,因此可以跳过显示跳过更新需要多长时间。
答案 1 :(得分:1)
我注意到同样的问题。这至少在一年前开始,但此后一直没有改善。 我还在很大的仓库上使用git。不幸的是,在我的案例中,其中还包含许多二进制数据,因为它只是使用git_svn的SVN存储库的镜像,而我的同事们认为将二进制测试数据放入存储库中是个好主意。
没有答案,只是提示和猜测在哪里搜索:
它的最大区别是,在stash -p
的情况下调用函数stash_patch
。否则为stash_working_tree
。
在stash_patch
中有称为执行其他git命令的子进程。其中之一是read-tree
(请参阅:man git-read-tree
)。最终命令如下所示:GIT_INDEX_FILE=index.stash.<PID> git read-tree HEAD
。实际上,这没有时间。
下一步是另一个称为GIT_INDEX_FILE=index.stash.<PID> git add--interactive --patch=stash -- <PATH>
的子进程– 这是所有读取结果的来源,并且一直占用所有时间。
有趣的是:仅在GIT_INDEX_FILE=index.stash.<PID> git status
之后拨打GIT_INDEX_FILE=index.stash.<PID> git read-tree HEAD
与git add--interactive
一样昂贵。实际上,add--interactive
是一个实现add -p
的perl脚本。我不了解perl,也很难阅读,但可能会以某种方式检查工作目录状态,并使用与git status
相同的代码。
基本思想是:
最昂贵的部分是为了获得工作目录的状态而没有临时索引。为什么这么贵我不知道。可能有一些缓存的数据无效,它必须读取工作副本中的所有文件至少达到一定数量才能与临时索引进行比较,但是要了解这一点,必须更深入地研究git status
的内部。
我试图这样测量:
GIT_INDEX_FILE=.git/index.stash.test git read-tree HEAD
GIT_TRACE_PERFORMANCE=/tmp/trace_status GIT_INDEX_FILE=.git/index.stash.test git st .
结果如下:
20:31:20.439868 read-cache.c:2290 performance: 0.000269090 s: read cache .git/index.stash.test
20:31:20.441368 preload-index.c:147 performance: 0.001419629 s: preload index
20:32:15.568433 read-cache.c:1605 performance: 55.128484420 s: refresh index
20:32:15.568611 diff-lib.c:251 performance: 0.000054503 s: diff-files
20:32:15.568847 unpack-trees.c:1546 performance: 0.000004362 s: traverse_trees
20:32:15.568868 unpack-trees.c:447 performance: 0.000008189 s: check_updates
20:32:15.568874 unpack-trees.c:1643 performance: 0.000040807 s: unpack_trees
20:32:15.568879 diff-lib.c:537 performance: 0.000079322 s: diff-index
20:32:15.569115 name-hash.c:600 performance: 0.000197074 s: initialize name hash
20:32:15.573785 dir.c:2326 performance: 0.004883714 s: read directory
20:32:15.574904 read-cache.c:3017 performance: 0.001083674 s: write index, changed mask = 82
20:32:15.575125 trace.c:475 performance: 55.135763475 s: git command: /usr/lib/git-core/git status .
20:32:15.575421 trace.c:475 performance: 55.136831211 s: git command: git st .
我的仓库如下:
>$ du -hd 1
1,1M ./.idea
74M ./code
3,0G ./.git
2,4G ./test-data
5,5G .
如果跟踪直接应用于git stash -p
,则为类似图片:
20:43:55.968088 read-cache.c:1605 performance: 59.716998605 s: refresh index
20:43:55.969584 trace.c:475 performance: 59.719061140 s: git command: git update-index --refresh
git update-index --refresh
状态的手册页:
USING --REFRESH
--refresh does not calculate a new sha1 file or bring the index up to date for mode/content changes. But what it does do is to "re-match" the stat information of a file with the index, so that you can refresh the index for a
file that hasn’t been changed but where the stat entry is out of date.
For example, you’d want to do this after doing a git read-tree, to link up the stat index details with the proper files.