☰
MooTool Next Compose 的 Vault Git 能力与 Electron 行为对齐:基于 vaultGitService 对照登记与 GitEngine 源码的深度解析
2026/10/16 2:21:01 网站建设 项目流程
  • 桌面应用
  • 开发工具

【免费下载链接】MooTool

Handy tool set for developers

项目地址:https://gitcode.com/gh_mirrors/mo/MooTool
点击查看免费下载

MooTool 是一个面向开发者的跨平台工具箱,其 Compose 桌面版(next-compose)正在将 Electron 版的 Vault Git 版本管理能力逐项迁移并对齐。electron-next-vaultGitService-vitest.md 正是这一对账工作的核心登记表,逐条列出 ElectronvaultGitService.integration.test.ts覆盖的 Git 行为及其在 Compose 侧GitEngineTest等测试中的对应验证。本文将以这份登记表为骨架,结合 GitEngine.kt 的引擎实现与两侧测试源码,讲清楚 Vault Git 的初始化、提交、冲突处理、检查点、锁恢复、远端管理与并发安全等机制,帮助你理解这套「以测试对齐替代人工比对」的跨实现迁移方法,以及每项行为背后的工程细节。

一、这份对照登记表是什么

登记表的结构非常简单:每一行描述一个「caseId」,从三个维度记录一个行为点的对齐情况:

维度说明
sourceProduct行为来源产品,绝大多数为 MooTool Next Electron
sourceFile源实现/测试所在文件,主要是next/src/shared/vaultGitService.integration.test.ts
Compose 验证Compose 侧对应的测试类与方法,附带 DIFF 文档编号

登记表覆盖约 35 个行为点,可以归类为以下几组:

  • 基础生命周期:init、commit、history、diff(含提交级与工作区级);
  • 冲突处理:pull导致分叉合并冲突、merge/rebase的resolve、abort、continue;
  • 操作守卫(Guard):merge/rebase 期间禁止 commit/pull/push、未解决冲突禁止 continue/commit/pull/push;
  • 自动化检查点(automatic checkpoint):首次使用自动初始化、配置远端后自动 push、冲突期跳过检查点;
  • 健壮性与安全:非安全路径拒绝、嵌套 Vault 不被父仓库吸收、index.lock陈旧锁恢复、并发提交序列化、Unicode 路径、提交说明截断 300 字符、远端 URL 校验与空 URL 移除;
  • fetch/push 语义:fetch --prune、分叉后的 ahead/behind 报告、非快进 push 拒绝;
  • UI 呈现:VaultGitDialog冲突行「使用本地/远端」Tab 帧、顶栏分支图标与操作钮的 Lucide 对齐。

每个 Compose 验证项都挂着一个 DIFF 编号(如 DIFF-442),DIFF 文档是「行为差异描述 + 修复方案 + 验证结果」的完整记录,两者配合构成了可追溯的对齐证据链。

二、核心引擎:GitEngine 的职责划分

Compose 侧的对齐验证全部落在GitEngine这个单例对象上,源码位于 GitEngine.kt。它的设计值得先看清楚:

2.1 与 Electron VaultGitService 的镜像关系

Electron 侧实现是VaultGitService(next/electron/main/vaultGitService.ts),对外暴露三类接口:

  • status()/history()/diff():只读查询;
  • action(input):以{ action, path, message, remote, strategy }为参数的统一动作入口,内部 switch 到init、configure-remote、commit、fetch、pull、push、discard、abort-merge、resolve-conflict、continue-operation;
  • automaticCheckpoint(message):检查点专用流程。

Compose 侧GitEngine则以顶层函数形式提供status、init、commit、history、diff、fileDiffs、setRemote、fetch、pull、push、discard、abortMerge、resolveConflict、continueOperation、automaticCheckpoint等方法。两条实现的命令参数几乎一致——例如status都用git status --porcelain=v1 -z --branch --untracked-files=all,fetch 都用git fetch --prune origin,push 都用git push -u origin HEAD,pull 都用git pull --no-rebase origin。这保证了「同一行为、同一命令、同一预期」的可比性。

2.2 数据模型与关键常量

GitEngine定义了清晰的数据结构(见 GitEngine.kt 顶部):

  • GitStatus:available、repository、version、branch、remote、ahead、behind、changes、conflicts、merging、operation(取值为merge/rebase/none);
  • GitChange:path、originalPath、status、conflict(conflict由UU/AA/DD等状态判定);
  • GitCommitInfo:hash、shortHash、author、date、message;
  • GitFileDiff:path、originalPath、status、before、after、preview(Text/Binary/TooLarge三态);
  • GitActionResult:统一的success+message返回。

