用于跨上下文留存问题处理经验,避免重复踩坑。新条目追加在末尾,同 Issue 只维护一处。
每条摘要包含:表因 / 根因 / 处理方式 / 后续防范 / 同类问题影响。
- 表因:
pnpm run check-types在src/engine/model/index.ts报大量TS1127 Invalid character/TS1109 Expression expected,指向一段纯中文注释行。 - 根因:注释文本「
INDEX_*/工作区/冲突」中的*/序列被 TypeScript 解析为块注释终止符,导致其后中文文本暴露为代码,触发语法错误。 - 处理方式:改写注释,移除
*/序列(INDEX_*/工作区→INDEX 系列、工作区)。 - 后续防范:在任何
/* ... */块注释内引用含*/的内容(如gitDecoration.*、正则*/、glob)时,必须转义或改写;可用grep -rn '\*/' src/ | grep -vE '\*/\s*$'扫描提前闭合。 - 同类问题影响:所有含中文技术注释的 TS 文件,尤以注释内出现路径/枚举/正则片段时高发。
- 表因:
pnpm install输出[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: esbuild, @vscode/vsce-sign, keytar,导致 esbuild 原生二进制未安装,后续构建可能失败;且package.json的pnpm.onlyBuiltDependencies字段被忽略并告警。 - 根因:pnpm 10/11 出于供应链安全默认拦截依赖 postinstall;同时 pnpm 11.9 将
onlyBuiltDependencies等设置移出 package.json,新位置为pnpm-workspace.yaml(本版本使用allowBuilds:审批格式,由 pnpm 自动生成脚手架)。 - 处理方式:删除 package.json 的
pnpm字段;在pnpm-workspace.yaml写入allowBuilds: { esbuild: true, '@vscode/vsce-sign': true, keytar: true }后重新pnpm install,三个 postinstall 正常执行。 - 后续防范:pnpm 项目一律在
pnpm-workspace.yaml管理构建脚本审批;新增含原生二进制的依赖时,需在此文件追加放行;CI 首次pnpm install后确认无ERR_PNPM_IGNORED_BUILDS。 - 同类问题影响:所有 pnpm 11 工程;凡依赖 esbuild / keytar / @vscode/vsce-sign / prebuild-install 类原生模块的扩展。
- 表因:CI
Lint & Buildjob 10s 内失败,日志Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:sqlite,并告警This version of pnpm requires at least Node.js v22.13。本地不暴露(本地用 Node 24)。 - 根因:pnpm 11.9 内部使用 Node 22.13+ 才有的
node:sqlite内置模块;CI 工作流配置node-version: 20,pnpm 启动即崩。 - 处理方式:CI 所有 job 的
setup-node由node-version: 20升至node-version: 22。 - 后续防范:pnpm ≥ 11 工程的 Node 基线须 ≥ 22.13;
engines.node/CI/本地三者对齐(建议 22 LTS 或 24);升级 pnpm 前查其 Node 版本要求(https://r.pnpm.io/comp)。 - 同类问题影响:所有 pnpm 11+ 的 CI/本地环境;node:sqlite 依赖的其他工具链。
- 表因:CI
Testjob 集成测试报Activating extension 'threefish-ai.hyper-git' failed: Cannot find module '.../dist/extension.js';本地却通过。 - 根因:
testjob 仅跑test:unit+test:integration,未执行node esbuild.js构建dist/extension.js;test-electron 启动真实 VS Code 加载扩展(main: ./dist/extension.js)时找不到入口。本地因先前pnpm run package残留 dist/ 而误判通过。 - 处理方式:
testjob 在pnpm install后、测试前增加node esbuild.js(或pnpm run compile)构建 dist/。 - 后续防范:凡含
@vscode/test-electron集成测试的 CI job,必须在测试前显式构建扩展产物;本地验证集成测试后清理 dist/ 以暴露该依赖;.gitignore排除 dist/ 时注意 CI 需重建。 - 同类问题影响:所有 VS Code 扩展的 test-electron CI job;本地"能跑"但 CI 失败的构建产物缺失类问题。
- 表因:本地
pnpm run lint在 ~70s 后FATAL ERROR: ... JavaScript heap out of memory(4GB 耗尽);M0 时却正常。 - 根因:
@vscode/test-electron首次运行将完整 VS Code(约 260MB、海量 JS)下载到.vscode-test/;ESLint 9 flat config 默认仅忽略node_modules,不忽略.vscode-test/,于是 eslint 遍历其下成千上万 JS 文件导致 OOM。M0 lint 通过是因为当时.vscode-test/尚未生成。 - 处理方式:在
eslint.config.mjs的ignores增加.vscode-test/**。 - 后续防范:含 test-electron 的扩展,eslint ignores 必须含
.vscode-test/**(及out/**、dist/**、*.vsix);CI 因不缓存该目录可能不暴露,但本地必现——本地与 CI 环境差异需警惕。 - 同类问题影响:所有跑过 test-electron 的本地环境的 eslint/其他静态分析工具。
- 表因:调用
Repository.add(['README.md'])(相对路径)无效或误加文件;CommitService 初期也曾困惑路径语义。 - 根因:
extensions/git/src/api/api1.ts的add(paths)实现为paths.map(p => Uri.file(p))——Uri.file()要求绝对路径;相对路径会被包装成畸形 Uri,内部path.relative(root, ...)计算错误。revert/clean/restore同理。 - 处理方式:CommitService 始终传
ChangeItem.uri.fsPath(绝对)。 - 后续防范:消费 vscode.git 公开 API 的路径类方法(add/revert/clean/restore)一律传绝对 fsPath;已加集成测试
tests/suite/commit-flow.test.js守护。 - 同类问题影响:所有消费 vscode.git API 做 stage/revert 的扩展;git CLI 本身接受相对路径,但公开 API 层不接受,二者语义差异易踩。
- 表因:README 指引「从 Releases 下载
.vsix→Extensions: Install from VSIX」,但 rc.1/rc.2 的 GitHub Release 页面无任何.vsix资产,用户无法手动安装。 - 根因:
ci.yml的packagejob 只把.vsix当作 Actions artifact(90 天即逝、非公开下载)上传,publishjob 仅将其发往 VS Code Marketplace / OpenVSX;全流程无任何 step 创建 GitHub Release 或向其上传资产(rc.1/rc.2 的 Release 实为手工gh release create,本就不含.vsix)。 - 处理方式:新增独立
github-releasejob(softprops/action-gh-release@v2),needs: package复用 vsix artifact,对v*tag 自动建 Release 并files: '*.vsix'上传;*rc*自动prerelease;fail_on_unmatched_files: true防空资产。 - 后续防范:该 job 与市场
publish解耦(不needs: publish、不挂environment: production),保证「Release 带.vsix」不被市场审批门/密钥缺失阻塞;「仅出 Release、暂不发市场」时不审批 production 即可,无需改 publish job;最小权限仅本 job 提权contents: write。 - 同类问题影响:所有「CI 只上传 artifact + 发市场、却在 README 承诺 Release 手动下载」的 VS Code 扩展;artifact ≠ Release 资产,二者可见性/留存期差异易被忽视。
- 表因:用户截图反馈 Branches 视图中一组功能/工作分支无法框选多个、无法批量删除。
- 根因:
hyperGit.branches经vscode.window.registerTreeDataProvider注册——该 API 不支持canSelectMany,故视图天然单选;所有分支命令处理器亦只接收单个BranchNode。多选能力(canSelectMany: true)仅createTreeView的TreeViewOptions支持。 - 处理方式:改用
createTreeView('hyperGit.branches', { treeDataProvider, canSelectMany: true })(句柄入 subscriptions)。批量命令处理器签名扩展为(clickedNode, selectedNodes[])——VS Code 多选树的view/item/context命令第 2 实参即完整选区数组。新增纯逻辑engine/ref/selection.collectBranchRefs(谓词过滤 + shortName 去重 + 「点击在选区之外则以点击项为准」)与engine/ref/cleanup.partitionByMerged/formatBranchDeleteConfirm,使branchDelete/tagDelete/copyBranchRef/toggleFavorite批量化(删除仅一次git branch --merged分类、汇总成功/失败、末尾单次刷新)。package.json对仅单目标命令(检出/合并/变基/重命名/比较等)追加&& !listMultiSelection在多选时隐藏。 - 后续防范:① 需要承载
canSelectMany等TreeViewOptions能力的 TreeView,一律用createTreeView而非registerTreeDataProvider(本仓hyperGit.branches即此);.badge则TreeView与WebviewView均支持——hyperGit.changes视图移除后(其活动 Changelist 与 Commit 视图重复),未提交数角标已迁至 CommitWebviewView.badge,注意WebviewView仅在resolveWebviewView后可置 badge,需pendingBadge兜底首帧未 resolve 的时序。② 多选命令正确性只依赖处理器读取实参(clickedNode+selectedNodes[]),不得依赖listMultiSelection上下文键——其对自定义贡献视图的可靠性无法确证,仅作菜单整洁的视觉优化;单目标命令因只读clickedNode即便该键失效仍安全。③ 「右键点击选区之外」须以点击项为准(手势目标优先),由归一化助手统一兜底。 - 同类问题影响:所有以
registerTreeDataProvider注册却后续需要多选/角标的自定义 TreeView;以及误把单目标命令在多选下直接作用于「点击项」造成的隐性误操作。
- 表因:用户截图反馈 LOG 的 All 范围下,一批本应随分支删除而消失的提交仍以游离泳道残留;运行「清理已删远程分支」(#44,
git fetch --prune)后依旧存在。 - 根因:
engine/log/log-query.ts的buildLogArgs对all/checkpointer范围下git log --all。--all遍历refs/下全部引用,不止 heads/remotes/tags——还包括宿主工具(如 Conductor)注入的refs/conductor-checkpoints/*(会话快照)、refs/conductor-archive-heads/*(已删/被取代分支头的归档)。这些归档头让真实的游离提交(被 amend/rebase 取代、或分支删除后仅靠归档存活者)仍可达,画成游离泳道。而既有的客户端CHECKPOINT_SUBJECT_RE=/^checkpoint:/i过滤只能拦住 checkpoint 元数据提交本身,拦不住作为其祖先的游离业务提交——故泄漏。git fetch --prune仅清理refs/remotes/*,对上述非远端跟踪引用完全无效,这正是「prune 后依旧存在」的根因。实证:本仓--all取 241 提交、--branches --tags --remotes仅 70;refs 命名空间 135 conductor-checkpoints + 17 conductor-archive-heads,远多于 3 heads/3 remotes/2 tags。 - 处理方式:
all范围由--all改为--branches --tags --remotes(仅三大标准命名空间,排除一切工具注入的内部引用),根治游离泳道;checkpointer范围保留--all——该 Tab 的职责即「原始完整视图,含内部 checkpoint 快照」,需触达refs/conductor-checkpoints/*。客户端keepCheckpoint过滤作为双保险保留。更新tests/unit/log-query.test.ts断言(all含三件套、不含--all;checkpointer含--all、不叠三件套)作回归护栏。 - 后续防范:① 「全分支视图」语义应映射到
--branches --tags --remotes而非--all——--all是「全部引用」而非「全部分支」,二者差异恰是工具注入引用的污染面。② 客户端按提交 message 正则过滤是漏的抽象(拦不住作为祖先被带入的游离提交);根治应在 ref 选取层(服务端参数)而非 subject 过滤层。③ 诊断 git 引用类问题时务必先git for-each-ref列出全部命名空间——本案最初误判为「远端已删、本地未 prune」(#44 与一度推进的 prune-on-fetch 方案均为此误判),直到列出 refs 才发现真凶是 conductor-* 引用;「prune 无效」本身就是关键反证,应据其反向收敛而非强行加 prune。④ 修正「错漏逻辑」前先用git log --allvs--branches --tags --remotes的差集实证根因,避免再次基于关键字匹配机械式修改。 - 同类问题影响:所有在带「工具注入内部引用」环境(IDE/Agent checkpoint、
refs/stash、refs/replace/*、refs/notes/*等)下展示git log --all图的 Git GUI;凡把「范围 = 引用集合」与「范围 = message 过滤」混为一谈的实现均可能漏过游离提交。
- 表因:用户截图反馈 Hyper Git 活动栏图标的未提交变更数角标更新不及时——有时已有变更却不显示角标,有时文件已提交/撤销角标仍不消失。
- 根因:角标承载于 Commit
WebviewView.badge(#8 移除 Changes 视图后迁入)。命中 VS Code 已知限制:webview 角标在resolveWebviewView(即用户至少打开过一次该视图)之前无法显示(microsoft/vscode#164974、#146330);源码印证commit-webview.ts未 resolve 时updateBadge仅写入pendingBadge、永不上屏,WebviewView.onDidDispose亦仅在用户显式取消勾选视图时触发。故只要面板未打开/隐藏(用户在编辑器或其他活动容器工作),新变更无法点亮、提交/撤销后无法清除。#8 的「后续防范」已预警此pendingBadge首帧时序隐患,本 Issue 即其兑现。TreeView 无此限制——createTreeView可在 activate 强制实例化视图对象,.badge无论可见与否都可靠聚合到容器图标(容器角标 = 容器内各视图 badge 之和)。 - 处理方式:新增隐藏承载视图
hyperGit.changesBadge(package.jsonwhen:false,永不渲染,复用EmptyTreeProvider),经createTreeView于 activate 即实例化并置.badge;角标承载由 Commit WebviewView 整体迁出(移除updateBadge/pendingBadge死代码,杜绝容器求和 2× 计数)。新增engine/scm-mapping/change-count.ts(toRelKey/countUniqueChanges)作为去重单一事实源,GitRepositoryService.getChangeCount()与getChanges()共用;角标走独立 40ms 微防抖快路径(与 150ms 重刷新解耦、合并事件风暴、释放期清理定时器),首帧同步置初值。 - 后续防范:① 需要「面板未打开也持续显示」的活动栏计数角标,必须承载于
createTreeView建立的 TreeView(可用when:false隐藏视图专职承载),不可依赖WebviewView.badge——其 resolve 前不显示是 VS Code 已知限制而非本仓 bug;这与 #8「.badgeTreeView/WebviewView 均支持」并行:「支持置 badge」≠「未 resolve 也上屏」。② 容器角标为各视图 badge 之和,全仓须保证唯一承载者,迁移承载时务必删除旧承载,否则重复计数。③ 计数与文件列表去重须共用单一事实源(toRelKey),避免「列表条目数 ≠ 角标数」漂移。④when:false承载视图的实机角标渲染需在 EDH 回归确认(跨 VS Code 版本聚合行为),失败则回退为visibility:collapsed的空视图。 - 同类问题影响:所有以
WebviewView.badge承载活动栏/视图角标的自定义视图容器扩展;凡角标承载迁移未清理旧承载导致的重复计数;以及把「支持 badge 属性」误判为「隐藏态也能显示 badge」的时序类误区。
- 表因:以 rc tag(
v0.0.10-rc.1)触发发布时,publishjob 的「发布到 VS Code Marketplace」步骤报错Cannot use '--pre-release' flag with a package that was not packaged as pre-release. Please package it using the '--pre-release' flag and publish again.,job 失败;其后的 OpenVSX 步骤(虽continue-on-error)因前序步骤失败被 skipped,致三渠道仅github-release成功、Marketplace/OpenVSX 均未发出。 - 根因:
packagejob 以vsce package --no-yarn(不带--pre-release)打出「正式版」VSIX 作为 artifact;publishjob 却对 rc tag 用vsce publish --packagePath *.vsix --pre-release。vsce 强约束——以--pre-release发布的 VSIX 必须在打包时即带--pre-release(预发布标志写入 VSIX manifest),否则拒绝发布。打包端与发布端的--pre-release判定不对称即致此错。历史 rc(0.0.9-rc.*)从未真正发到市场(Marketplace 步骤因缺VSCE_PAT/变量被跳过),故该缺陷此前从未被触发暴露。 - 处理方式:
packagejob 打包步骤改为与 publish/OpenVSX 同款PRE_FLAG判定——GITHUB_REF_NAME含rc时追加--pre-release,使同一枚「预发布 VSIX」贯穿github-release/ Marketplace / OpenVSX 三渠道(单一产物、零重复打包)。正式版 tag(无rc)与分支/PR CI 仍打普通 VSIX,行为不变。 - 后续防范:① VS Code 预发布模型下,打包与发布两端的
--pre-release必须成对出现;凡「先 package 成 artifact、后 publish 复用同一枚 VSIX」的流水线,预发布判定要在 package 端就落地,不能只在 publish 端加 flag。② 预发布版本号仍须纯major.minor.patch(Marketplace 不接受-rc.Nsemver 后缀),预发布语义由--pre-release标志 + tag 命名承载;0.0.10作预发布后正式版须用更高版本(如0.0.11),同一版本号不可既预发布又正式发布。③ OpenVSX 步骤的continue-on-error只隔离其自身失败——前序 Marketplace 步骤失败仍会使其 skipped;排障时勿因「OpenVSX 未报错」误判其已发布,须查其步骤实际状态与日志。④ Marketplace 发布链路的双凭证不可混淆:VSCE_PAT(Azure DevOps PAT,scope Marketplace→Manage)与OVSX_PAT(open-vsx.org token)是不同服务的两个不同 token,且 Marketplace 发布还受仓库变量ENABLE_MARKETPLACE_PUBLISH门控。 - 同类问题影响:所有「package 出 artifact → publish 复用」且需发布预发布通道的 VS Code 扩展 CI;凡打包端与发布端 flag 判定不对称(
--pre-release、平台化--target等同理)的流水线均会踩。
- 表因:用户反馈 Worktrees 视图展开后无法继续缩小(截图中仍占大片空白),要求「所有视图可拖到任意高度、取消最小高度限制」。
- 根因:侧边栏每个视图面板(
Pane)的最小体高由 VS Code 核心硬编码 = 120px(竖直方向;构造函数this._minimumBodySize = ... orientation === HORIZONTAL ? 200 : 120,见src/vs/base/browser/ui/splitview/paneview.ts),加 22px 标题栏,展开态最小 ≈ 142px,该值经minimumSize直接驱动 SplitView 拖拽分隔条下限。WebviewViewPane extends ViewPane未覆写minimumBodySize,故本扩展 2 个 webview(Commit/Graph)与 4 个 tree(Branches/Stash/Shelf/Worktrees)视图共用同一 142px 下限。允许扩展为活动栏容器内视图指定固定/最小/最大高度的官方特性请求 microsoft/vscode#123715 已被关闭为 not planned / out-of-scope,从未新增任何 API 或package.json贡献点。扩展运行于独立进程,拿不到工作台面板对象,minimumBodySizesetter 仅核心ViewPaneContainer调用;注入 CSS 亦无效(.pane-body{min-height:0}改不动 JS 层用于夹取拖拽下限的minimumSize)。 - 处理方式:该限制无法经扩展解除,采用受支持的折中缓解——在
package.jsoncontributes.views调初始布局默认值:次要视图 Stash/Shelf 设visibility:"collapsed"(默认仅 22px 标题栏、点击即展开),Worktrees 保持visible(仅以initialSize权重收窄),全部视图加initialSize(Commit 3 / Graph 3 / Branches 2 / 其余 1,类 CSS flex 的高度权重)。两字段经src/vs/workbench/api/browser/viewsExtensionPoint.ts的viewDescriptorschema 确认可用;initialSize仅当「同一扩展同时拥有视图与视图容器」时生效(本扩展拥有hyper-git容器与全部视图,条件满足)。注:此处枚举系 v0.0.12 时点缓解快照——自 #16 起默认布局改为 Stash/Shelfhidden、Commit/Worktreescollapsed、顺序重排为 Commit·Branches·Graph·Worktrees,当前默认布局以 #16 为准。 - 后续防范:① VS Code 侧边栏视图存在约 142px 硬性最小展开高度,无法经扩展降低——遇「任意高度 / 无最小高度」类诉求应直接引 #123715(not planned)说明平台边界,勿承诺实现;判断「webview 内容 CSS
min-height」与「外层面板最小高度」是两回事。②visibility/initialSize只影响初始状态(「用户手动折叠/移动/隐藏过后即不再生效」)——老用户需命令面板「View: Reset View Locations」或右键容器图标「Reset Location」才采用新默认;实机验证须用干净 profile 或先重置以规避持久化布局。③ 想让展开视图更紧凑,只能靠「减少常驻视图数(默认折叠)+ 权重」,而非解除下限。 - 同类问题影响:所有向活动栏/侧边栏容器贡献 TreeView/WebviewView 且希望自定义或取消视图高度的扩展;凡把「webview 内容
min-heightCSS」误认为能改变外层面板最小高度的实现。
- 表因:用户要求将 Hyper Git 视图容器从默认的活动栏(Activity Bar / Primary Side Bar)迁移到底部面板(Panel),并希望排在 Terminal 页签之后。
- 根因:
contributes.viewsContainers的activitybar与panel是 dock 选择键,VS Code 通过容器 id 关联viewsContainers与views,容器挂在哪个 dock 与 views 归属、API 调用、图标规范完全解耦——故迁移仅需将package.json中"activitybar"改为"panel",容器 idhyper-git与全部 7 个视图、.ts源码、media/hyper-git-icon.svg(24×24 单色currentColor)均无需改动(engines.vscode: ^1.85.0≫ panel 容器所需 1.56+)。 - 处理方式:单行改动
viewsContainers.activitybar→viewsContainers.panel,并 bump0.0.12→0.0.13。用户经评估后明确接受 trade-off(见下),不引入双容器或状态栏计数器。 - 后续防范:
- Panel 容器相对内置页签顺序不可控:VS Code 不提供任何 contribution point 控制 panel 容器相对内置页签(Terminal/Output/Problems/Debug)的顺序;默认行为是新装扩展的容器追加在内置页签之后,用户可拖拽并持久化。遇「精确紧邻某内置页签」类诉求应直接说明平台边界,勿承诺实现。
- dock 迁移改变
initialSize语义:initialSize是容器主轴方向的权重(类 flex-grow);侧边栏主轴=垂直(高度权重),Panel 默认主轴=水平(宽度权重)。值本身无需改,但语义随用户布局方向变化;遇视图过挤应调权重而非解除下限(与 #12 同一硬编码边界)。 - dock 决定 badge 可见性上限(关键):activitybar 容器图标支持「未聚焦也聚合显示」的一等 badge(#10 据此实现「面板未打开也持续显示」);panel 容器页签 badge 的可见性受 Panel 展开/收起态约束。
when:falseTreeView 在 panel dock 下的聚合可见性需 EDH 实测(跨 VS Code 版本)。本案用户明确接受此 trade-off——未提交计数仅在 Panel 展开时可见,不触发双容器回退;若后续需恢复「始终可见」计数,应走双容器(panel 主 + activitybar badge 专用)或状态栏计数器方案。 - dock 迁移属用户可见布局变更:VS Code 会记忆旧 dock 位置,老用户升级后需「View: Reset View Locations」或右键容器页签「Reset Location」才采用新默认(与 #12 同款平台行为,实机验证须用干净 profile)。
- 同类问题影响:所有在 activitybar/panel 间迁移自定义视图容器的扩展;凡依赖容器图标 badge「始终可见」语义的扩展;以及把「dock 迁移」误当作纯内部重构而忽视 badge 可见性回退的实现。
- 表因:用户截图反馈 GRAPH 视图(All 范围)提交记录未按时间倒序——前 10 行作者日期由
2026-07-05正确递减到2026-05-25,但第 11 行起日期回跳回2026-07-05(一批远端旁支提交:fix-github-security/msgpack/pydantic-settings/ts-deepmerge/perceive daegu-v2/docs wiki×2 /PDF→MD),理应排在最顶部的新提交却落在列表底部。 - 根因:
engine/log/log-query.ts的buildLogArgs取数首参为--topo-order,且管道各层(engine 解析 / adapter 装配 / webview 渲染)均无任何二次排序——git 输出序即最终显示序。--topo-order语义为「子在父之上」且额外约束「不同分支线历史不相邻混排」:它把一条分支的提交整块输出后再切下一条。当仓库存在从较旧提交分叉、但提交日期较新的旁支(本案--branches --tags --remotes命中的远端跟踪分支),这些旁支提交会被整块挤到列表末尾,造成日期列回跳。--topo-order不是按日期排序,分支成块是其设计特性而非 bug,但对「按时间倒序浏览」的人类预期是错的。实证(本仓git log,前 18 条作者日期):--topo-order序列在22:07:16(旁支提交)处错位到第 15 位(晚于20:32:35);--author-date-order同位置归位到第 13 位,整体严格单调递减。 - 处理方式:
log-query.ts:39单行'--topo-order'→'--author-date-order'。选--author-date-order而非--date-order的依据——单一事实源:视图日期列渲染的是row.authorDate(log-webview.ts:771fmtDate(row.authorDate)),排序键须与显示键对齐,否则 rebase / cherry-pick 提交(committer date ≠ author date)仍会与显示列错位。lane 算法安全性:通读graph-layout.ts:32-145,算法仅依赖「处理 commit 时窗口内其全部子已处理」这一不变量(在父 hash 上开 / 闭 lane 槽),不依赖 topo-order 的「分支成块」特性;git log --author-date-order契约原文 "Show no parents before all of their children are shown" 同样保证「子在父之上」,故 lane 不会断裂。同步更新 8 处topo-order注释(log-query.ts/graph-types.ts/graph-layout.ts/log-line.ts/log-webview.ts)澄清「不变量真实要求 = 子在父之上」,更新log-query.test.ts断言并补「不含--topo-order」回归护栏;顺带加固既有的--author前缀测试(startsWith('--author')→startsWith('--author=')),消除与本 flag 的前缀碰撞隐患。 - 后续防范:① 自计算 DAG lane 布局的算法只需「子在父之上」拓扑约束,不要求「分支成块」——
--topo-order/--date-order/--author-date-order三者均满足前者,区别仅在无父子约束提交间的次序;选 flag 时应据「人类预期次序」而非默认 topo。② 排序键须与显示键对齐:UI 显示哪一列(author date / committer date),git 取数就应用对应的--author-date-order/--date-order,否则会出现「列内日期看似乱序」的二次 bug。③ 排查「显示乱序」类问题先确认管道是否存在二次排序——本案各层均无,根因在 git 取数参数层;若上层曾 re-sort,还须检查是否破坏拓扑约束。④ 测试断言里用--author/--grep这类短前缀判「无 flag」时,须警惕与同前缀的排序 / 过滤 flag(--author-date-order、--author-date)碰撞,宜用带=的精确前缀(--author=)。⑤ 与 #9 同源教训:log-query.ts的 git 取数参数是 GRAPH 视图多项语义(范围 / 排序)的单一事实源,改 git 参数 + 注释 + 测试断言三件套应一并完成。 - 同类问题影响:所有用
git log --topo-order取数、自计算或直接渲染提交图、且 UI 暴露「按时间浏览」预期的 Git GUI;凡把「lane 算法要求」误读为「必须--topo-order」(实为「子在父之上」即可)的实现均会复现日期回跳;以及排序键与显示日期列不一致(author date vs committer date)导致的「列内看似乱序」类二次 bug。
- 表因:用户反馈在 Commit 视图与 Graph 视图点击一个新增文件时差异视图直接打不开(预期应展示「全绿新增」对比);删除、重命名文件同样失败;Graph 视图重命名文件点击更是彻底无效。
- 根因:两处「打开差异」都为差异的「缺失端」构造了指向不存在对象的 git URI:Commit 视图
commands.ts的openDiff取toGitUri(change.uri, 'HEAD')(新增文件在 HEAD 不存在);Graph 视图history-commands.ts的openCommitFileDiff取toGitUri(uri,${hash}^)(新增文件在父提交不存在)。VS Code 的 gitGitFileSystemProvider.readFile行为随版本演进——1.85(本扩展engines.vscode下限)catch吞掉一切取对象错误返空(容错),当前主线改为对不存在对象抛FileNotFound、仅当ref === repository.getEmptyTree()(空树)时才回空。故'HEAD'/${hash}^这类具名 ref 在新版会抛错致差异打不开(旧版恰好容错掩盖了缺陷)。附带:Graph 视图detailLeafHtml的data-path取的是展示串"old → new"(sendCommitFiles把 rename 拼进path),joinPath得伪路径致重命名彻底崩溃;点击仅回传提交级hasParent而无逐文件status,宿主无法区分 A/D/M/R。 - 处理方式:缺失端统一改用 git 空树 ref(
4b825dc642cb6eb9a060e54bf8d69288fbee4904)构造 URI——旧版容错、新版空树逃逸,两版皆稳定解析为空内容(这正是 VS Code 官方 git 扩展现今为「新增文件」左端的做法,复用非自造)。正交分解:① 纯状态分类器engine/diff/change-side.ts(diffShapeFromStatus/diffShapeFromCode→added/deleted/renamed/modified);② adapter 层diff-sides.ts(GIT_EMPTY_TREE+resolveDiffSides按形态把缺失端置空树);③openDiff/openCommitFileDiff改为按status选端(Commit 视图签名不变;Graph 视图签名(hash, filePath, status?, oldPath?),移除hasParent,由status取代);④ 协议log/openFilepayload 增status/oldPath、去hasParent;⑤sendCommitFiles的path改回干净新路径,展示串由 webview 端用oldPath拼出(数据与展示分离)。新增/根提交均不再依赖^。同步补tests/unit/diff-change-side.test.ts(分类器)与tests/suite/diff-open.test.js(空树 URI 解析为空 + A/D/R/工作区新增打开差异)。 - 后续防范:① 为差异的「缺失端」构造 URI 时,一律用 git 空树 ref,不要对不存在对象取
'HEAD'/${hash}^这类具名 ref——它们只在旧版 VS Code(容错 readFile)上侥幸可用,新版必抛 FileNotFound。② VS Code git 扩展内部行为(如 readFile 容错性)随版本变化,复用其toGitUri时须确认跨engines.vscode下限到当前主线的兼容矩阵;空树 ref 是少数有契约保障的「稳定回空」途径。③ webview 的data-*属性应承载数据(机器可用的稳定 key/路径),展示串(含"old → new"这类人为拼接)只放在可见标签文本里——二者混用会导致joinPath之类以数据为输入的下游崩溃。④ 逐文件级语义(status)须端到端透传到决策点(host 命令),勿用提交级布尔(hasParent)模糊替代——后者无法区分单文件是 A/D/M/R。⑤ 测试断言「差异已打开」时勿按标签计数(VS Code{preview:true}会复用预览槽替换而非新增),应先closeAllEditors再按差异标题(含文件名)匹配标签。 - 同类问题影响:所有消费 vscode.git
toGitUri自建差异打开逻辑的扩展,凡为缺失端取具名 ref 的均在新版 VS Code 复现;凡 webviewdata-path复用展示串(含分隔符)的实现均会在路径拼接处崩溃;以及任何「逐文件操作」误用「提交级 / 全局级」标志判定单文件形态的设计。
- 表因:用户(附 VS Code「视图显隐」右键菜单截图)要求——Stash / Shelf 默认不显示、仅在用户勾选后出现;其余视图显示顺序为 Commit → Branches → Graph → Worktrees;且 Commit 与 Worktrees 默认折叠。
- 根因:非缺陷,系默认布局的 UX 决策落地。视图的显隐 / 顺序 / 初始尺寸纯由
package.jsoncontributes.views声明式驱动(src/无任何代码断言或依赖,两处.focus在折叠/可见态均正常),可零代码达成。此前 #12 缓解取值(Stash/Shelfcollapsed、Worktrees/Commit/Graph/Branchesvisible)与本诉求不符,需重排数组并改写visibility。 - 处理方式:重排
contributes.views["hyper-git"]数组为commit → branches → log → worktrees → stash → shelf → changesBadge(容器内顺序取声明顺序);visibility改写为 Commit/Worktrees=collapsed、Branches/Graph=visible、Stash/Shelf=hidden(VS Codesrc/vs/workbench/api/browser/viewsExtensionPoint.ts的viewDescriptorschema 确认visibility枚举为['visible','hidden','collapsed'],hidden映射hideByDefault:true——不入容器但「可经视图菜单发现/勾选」,正对附图未勾选态);initialSize权重与changesBadge(when:false,恒末位)不变。命令 / 协议 / 菜单 /viewsWelcome均按 id 匹配、与顺序无关,零破坏。新增tests/unit/views-layout.test.ts声明式护栏锁定顺序 + 各视图visibility+changesBadge的when:false。 - 后续防范:①
visibility/ 顺序 /initialSize仅影响全新安装 / 干净 profile 的初始态(复用 #12 结论,不重述)——老用户须「View: Reset View Locations」或右键容器「Reset Location」方生效,实机验证须用干净 profile。②visibility:"hidden"≠when:"false":前者「默认隐藏但用户可经视图菜单勾选恢复」(Stash/Shelf),后者「恒不渲染」(changesBadge 角标承载专用)——诉求「默认不显示但用户可自行开启」必须用hidden,误用when:false会致用户无法启用。③ 容器内视图相对顺序可控(声明顺序驱动),但容器相对内置页签的顺序不可控(见 #13),勿承诺后者。④ 三态语义以 VS Code schema 为权威,勿据「本仓此前只用过visible/collapsed」臆断hidden不存在。 - 同类问题影响:所有以
contributes.views声明默认布局的 VS Code 扩展;凡将「默认隐藏但可恢复」误用when:false(致用户无法启用)或反向混淆的实现;以及把「仅初始态生效」误当「持久强制」而困惑于老用户升级后不生效的排障。