VS Code 扩展,在源代码管理(SCM)面板中提供类 IntelliJ IDEA 的 Git 分支浏览器与差异对比功能。
仓库结构
| 文件 | 职责 |
|---|---|
src/extension.ts |
扩展入口,注册所有命令与 TreeView |
src/gitService.ts |
封装所有 git 命令(分支/标签/远程/差异/导出) |
src/gitBranchTree.ts |
BRANCHES 视图的 TreeDataProvider,右键菜单逻辑 |
src/gitDiffTree.ts |
Changed Files 视图的 TreeDataProvider,差异文件列表 |
package.json |
命令、views、menus、when 贡献点 |
webpack.config.js |
仅打包 src/extension.ts 为 out/extension.js |
视图(Views,挂在 scm 容器下)
maxGit.branches—— 标题 BRANCHES,始终显示(when: true)- 节点结构:
HEAD(current branch)/Local(本地分支) /Remotes(远程仓库 → 远程分支) /Tags(标签) - 顶部
HEAD节点显示当前分支;localBranchCurrent为当前分支的contextValue
- 节点结构:
maxGit.changedFiles—— 标题 Changed Files,仅在有差异会话时显示(when: maxGit.hasDiffSession,由setContext控制)- 展示某次对比的变更文件列表,带状态图标:新增
add/ 修改diff-modified/ 删除remove - 单击文件节点(或 hover 点 inline "Open Diff")打开该文件原生差异(
vscode.diff)
- 展示某次对比的变更文件列表,带状态图标:新增
命令(Commands)
maxGit.refreshBranches —— 刷新 BRANCHES 视图
maxGit.clearDiff —— 关闭 Changed Files 列表(标题栏关闭按钮)
maxGit.openFileDiff —— 打开单个文件的差异对比
右键菜单(branch/tag 节点,view/item/context)
当前已显示的菜单项
| 命令 | 标题 | 适用节点 |
|---|---|---|
maxGit.checkout |
Checkout | local/remoteBranch/tag |
maxGit.checkoutNewBranch |
New Branch from... | local/remoteBranch |
maxGit.compareWithCurrent |
Compare with Current | local/remoteBranch/tag |
maxGit.showDiffWithWorkingTree |
Show Diff with Working Tree | local/remoteBranch/tag |
maxGit.deleteRef |
Delete | local/remoteBranch/tag |
已隐藏但代码仍保留的菜单项(从 package.json 的 view/item/context 移除即可恢复)
这些命令的完整实现都在
gitService.ts和gitBranchTree.ts中,只需在package.json的contributes.menus.view/item/context加回对应条目即可重新出现在菜单。
| 命令 | 标题 | when 条件 |
后端实现 |
|---|---|---|---|
maxGit.checkoutAndRebase |
Checkout and Rebase onto... | viewItem =~ /^(localBranch|localBranchCurrent|remoteBranch)$/ |
GitService.checkoutAndRebase → git rebase <target> |
maxGit.rebaseCurrentOnto |
Rebase Current onto... | 同上 | GitService.rebaseCurrentOnto → git rebase <target> |
maxGit.mergeIntoCurrent |
Merge into Current | 同上 | GitService.mergeIntoCurrent → git merge <source> |
maxGit.pullIntoCurrentWithRebase |
Pull into Current using Rebase | viewItem == remoteBranch |
GitService.pullIntoCurrentWithRebase → git pull --rebase <remote> <branch> |
maxGit.pullIntoCurrentWithMerge |
Pull into Current using Merge | viewItem == remoteBranch |
GitService.pullIntoCurrentWithMerge → git pull <remote> <branch> |
恢复示例(放回 view/item/context 数组):
{
"command": "maxGit.checkoutAndRebase",
"when": "view == maxGit.branches && viewItem =~ /^(localBranch|localBranchCurrent|remoteBranch)$/",
"group": "1_branches@3"
},
{
"command": "maxGit.rebaseCurrentOnto",
"when": "view == maxGit.branches && viewItem =~ /^(localBranch|localBranchCurrent|remoteBranch)$/",
"group": "3_merge@1"
},
{
"command": "maxGit.mergeIntoCurrent",
"when": "view == maxGit.branches && viewItem =~ /^(localBranch|localBranchCurrent|remoteBranch)$/",
"group": "3_merge@2"
},
{
"command": "maxGit.pullIntoCurrentWithRebase",
"when": "view == maxGit.branches && viewItem == remoteBranch",
"group": "3_merge@3"
},
{
"command": "maxGit.pullIntoCurrentWithMerge",
"when": "view == maxGit.branches && viewItem == remoteBranch",
"group": "3_merge@4"
}
Checkout 行为约定(与 IDEA 一致)
- 本地分支 →
git checkout <branch> - 远程分支 →
git checkout -b <branch> <remote>/<branch>(创建同名跟踪分支,避免 detached HEAD) - 标签(tag)→
git checkout -b <tag> <tag>(创建同名本地分支,避免 detached HEAD)
GitService.getCurrentBranchName() 用 git rev-parse --abbrev-ref HEAD 作为当前分支的权威判定,兜底保证 HEAD 节点与本地分支 current 标记正确。
差异对比实现说明
- 对比时调用
GitService.exportTree(ref, dir)(git ls-tree -r -z --name-only+git show <ref>:<file>,-z避免中文文件名被加引号)把整棵树导出到临时目录。 - 工作树用
GitService.exportWorkingTree(dir)(git checkout-index -a -f --prefix=...)。 - 变更列表用
GitService.getDiffFiles/getDiffFilesWithWorkingTree(git diff --name-status)返回带A/M/D状态的ChangedFile[]。 - 不要用目录 URI 直接
vscode.diff(旧版本 VS Code 不支持目录级 diff,会报 "is actually a directory");改为逐文件vscode.diff(fileUri, fileUri)。
构建
npm install
npm run compile # 或 npm run watch
按 F5 启动 Extension Host 调试。