关键常量定义了行为边界:普通命令超时TIMEOUT_MS = 30_000L,远端操作超时REMOTE_TIMEOUT_MS = 120_000L,diff 预览最大512 * 1024字节,提交说明截断上限300字符,陈旧index.lock判定阈值5分钟。defaultGitignore默认为.DS_Store、.idea/、.vscode/、*.tmp、.migrated-from-db。

2.3 执行器:进程级隔离与超时控制

GitEngine.run是命令执行的统一出口(GitEngine.kt),它的职责包括:

  • 设置GIT_TERMINAL_PROMPT=0禁止交互式提示,设置GIT_OPTIONAL_LOCKS=0;
  • isolateConfig = true时通过GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM指向空设备,避免用户全局 Git 配置影响测试结果(这也是所有对齐测试统一传isolateConfig = true的原因);
  • token 非空时写入临时GIT_ASKPASS脚本,通过环境变量MOOTOOL_COMPOSE_GIT_TOKEN向脚本传值;
  • 用两个线程异步排空 stdout/stderr,防止管道写满造成死锁;超时后destroyForcibly()并返回退出码 124。

locked(root)则用ConcurrentHashMap<Path, Any>配合synchronized保证同一 Vault 目录下所有 Git 操作串行执行——这是并发提交对齐用例的直接实现基础。

三、逐项解析:登记表中的核心行为点

下面按登记表的分组,逐项说明行为语义、Electron 测试与 Compose 验证的对应关系,以及源码依据。

3.1 初始化、提交与历史(init / commit / diff / history)

登记表第一行init-commit-diff-history对齐的是 Electron「initializes, reports changes, commits, shows history, and builds a diff」这条完整链路。两端测试都覆盖了以下断言:

  1. 未初始化时status().repository === false;
  2. init后写入默认.gitignore(必须包含.DS_Store),工作区出现.DS_Store也不进入变更列表(Compose 对应GitEngineTest.initWritesGitignoreAndIgnoresDsStore);
  3. 未跟踪文件在status.changes中以??呈现;
  4. commit后history首条即为本次提交,author 非空;
  5. diff({ commit })返回该提交的文件级前后内容,diff({ path })返回工作区相对 HEAD 的前后内容。

Compose 侧对应的mirrorsElectronVaultGitInitCommitDiffHistory(GitEngineTest.kt)把这条链路完整重演了一遍:提交sample.json两版内容,验证fileDiffs的before/after分别等于旧值与新值,并且新文件的before为空、preview为Text。

实现层面,GitEngine.commit内部会先把提交说明trim().take(300)截断,空说明返回失败,merge/rebase 进行中直接拒绝,无变更返回No changes to commit;随后执行git add --all与git commit -m。提交命令通过identityArgs携带-c user.name、-c user.email、-c commit.gpgsign=false,与 Electron 的ensureIdentity()逻辑对应。

3.2 提交说明 300 字符截断

commit-message-300对应 Electron 的说明截断行为。Compose 的truncatesCommitMessageToThreeHundredCharacters直接传入 320 个m提交,再断言history返回的 message 恰好为 300 字符。实现依据是commitLocked中的message.trim().take(300)(GitEngine.kt)。

3.3 冲突处理:pull、merge、rebase 的完整闭环

登记表围绕冲突操作列出多条:pull-merge-conflict-resolve、rebase-continue、merge-abort/continue。

分叉 pull 产生合并冲突:pullLeavesMergeConflictWhenHistoriesDiverge构造 bare 仓库、upstream 与 local 三方环境:upstream 提交Base后 push,local clone,随后两边各自修改同一文件并提交,此时GitEngine.pull必然失败;status显示operation == "merge"、merging == true、conflicts >= 1。接下来用resolveConflict(root, path, "theirs")解决冲突,status.conflicts归零,continueOperation成功,最终operation == "none"。Electron 侧对应的「resolves and aborts real merge conflicts」测试路径完全一致(vaultGitService.integration.test.ts)。

