ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

isomorphic-git status 全解析:用纯 JavaScript 精准判断任意文件的 Git 状态

isomorphic-git status 全解析:用纯 JavaScript 精准判断任意文件的 Git 状态 开发工具【免费下载链接】isomorphic-gitA pure JavaScript implementation of git for node and browsers!项目地址https://gitcode.com/gh_mirrors/is/isomorphic-git点击查看免费下载git.status是 isomorphic-git 提供的单文件状态查询 API只要给定工作区目录与文件路径它就会返回一个描述该文件当前 Git 状态的字符串如unmodified、*modified、ignored。本指南以 0.70.7 版本文档为核心结合仓库源码与测试用例带你完整掌握status的参数语义、11 种返回值的判定逻辑、.gitignore边界行为以及缓存与性能调优方案。读完本文你将能够在自己基于 isomorphic-git 构建的工具如 CLI、CI 检查、编辑器插件中精确复刻原生git status的文件级判断能力。快速开始一行代码读取文件状态git.status的用法非常直接只需要提供文件系统、工作区目录和目标文件路径let status await git.status({ dir: /, filepath: README.md }) console.log(status)在上面的示例中dir指向仓库的工作区根目录filepath是相对于该目录的路径示例中为README.md。调用后会返回一个Promisestring解析为该文件当前的 Git 状态字符串。在 Node.js 环境中你需要同时传入fs参数或通过插件系统注册文件系统import git from isomorphic-git import fs from fs const status await git.status({ fs, dir: /path/to/repo, filepath: README.md }) console.log(status) // 例如 unmodified关于文件系统的注册与Bring Your Own FSBYOFS机制可参考 plugin_fs.md 与 guide-fs.mddir与gitdir两个概念的区别见 dir-vs-gitdir.md。参数详解status的核心参数如下表所示参数类型 [ 默认值]描述corestring default插件注入时使用的插件核心标识符fs [deprecated]FileSystem包含 git 仓库的文件系统。会覆盖插件系统提供的fsdirstring工作树目录路径gitdirstring join(dir, .git)git 目录路径filepathstring要查询状态的文件路径returnPromisestring解析成功后返回文件的 git 状态从源码看参数的默认值与校验在 src/api/status.js 的实现中参数默认值与校验逻辑清晰可见export async function status({ fs: _fs, dir, gitdir join(dir, .git), filepath, cache {}, refresh true, }) { try { assertParameter(fs, _fs) assertParameter(gitdir, gitdir) assertParameter(filepath, filepath) const fs new FileSystem(_fs) const updatedGitdir await discoverGitdir({ fsp: fs, dotgit: gitdir }) // ...几个值得注意的细节gitdir默认值为join(dir, .git)绝大多数场景只需传dir。只有当使用裸仓库bare repository或gitdir与dir分离时才需要显式传入与原生 git 的--work-tree/--git-dir参数对应。cache默认是空对象{}用于跨调用共享中间解析结果packfile 等详见下文性能优化一节。refresh默认是true这是源码中比文档更进一步的参数0.70.7 版本文档未列出但源码已实现。当工作区文件内容与暂存 blob 一致时status会顺手刷新.git/index的 stat 缓存设为false后调用对 index 变为只读代价是后续对 stat 信息已漂移的文件会重新计算 SHA1。discoverGitdir即使传入的是工作区路径而非.git目录也会向上探测真正的 git 目录位置兼容 linked worktree 等布局。filepath 的路径约定filepath必须是相对dir的路径源码中通过join(dir, filepath)组装实际文件位置并通过getOidAtPath按/分段在 HEAD 树中逐层定位见 src/api/status.js 中的getOidAtPath函数。这意味着嵌套目录如src/utils/index.js也只需直接给出相对路径字符串。返回值详解11 种状态的完整语义status可能返回的字符串值如下表status描述ignored文件被某个 .gitignore 规则忽略unmodified文件与 HEAD 提交一致未做修改*modified文件有修改但尚未暂存*deleted文件已被删除但删除操作尚未暂存*added文件未被跟踪untracked尚未暂存absent文件在 HEAD 提交、暂存区和工作区中均不存在modified文件有修改且已暂存deleted文件已被删除且已暂存added此前未被跟踪的文件现已暂存*unmodified工作区与 HEAD 提交一致但 index暂存区内容不同*absent文件不在工作区和 HEAD 提交中但存在于暂存区命名规律值得单独说明带*前缀的状态表示存在未被暂存staged的差异不带前缀则表示差异已经进入暂存区或完全没有差异。例如*modified是改了但没git addmodified是改了且已git add。从源码看判定逻辑H/I/W 三位布尔模型11 种状态并非凭空枚举而是由三个布尔量组合而成。在 src/api/status.js 中可以看到核心判定模型const H treeOid ! null // head文件是否存在于 HEAD 提交 const I indexEntry ! null // index文件是否存在于暂存区 const W stats ! null // working dir文件是否存在于工作区treeOid来自getHeadTreegetOidAtPath解析HEAD引用得到 commit再读取其 tree 对象按路径逐层查找该文件的 blob OIDindexEntry来自GitIndexManager.acquire在 index 中线性查找路径匹配的条目stats来自fs.lstat(join(dir, filepath))工作区中是否存在该文件。随后源码按照 H/W/I 的 8 种组合逐一分支源码中以---、-A-、--A等注释直观标注了每种组合if (!H !W !I) return absent // --- if (!H !W I) return *absent // -A- if (!H W !I) return *added // --A if (!H W I) { // 新增文件比较工作区 oid 与暂存 oid return workdirOid indexEntry.oid ? added : *added } if (H !W !I) return deleted // A-- if (H !W I) return *deleted // AA- / AB- if (H W !I) return *undeleted // A-A / A-B从 index 删除但工作区还在 if (H W I) { // 全部存在进一步比较三个 oid if (workdirOid treeOid) { return workdirOid indexEntry.oid ? unmodified : *unmodified } else { return workdirOid indexEntry.oid ? modified : *modified } }其中H W I文件在三个位置都存在是最常见的情况此时需要调用getWorkdirOid计算工作区文件的 blob OID再与 HEAD OID、index OID 两两比较三者一致 →unmodified工作区 HEAD、但 index 不同 →*unmodified即暂存区与 HEAD 不一致典型场景是git add后又把文件恢复成原样工作区 ! HEAD、但工作区 index →modified修改已暂存工作区 ! HEAD 且 ! index →*modified修改未暂存。值得注意的是源码中还包含了文档未列出的两种额外状态*undeleted文件已从 index 删除但工作区仍存在且内容与 HEAD 一致与*undeletemodified已从 index 删除但工作区存在且有修改它们对应H W !I的分支。0.70.7 版本文档的状态表没有收录这两项但源码的 JSDoc 与测试均已覆盖属文档滞后于实现的情况。状态流转的完整测试验证仓库的tests/test-status.js 完整演示了修改 → 暂存过程中的状态迁移// 初始a.txt 未修改b.txt 改了没 addc.txt 删了没 addd.txt 未跟踪e.txt 不存在 const a await status({ fs, dir, gitdir, filepath: a.txt }) // unmodified const b await status({ fs, dir, gitdir, filepath: b.txt }) // *modified const c await status({ fs, dir, gitdir, filepath: c.txt }) // *deleted const d await status({ fs, dir, gitdir, filepath: d.txt }) // *added const e await status({ fs, dir, gitdir, filepath: e.txt }) // absent // 逐个 add / remove 之后 await add({ fs, dir, gitdir, filepath: b.txt }) // 现在 b 是 modified await remove({ fs, dir, gitdir, filepath: c.txt }) // 现在 c 是 deleted await add({ fs, dir, gitdir, filepath: d.txt }) // 现在 d 是 added测试还覆盖了两个怪癖场景*unmodified把a.txt改成Hi后add再写回原内容。此时工作区与 HEAD 一致但 index 记录的是Hi的 OID返回*unmodified*undeletedremove后工作区文件仍在返回*undeleted*absent新增e.txt并add然后删除工作区文件此时文件仅存在于 index返回*absent。ignored 状态.gitignore 的精确边界当文件在 HEAD 与 index 中都不存在即未被跟踪时status才会检查.gitignoreif (treeOid null indexEntry null) { const ignored await GitIgnoreManager.isIgnored({ fs, gitdir: updatedGitdir, dir, filepath, }) if (ignored) return ignored }这一点与原生 git 的行为保持一致.gitignore只管辖未跟踪文件。测试用例专门验证了已跟踪文件命中 .gitignore 规则的边界情况// 先把 i.txt 加入跟踪再写入 .gitignore 忽略它并修改 i.txt 内容 expect(await status({ fs, dir, gitdir, filepath: i.txt })).toEqual(*added)即使.gitignore写了i.txt已跟踪文件的真实状态此时它是有修改的未跟踪文件仍然会如实上报而不是错误地返回ignored。源码注释也明确说明了这一点statusMatrix同样遵循该语义。测试还覆盖了嵌套忽略规则f.txt、g/g.txt、h/h.txt均因各级.gitignore返回ignored而i/i.txt未被忽略则返回*added。性能优化cache 参数与 statusMatrix不要逐文件裸调用 status如果对仓库每个文件都单独调用一次status而不共享任何中间结果性能会非常糟糕。原因在于每次调用都可能需要重新读取并解析.git/objects/pack中的 packfile——对大型仓库而言单个文件的状态查询可能耗 10ms100ms累积到数千个文件就是数分钟级别的开销并行调用甚至会同时挤爆内存详见 cache.md 中的演示与实测数据。共享 cache 对象解决方法是传入同一个cache对象让 packfile 的解析结果在多次调用间复用let cache {} for (const filepath of await git.listFiles({ fs, dir, cache })) { console.log(${filepath}: ${await git.status({ fs, dir, filepath, cache })}) } // 用完后丢弃引用即可cache 只是一个普通对象会被垃圾回收 cache {}cache的具体机制是isomorphic-git 会把中间数据以 Symbol 属性挂到传入对象上因此不要直接操作cache内部清空缓存只需删除所有引用等待 GC。src/api/status.js 中cache {}的默认值也意味着不传 cache每次调用都是零缓存的全量计算。批量场景优先选 statusMatrix当需要一次性了解整个仓库的状态时官方推荐使用statusMatrixsrc/api/statusMatrix.js文档见 statusMatrix.md。它基于walk机制一次性遍历 HEAD / WORKDIR / STAGE 三棵树以[filepath, head, workdir, stage]四元组构成的二维数组返回全部文件状态比逐文件status快几个数量级cache.md 中给出的实测对比是 843ms vs 2 分钟以上。选择建议只关心单个或少数文件→status语义直白返回人类可读字符串需要全量状态报告、变更清单、CI 检查→statusMatrix返回紧凑矩阵且支持filter、filepaths、ref等参数还能通过patternglob 过滤见 statusMatrix.md 的示例在status循环场景中务必共享cache对象。边界场景与实现细节空仓库无任何提交getHeadTree在解析HEAD时若抛出NotFoundError分支尚无提交会返回空树try { oid await GitRefManager.resolve({ fs, gitdir: updatedGitdir, ref: HEAD }) } catch (e) { if (e instanceof NotFoundError) return [] // 处理没有提交的新分支 }因此新init的仓库也能正常工作。测试验证在空仓库中写入a.txt返回*addedadd过的b.txt返回added。core.autocrlf 与 CRLF/LF 归一化getWorkdirOid计算工作区文件哈希时会读取core.autocrlf配置并按其归一化规则处理避免CRLF 检出导致 LF blob 被误判为已修改const config await GitConfigManager.get({ fs, gitdir: updatedGitdir }) const autocrlf await config.get(core.autocrlf) const object await fs.read(join(dir, filepath), { autocrlf })对应测试honours core.autocrlf when hashing the working copy验证在core.autocrlf true的仓库中blob 以 LF 存储、工作区为 CRLF 时status仍返回unmodified。refresh 参数与 index stat 缓存默认refresh true时若工作区文件内容哈希与 index OID 一致但 stat 信息不同例如 mtime 被触碰status会顺手把新 stat 写回 index源码中通过GitIndexManager.acquire调用index.insert让后续调用能直接复用缓存 OID 而免于重新哈希。当fs.stat无法提供可靠大小如 BrowserFS 的 HTTP 后端返回 size -1时会跳过刷新。测试does not modify .git/index when refresh is false验证了refresh: false下 index 文件字节级不变只读语义。小结git.status是 isomorphic-git 文件状态判断的基石 API三个参数dir、gitdir、filepath即可获得语义精确的状态字符串其内部通过 HEAD / index / workdir 的三位布尔模型加上 OID 两两比较完整复刻了原生 git 的状态机.gitignore只影响未跟踪文件、core.autocrlf参与工作区哈希计算等细节也与原生 git 保持了一致。在批量场景中请务必使用共享cache或直接切换到statusMatrix避免陷入逐文件解析 packfile 的性能陷阱。进一步阅读源码实现、测试用例、statusMatrix 文档、cache 参数详解、dir 与 gitdir 的区别。赞分享开发工具【免费下载链接】isomorphic-gitA pure JavaScript implementation of git for node and browsers!项目地址https://gitcode.com/gh_mirrors/is/isomorphic-git点击查看免费下载相关推荐isomorphic-git isDescendent 使用指南纯 JavaScript 判断 Git 提交的祖先关系isomorphic git isDescendent 使用指南纯 JavaScript 判断 Git 提交的祖先关系 导读 isDescendent 是 i开发工具探索 **Isomorphic Git**全栈友好的纯JavaScript实现Git探索 Isomorphic Git 全栈友好的纯JavaScript实现Git 是一个令人惊艳的开源项目它完全使用JavaScript编写可以在Node.开发工具Tiny RDM 在 macOS 上安装后无法打开怎么解决Tiny RDM 在 macOS 上安装后无法打开怎么解决 在 macOS 上安装 Tiny RDM 桌面版后有的用户双击启动会收到系统提示报 不受信任开发工具上一篇HsMod 炉石传说插件60 功能解决换皮肤、MMR 显示与挂机开包下一篇WechatHook新手也能跑通的微信自动化实验田附完整避坑清单创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表