(这不是How does git detect that a file has been modified?的副本,因为我问的是Windows,引用的QA提及stat
和lstat
,它们不适用于Windows。)
对于像SVN和TFS这样的传统系统,需要明确地手动通知“状态数据库”本地工作区中文件的任何更改:默认情况下文件是只读的,这样您就不会意外地进行更改首先明确通知您的SVN / TFS客户端。幸运的是,IDE集成意味着导致文件的添加,修改,删除和重命名(即“检出”)的操作可以自动传递给客户端。这也意味着你需要像TortoiseSVN这样的东西来处理Windows资源管理器中的文件,以免你的更改被忽略 - 而且你应该定期运行一个经常冗长的服务器到本地比较扫描来检测任何变化。
但Git没有这个问题 - 在我的Windows机器上,我可以拥有一个带有数十万个文件的千兆字节大小的repo,很多级别,但是如果我对一个非常深的嵌套文件进行1个字节的更改,我可以看到Git在运行git status
后知道。这是一个奇怪的部分 - 因为git不使用任何守护程序进程或后台任务 - 运行git status
也不涉及我可以看到的任何重要的IO活动,我立即得到结果,它不会破坏我的磁盘寻找我所做的改变。
此外,Git GUI工具,例如Git与Visual Studio 2015的集成也有一定程度的魔力 - 我可以在记事本或其他程序中进行更改,并且VS'Git Changes窗口会立即进行更改。 VS可能只是使用ReadDirectoryChanges
(FileSystemWatcher
) - 但是当我在Process Explorer中查看devenv
进程时,我看不到任何相应的句柄,但这也没有解释如何git status
看到了这些变化。
答案 0 :(得分:2)
Git在索引中记录的每个文件上运行Windows等效的POSIX-y lstat(2)
调用,以便首先尝试确定文件是否被修改。它将修改时间和从该信息中获取的大小与索引中为该文件记录的值进行比较。
此操作在NTFS(和网络映射驱动器)上的速度非常慢,因此有一段时间Git for Windows获得了一个由core.fscache
configuration option控制的特殊调整,默认情况下已经启用了2或3个GfW版本。我不知道确切的详细信息,但它会尽量减少Git需要lstat(2)
文件的次数。
IIUC,由core.fscache
启用的机制没有使用文件系统观看Win32 API,因为Git在您的系统上不运行守护进程/服务;所以它只是优化了Git向文件系统层询问被跟踪文件的统计信息的方式。
答案 1 :(得分:2)
正如Briana Swift和kostix指出的那样 - 正在扫描你的磁盘。但是,在查找未分级更改时,不需要读取磁盘上的每个文件。相反,它可以查看存储在索引中的元数据,以确定要更密切检查的文件(实际读取它们)。
如果使用git-ls-files
命令检查索引,则可以看到此元数据:
% git ls-files --debug worktree.c
worktree.c
ctime: 1463782535:0
mtime: 1463782535:0
dev: 16777220 ino: 120901250
uid: 501 gid: 20
size: 5591 flags: 0
现在,如果您运行git status
,git将会查看磁盘上的worktree.c
。如果时间戳和文件大小匹配,那么git将假定您不更改了此文件。
但是,如果时间戳和文件大小不匹配,则git会更仔细地查看该文件,以确定您是否更改过它。
所以git " thrash"磁盘,但是比你执行tf reconcile
之类的操作以更有限的方式来检查你的更改。 (当然,TFVC旨在处理非常大的工作树,如果您正确使用它,永远不会触摸您的磁盘。)
是的 - Visual Studio 确实有一些魔力。它在您的工作目录和Git存储库的某些部分中运行后台文件系统观察程序。当它注意到工作目录中的更改时,它将重新计算git status
。它还会查看Git存储库中分支的更改,以了解您何时切换分支或使用远程重新计算本地存储库的状态。
答案 2 :(得分:0)
Git的git status
过程非常轻量级。
git status
检查索引(在运行git add
之前也称为暂存区域)和工作目录(在git add
之后但在git commit
之前),然后比较这些具有最后提交版本的文件。 Git不是必须遍历存储库中的每个文件,而是首先检查这些区域以查看在最近的提交中要查找的内容。
git diff
的工作方式类似。我建议查看here以获取更多信息。