merge 的 abort 与 continue:resolvesAndAbortsMergeConflicts验证解决冲突后可abortMerge干净退出;continuesMergeAfterConflictResolved验证解决后continueOperation能正常收尾。实现上abortMerge先试git merge --abort,失败再试git rebase --abort(GitEngine.kt);continueOperation在operation == "rebase"时执行rebase --continue(附带-c core.editor=true),否则执行commit --no-edit,但冲突未清一律拒绝(GitEngine.kt)。

rebase 的 continue:continuesRebaseAfterConflictResolved复现了 rebase 冲突→resolveConflict("theirs")→continueOperation的流程,验证operation从rebase回到none。这与 Electron 的「detects and continues a rebase after conflicts are resolved」用例逐条对应。

resolve-conflict 的两种策略:resolveConflict只接受ours/theirs两个策略,内部执行git checkout --ours/--theirs -- <path>后git add;对未处于冲突状态的文件返回Git path is not conflicted(GitEngine.kt)。

3.4 操作守卫:冲突期禁止 commit / pull / push / continue

登记表中出现了一组形态相同的守卫行为,这是「操作顺序正确性」的对齐重点:

caseId行为Compose 验证类
commit-during-mergemerge 中禁止 commitrejectsCommitWhileMergeInProgress
commit-unresolved-conflicts未解决冲突禁止 commitGitCommitGuardTest.commitBlockedWhileMergeConflictsRemain
pull-during-mergemerge 中禁止 pullGitPullGuardTest.mergeInProgressRejectedBeforeMissingRemoteMessage
pull-unresolved-conflicts未解决冲突禁止 pullGitPullGuardTest.pullBlockedWhileMergeConflictsRemain
continue-unresolved-conflicts冲突未解决禁止 continueGitContinueGuardTest/GitRebaseGuardTest
pull-during-rebaserebase 中禁止 pullGitRebaseGuardTest.pullBlockedWhileRebaseInProgress
commit-during-rebase-conflictsrebase 冲突未清禁止 commitGitRebaseGuardTest.commitBlockedWhileRebaseConflictsRemain
push-unresolved-conflicts冲突未清禁止 pushGitPushGuardTest.pushBlockedWhileMergeConflictsRemain
abort-rebase-unresolvedrebase 冲突态允许 abortGitRebaseGuardTest.abortEndsRebaseWithUnresolvedConflicts

从GitEngine.pull的实现可以看到守卫顺序(GitEngine.kt):先检查repository,再检查merging(返回Finish or abort the current merge/rebase before pulling),随后检查conflicts > 0(返回Resolve all conflicts before pulling),最后才检查 remote。GitPullGuardTest.mergeInProgressRejectedBeforeMissingRemoteMessage专门锁定「merge 进行中时,错误提示必须命中 merge 语义且不能出现 remote 字样」,即守卫优先级高于远端校验。

push的守卫链与之类似(GitEngine.kt):merge/rebase 进行中或存在冲突都直接拒绝。这些 guard 测试的 fixture 模式相同:构造base/other/base-branch三方修改同一文件,git merge other必失败制造冲突态,然后调用目标动作断言失败文案。

3.5 自动检查点(automatic checkpoint)

automatic-checkpoint-init/push与automatic-checkpoint-conflict-skip两条,对应 ElectronautomaticCheckpoint()与 ComposeautomaticCheckpoint()(GitEngine.kt)的完整语义:

  1. 首次使用自动初始化:目录还不是仓库时,automaticCheckpoint内部调用init,并以「Initial MooTool Vault setup」作为首个提交说明。对应用例automaticCheckpointInitializesRepositoryOnFirstUse与 Electron「initializes a new Vault when creating its first automatic checkpoint」;
  2. 配置远端后自动 push:有未提交变更或ahead > 0且remote已配置时,检查点提交完成后自动git push -u origin HEAD。对应用例automaticCheckpointPushesWhenRemoteConfigured会去 bare 仓库执行git log --all断言提交确实到达远端,且status().ahead == 0;
  3. 冲突期跳过:merging || conflicts > 0时返回「Merge/rebase in progress; checkpoint skipped」,并保持 HEAD 不变。resolvesAndAbortsMergeConflicts中断言冲突期间的检查点调用后 history 首个 hash 与冲突前一致。

这个流程保证了「随手记/JSON 笔记自动保存到 Git」不会在合并中途制造脏提交——这正是登记表标注对齐 ElectronautomaticCheckpoint的核心价值。

