复制一个 200 MB 的文件,它飞快。在同一个目录里运行 git status,等待时间长到足以让你切去做别的事。这两种观察并不矛盾,因为大块传输衡量的是吞吐量,而 Git 衡量的是每秒能问出多少个小型的文件系统问题,并且 挂载的远程树会把每个问题都变成一次往返。调 VPN 治不好一个形状就不对的工作负载。

为什么 status 是错的基准

Git 需要了解许多文件:哪些被修改、哪些未被跟踪,以及缓存的索引数据是否仍与磁盘上的内容一致。走 SSHFS 时,其中每项观察都可能是一次网络操作。这里的工作单位是文件数量,而不是兆字节。

这就是克隆后第一次 status 很慢的原因,也是满是要点输出(build 产物)的树里后面几次可能更慢的原因。未跟踪扫描会走过你也许并不关心的目录,而每一步都是链路上的一次请求。在 40 ms 的路径上,几千个条目的树就变成几千次往返。确认这道算术不需要抓包。

下载基准测试说明链路没问题;它完全没说一千次小读取今天下午能不能做完。

挪动工作树,而不是调参数

本地克隆把工作树、索引和 Git 操作放在同一台机器上。pull 变成一次看得见的网络步骤,而不是每次文件检查都变成一次。

只要开发者能把所需的源码留在本地,这就适用,而多数时候如此。它也让提交与部署的边界更清晰,因为编辑本地文件不再会意外改动正在运行的远程服务;改动必须像其他任何东西一样推送并部署。代价是代码存在两处,所以要想清楚哪一份是权威副本,以及服务器上的热修复如何回到仓库。这场谈话比一条没人愿意跑的 status 命令便宜。

如果工作区必须留在远端,就在那里运行 Git

有时工作区挪不动:构建需要那个环境、数据太大,或者策略要求源码留在主机上。那就紧挨着文件运行 Git 和相关开发工具,通过获批的远程 shell 或远程开发环境。编辑器可以留在笔记本上,让它的后端在工作树旁运行。

昂贵的操作于是在远程主机上本地完成,网络承载的是编辑器流量,而不是几千次 stat 调用。这与本地克隆是同一个修法,只是表述为部署选择,而不是迁移。

同步是有主人的工作流,不是缓存

同步工作区可以减少重复的远程读取,但它不是透明缓存。它需要每次改动都有主人、冲突有规则,并就双方都认为归自己的生成文件达成一致。没有这些规则,这套安排带来的是争执而不是便利。

没有这些规则的双向同步器会制造出与它解决的问题不同的问题,通常是一次谁也说不清归属的合并。除非你明白两个 Git 进程如何协同,否则不要在同一个共享工作树上跑两个。

然后再调参,并让 SSH 保持理智

安排正确之后,有两项设置会改变 Git 需要询问的文件数量:status.showUntrackedFiles 以及 core.untrackedCache 背后的未跟踪缓存。两者都不改变每次询问在 40 ms 上的代价;它们只改变询问的次数。等往返都变成本地之后,再去动旋钮。

给那个让你恼火的命令计时,而不是从拷贝一个大文件的基准去推理,并且别动受支持的 SSH 加密。过时的加密不会缩短一次往返;它只是加上第二个比第一个活得还久的问题。git status 参考文档值得读一遍,因为这个命令干的活比它的名字多得多。

相关文章