使用最新的 Debian 版本的 git(我使用的是 1.7.2.5),我注意到一个.git/index
文件可能会发生神秘的变化,而我没有执行任何我认为应该更改存储库的操作。(我的 shell 偶尔会运行git branch
,因此它可以显示签出的分支,但这不应该改变任何东西。)更改导致.git/index
文件的长度与原始文件相同,但包含不同的位。 是什么导致了这种变化,我该如何阻止它?
(这个改变很不方便,因为它把Unison文件同步器弄乱了。)
使用最新的 Debian 版本的 git(我使用的是 1.7.2.5),我注意到一个.git/index
文件可能会发生神秘的变化,而我没有执行任何我认为应该更改存储库的操作。(我的 shell 偶尔会运行git branch
,因此它可以显示签出的分支,但这不应该改变任何东西。)更改导致.git/index
文件的长度与原始文件相同,但包含不同的位。 是什么导致了这种变化,我该如何阻止它?
(这个改变很不方便,因为它把Unison文件同步器弄乱了。)
索引文件不应该只是随机更改。那是暂存树,是提交存储库和工作树之间的缓冲区。为了提高效率,它还存储了一些关于工作树的元数据(您可以修改的签出文件),这将允许更快status
或diff
结果。要查看存储了什么样的此类信息,请尝试执行git ls-files --debug
. 对于每个文件和目录,这应该打印如下内容:
path/to/file
ctime: 1332898839:873326227
mtime: 1332898839:873326227
dev: 2052 ino: 4356685
uid: 1000 gid: 100
size: 3065 flags: 6c
因此,如果一个文件在磁盘上发生任何变化,而不是它的内容,而是它使用的 inode 等内部内容,它将在index
下次使用索引时触发对文件的更新。
git branch
不更新索引,因为它只检查.git/HEAD
文件.git/refs/heads
和.git/packed-refs
文件,它不关心索引或工作树。git diff
并且git status
,另一方面,使用索引。
我做了一个实验:我复制了当前index
文件,我创建了一个新版本的文件,确保将分配一个新的 inode 给它(复制,删除原始,将副本重命名回原始名称),执行git status
,然后将新的索引文件与原始副本进行比较。有两件事发生了变化:其中包含受影响文件的行,更改位于文件名之前的字节中,以及索引文件末尾的几个字节,可能是最后一次索引计算的时间戳。文件的整体大小保持不变。
回到您的问题,如果您自己没有执行任何涉及索引的命令,那么也许您有另一个工具可以为您执行此操作:IDE 插件或了解 git 存储库并检查状态的文件浏览器扩展的 git 存储库。或者,还有另一个过程可以改变文件在磁盘上的存储方式,例如磁盘碎片整理实用程序。
我也遇到过这个问题,我相信是 unison 和 git 之间的交互导致了这个问题。unison 同步两个目录时,不同步ctimes。这意味着在 git 存储库的一个副本中,比如副本 2,文件 ctimes 与存储在 .git/index 中的时间不匹配。这意味着副本 2 中的 .git/index 将在您下次运行统计文件的 git 命令时更新。当 unison 运行时,.git/index 被复制到副本 1,但其内容与那里的 ctimes 不匹配。所以下次在那里运行 git 命令时,索引会更新。然后 unison 将其复制到副本 2,依此类推。
我还没有找到一个合理的解决方法。设置 core.trustctime=false 没有帮助。
就 .git/index 是一个缓存而言,它应该从同步中省略。但我相信 .git/index 也用于暂存文件,并且可能在一台机器上启动该过程并在另一台机器上完成它,这需要 .git/index 同步。
(我知道有些人认为将 git repos 与 unison 同步很奇怪,但 unison 的重点是您可以在两台不同的机器上工作之间切换,并从您离开的地方继续。这是一个了不起的工具!)
对于这个问题的作者来说,这可能不是解决方案,但就我而言,etckeeper 的每日自动提交功能是罪魁祸首。
我在设置中看到了同样的问题,我让 Unison 在两台机器之间同步我的主目录(包含 3 个 git repos),以及一个 cd 到每个 repo 目录并每天运行“git status”的 cron 作业(并通过电子邮件发送给我如果未签入更改)。我的测试表明这是由于 .git/index 存储了特定于机器的数据,例如文件的 inode 数量[1]。
要对此进行测试,请使用已经在两台机器上同步且相同的存储库。将 .git/index 从一台机器复制到另一台机器,例如
scp -p machineB:/home/me/myrepo/.git/index /home/me/myrepo/.git/index
现在比较这两个文件,您应该会发现它们是相同的:
sha1sum /home/me/myrepo/.git/index
ssh machineB "sha1sum /home/me/myrepo/.git/index"
现在运行:git status
现在再次比较这两个文件,你会发现它们已经改变了:
sha1sum /home/me/myrepo/.git/index
ssh machineB "sha1sum /home/me/myrepo/.git/index"
我没有看到解决方案,因为如果不运行诸如更新索引的 git status 之类的命令,就无法使用 git。
罪魁祸首竟然是 Emacs VC 模式: https ://emacs.stackexchange.com/questions/38418/could-magit-be-writing-git-index-without-my-intervention
为了使这篇文章成为答案,而不是评论,我不得不说更多。所以这里转载了正确的答案:
Emacs VC 使用计时器来定期刷新一些信息并调用
git
命令来这样做,其中一些会触及索引。如果 VC 是导致此问题的原因,则删除
Git
fromvc-handled-backends
可能会解决此问题。
在 Emacs 中禁用 VC 并不能真正解决这个问题。它只会阻止 Emacs 自己运行git status
,但手动运行它仍然会修改文件.git/index
并导致与 Unison 的虚假修改/冲突。
git 邮件列表建议了一个适合我的解决方法 [1]:
times = true
在 Unison ( )中启用 mtime 同步git config core.trustctime false
git config core.checkstat minimal
(当然可以全局设置 git-config 选项。)
通过这些设置,git 现在在检查文件是否被修改时只查看 mtime 的组成部分和文件大小,并且两者都由 Unison 同步。