3.6 安全与路径规则

  • rejects-unsafe-paths:fileDiffsMatchesElectronPathRules验证../outside抛出IllegalArgumentException("Invalid Git path"),而valid..name.json(合法但无变更)返回空列表。实现依据是normalizeGitPath(GitEngine.kt):拒绝绝对路径、含\u0000、长度超 512 的路径,并拒绝任意一段为./../空串的相对路径;
  • parent-repo-not-adopted:rejectsParentRepositoryUntilVaultRootIsInitialized构造「父目录已 Git init、子目录为 Vault」的嵌套场景,断言status(vault)先报告repository == false,init(vault)之后才成为独立仓库,且父仓库的git status能继续看到outside-unrelated.txt——即嵌套 Vault 不会被父仓库的git add --all吞并。这一点由status()中rev-parse --show-toplevel与root.toRealPath()的比对逻辑保障(GitEngine.kt);
  • unicode-paths:unicodePathsWorkForStatusDiffDiscardAndRename用中文文件名验证 status/diff/discard/rename 全链路,重命名后status.changes需同时给出path与originalPath。

3.7 index.lock 恢复与并发提交

这是GitEngine.run最精细的一段逻辑(GitEngine.kt),对应登记表三条用例:

  • stale-index-lock:命令因unable to create ... index.lock ... file exists失败后,先sleep(250ms)重试一次;仍失败则走quarantineStaleIndexLock。隔离流程为:锁文件存在且最后修改时间距今超过 5 分钟(STALE_INDEX_LOCK_MS)→ 用lsof -t确认无进程占用 → 再等100ms确认 mtime 稳定 → 把锁文件改名为index.lock.mootool-stale-<时间戳>-<pid>→ 重试命令 →finally删除隔离文件。对应用例repairsStaleIndexLockAndRetriesCommit在锁文件 mtime 设为 10 分钟前的前提下,断言提交成功、锁文件消失、.git下没有残留index.lock.mootool-stale-*;
  • recent-index-lock:锁文件刚创建(不足 5 分钟)时不做隔离,commit返回失败且文案包含index.lock。对应用例recentIndexLockBlocksCommit;
  • held-index-lock:锁文件虽陈旧但被本进程以文件句柄占用时,lsof -t检测到占用,不做隔离,提交保持失败。对应用例doesNotQuarantineIndexLockHeldOpenByThisProcess(该用例依赖平台lsof,macOS 为/usr/sbin/lsof,Linux 为lsof,Electron 侧同样用skipIf(!lockInspectorAvailable)做条件跳过)。

并发提交序列化:serializesConcurrentCommitsForSameVaultRoot用双线程同时发起两条不同 message 的提交,断言两个结果都成功,但git log --pretty=%s -- concurrent.txt只能看到一条 message——说明后到的提交看到的是已经干净的工作区,不会产生叠加提交。Electron 侧同名用例使用「两个独立VaultGitService实例并行Promise.all」,验证的是repositoryOperationQueues按目录的队列化;Compose 侧locks的synchronized达到同一效果。

3.8 fetch 与 push 的远端语义

