Home Resources

Git 如何监控文件变更

Table of Contents

内容主要来自引文1,该文提供了更为详实的说明和性能对比实例。

git status 如何监控文件变更

Git 仓库包含三块区域,已提交内容(Repository),暂存区(Index, Staging Directory)以及工作目录(Worktree, Working Directory)。

在 worktree 中文件按是否被 Git 追踪,分为 tracked 和 untracked 文件;对于 untracked 文件,可能存在被 .gitignore 定义忽略的文件;对于 tracked 文件,它可能存在三个版本,已提交的版本,修改并提交到暂存区的版本,worktree 中的版本。

git-areas.svg

Staged changes

Tracked 文件已提交部分(commit)与被提交到暂存区(index)的部分使用 Git 的内部数据结构存储,这部分的差异内容一般称为 *staged changes*,对应执行 git status 时看到的 Changes to be committed. 这些数据结构针对内容比较设计,这部分的差异比较性能较高。

Unstaged changes

Tracked 文件在 index 区与 worktree 中版本的对比性能相对低下,理论上需要遍历每个 tracked 文件,计算对应文件在 worktree 中当前内容的 hash,与 index 中存储的 hash 对比,判断是否一致,不一致则认为对应文件存在 unstaged changes.

对于一个大型仓库,tracked 文件数量极大,这个过程性能比较低下,Git 从早期版本开始就引入对这个过程的优化,目前主要有三个优化:

  1. lstat() 优化2在 Git 较早版本就引入了,它通过文件的最近修改时间(last modification time, mtime) 判断文件是否改变,通过 lstat() 系统调用可以获取文件的修改时间;如果 mtime 未改变,Git 认为文件内容未改变,如果 mtime 改变,仍要进行 hash 计算;这个优化大大减少需要执行的计算量,不过它仍需要遍历每个 tracked 文件,针对它们执行 lstat()
  2. preload 优化3是在 lstat() 基础上做的改进,充分利用多核,开启多个线程对于文件系统进行并发遍历
  3. FSMonitor (core.fsmonitor) 最终引入一个对于文件系统监控的后台进程,它监控、记录来自操作系统包括创建、删除和修改在哪的文件系统事件,Git 在执行如 git status 命令时会通过它获取这些事件用于优化查找变化文件的过程

Untracked files

对于 untracked 文件的发现,理论上需要

  1. 遍历得到 worktree 中所有的文件路径
  2. 对于每个路径,判断其在 index 中是否存在,忽略掉存在的文件(即 tracked file)
  3. 对于剩下的路径,执行 .gitignore 检测,只保留未 ignore 的
  4. 最终剩下的就是 untracked file

由于目录是一类特殊文件,它的内容是 (filename, i-node) 元组的集合,当元组发生改变,也即目录下文件存在新增、移除、重命名,目录的 mtime (last modification time)将发生改变4,因此 untracked file 可以缓存所有目录的 untracked 文件计算结果来节省大量的目录读取操作。这个优化也即 core.untrackedcache 特性。另外, FSMonitor 优化开启后,也会被用于优化此项操作。

git status 执行结果示例

可以通过 git status 查看仓库当前的状态,比如下面 git status --ignored 的执行结果:

On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   a.c

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a.c

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        b.c

Ignored files:
  (use "git add -f <file>..." to include in what will be committed)
        a.o

为仓库开启优化

git config core.fsmonitor true
git config core.untrackedcache true
# 查看 FSMonitor 服务进程的状态
git fsmonitor--daemon status

另外,Git 也支持使用外部的文件系统监控服务,比如使用 watchman. 具体操作参见引用文章1 Using Watchman and the sample hook script 小节。

Footnotes:

Author: lotuc, Published at: 2023-07-16 Sun 00:00, Modified At: 2023-07-16 Sun 16:17 (orgmode - Publishing)