登记表后半段集中对齐远端操作:

  • push-non-ff:pushFailsWhenRemoteIsAheadWithoutPull让两个 clone 先后 push 到同一 bare 仓库,第二个 push 必须失败,且文案匹配rejected/non-fast-forward/fetch first之一。这对应 Electron 侧「rejects non-fast-forward push」语义;
  • fetch-prune-behind:fetchUpdatesBehindWithoutPullingWorkingTree验证git fetch --prune origin只更新远端追踪引用(status.behind从 0 变为 ≥1),但工作区文件v2.txt依然不存在,随后pull才真正落盘。对应 Electron「fetch updates behind without pulling the working tree」;
  • fetch-prune-stale-ref:fetchPruneRemovesStaleRemoteTrackingBranch先让客户端 fetch 到origin/feature,远端删除该分支后再次 fetch,git branch -r不再出现origin/feature——验证 prune 清理陈旧追踪分支;
  • fetch-diverged-ahead-behind:fetchReportsAheadAndBehindWhenBranchesDiverge验证分叉后 fetch 能同时更新 ahead 与 behind,且不触碰工作区;
  • fetch 未配置远端:fetchFailsWhenRemoteIsNotConfigured断言失败文案包含remote;
  • configure-remote-remove:configureRemoteRemoveClearsOriginFromStatus与configureRemoteRemoveWhenAlreadyRemovedIsIdempotent验证空 URL 删除origin的幂等语义:首次删除返回成功且status.remote == "",二次删除返回「Remote is already removed」。Electron 侧对应「removes a configured remote when an empty URL is saved」;
  • 远端 URL 校验:rejectsUnsafeGitRemotes验证javascript:alert(1)、/etc/passwd这类 URL 被拒绝(normalizeGitRemote只放行https?://、ssh://、git://、git@、file://前缀,见 GitEngine.kt),合法file://URL 可配置,空字符串可移除。

3.9 UI 呈现层的对齐

登记表末行与底注说明 UI 层也有明确的对照物:

  • git-ui-abort-copy:merge/rebase 中止与确认文案、continue/counts/resolve/pull 的显隐与 busy 状态,由GitOperationPresentationTest锁定(参见 DIFF-520);
  • merge-conflict-ui-hint:未解决冲突的说明文案与变更行冲突<em>高亮,由GitMergeConflictPresentationTest与VaultGitDialog锁定;
  • 底注补充了两条 UI 对照:VaultGitDialog冲突行「使用本地/远端」的 Compose Tab 帧编号 142(见 DIFF-437),以及顶栏分支图标与操作钮的 Lucide 图标对齐(见 DIFF-540)。

四、跨实现对齐的验证方法论

这份登记表本身就是一个值得借鉴的工程实践:用一张 caseId 表把两个实现的行为一一钉死,再用同名/同语义测试双向锁定。要点有三:

  1. 统一 fixture 模式:冲突类测试几乎都遵循「base 提交 →checkout -b other修改提交 → 切回 base 修改提交 →merge/rebase制造冲突」的固定剧本,两侧代码逐行对应,降低了环境差异带来的噪声;
  2. 双保险验证:引擎层断言(GitEngineTest/GitEngine*GuardTest)与呈现层断言(Git*PresentationTest)分离,行为正确性与 UI 文案/显隐分开验证;
  3. 可追溯的差异记录:每个行为点挂在 DIFF 文档上(如 DIFF-443、DIFF-475),DIFF 文档写明背景、行为差异、修复方案与./gradlew :composeApp:desktopTest --offline的通过统计,形成从需求到测试再到验证的完整闭环。

五、如何在当前仓库中验证这些行为

所有 Compose 侧测试都位于 desktopTest 目录下,主要文件包括:

  • GitEngineTest.kt:生命周期、冲突、fetch/push、锁恢复、并发、Unicode、路径规则的引擎级测试;
  • GitCommitGuardTest.kt/GitPullGuardTest.kt/GitPushGuardTest.kt/GitRebaseGuardTest.kt:各类冲突态守卫测试;
  • GitMergeConflictPresentationTest.kt/GitOperationPresentationTest.kt:UI 呈现测试。

Electron 侧的源测试是 next/src/shared/vaultGitService.integration.test.ts,实现是 next/electron/main/vaultGitService.ts。运行验证的方式:

# 在 next-compose 目录下运行 Compose 桌面测试(需要本机安装 git CLI) ./gradlew :composeApp:desktopTest --offline

需要注意的适用前提:冲突/锁/远端相关用例通过assumeGit()或Assume.assumeTrue在未安装 git 时自动跳过;held-index-lock用例额外要求lsof可用,Electron 侧同样按平台条件跳过,不属于失败。

六、小结

Vault Git 是 MooTool 把「随手记、JSON 笔记等本地数据放进一个可版本化的目录」的关键基础设施。本文基于 electron-next-vaultGitService-vitest.md 这份对照登记表,梳理了从init/commit/diff基础链路,到 merge/rebase 冲突闭环、操作守卫、自动检查点、index.lock恢复、并发序列化、fetch/push 远端语义,再到 UI 呈现的完整行为清单,并用 GitEngine.kt 的实现逐项印证。对正在做跨实现功能迁移的团队来说,这套「行为登记表 + 同名测试对 + DIFF 追溯」的方法本身,就比任何一条具体行为都更有长期价值。

  • 桌面应用
  • 开发工具

【免费下载链接】MooTool

Handy tool set for developers

项目地址:https://gitcode.com/gh_mirrors/mo/MooTool
点击查看免费下载
上一篇:DeepTutor v1.3.5 可靠性专项解析:本地启动、RAG 检索与本地 Embedding 兼容性改进
下一篇:如何用Spek音频频谱分析器快速检测音频质量:免费开源工具终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询