dsh-git-vcs
DSH Web GUI 的 Git 版本管理插件:右侧栏新增「版本管理」页,功能与界面参照 IntelliJ IDEA 的 Version Control 工具窗(本地变更 / 提交 / 历史 / 分支 / 命令 / 贮藏)。 适合需要在工作区直接进行Git提交、分支管理等操作的用户。
설치
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:TiChuXiXi/dsh-git-vcs使用方法
右侧栏 tab 条的「+」或引导页胶囊 → 版本管理。首次加入后右侧栏默认页会从「工作区文件」变为 引导页(官方规则:引导入口多于一个时打开引导页),页面上有两个胶囊:工作区文件 / 版本管理。
目录还不是 Git 仓库时
面板只渲染一块居中空态,只有三样东西:Git 图标 + 「当前目录不是 Git 仓库」+ 目录路径, 下面一个 「在当前目录初始化 Git 仓库」 按钮——页签、工具栏、提交树等一律不显示 (那些内容在非仓库下全是空壳),也没有多余的长篇说明。
点按钮先展开初始分支名:可自由输入,另有 main / master 两个一键备选(回车即确认),
点「初始化」才真的执行 git init;成功后空态自动消失、面板自己长回完整功能(不必再点 Refresh)。
分支名之所以要用户过一眼:git config init.defaultBranch 因机器而异(本机就是 master),
替用户决定会得到一个意料之外的分支名。不传 branch 时 host 侧的兜底顺序是
入参 → init.defaultBranch → main。
host 侧的语义边界(repo/init,受 allowWrite 门禁):
| 目录状态 | 行为 |
|---|---|
| 不是仓库 | `git init -b |
| `;git 两种「选择」在面板里是分开的、互不影响: |
勾选框决定本次提交包含哪些文件(提交走
git add --+git commit -m --:先暂存再按 pathspec 提交,未跟踪文件也能直接勾选提交, 且忽略索引里其它内容); 「暂存 / 取消暂存」是git add/git restore --staged,把改动放进或移出索引(Index), 供你在终端或别的工具里按标准 git 工作流使用。MM状态的文件会同时出现在「已暂存」和「已修改」两个分组里。
- 差异按行所属分组取:已暂存组走
diff --cached、其余走工作区diff;未跟踪文件必须带untracked=true(未跟踪文件不在 index 里,普通git diff --恒为空,只有 host 的--no-index分支拿得到"新文件"差异); 命中.gitignore的文件不发请求,直接说明没有可展示的差异。 - 选中项会随快照校验:条目消失(文件被别处提交/回滚)即收起差异面板与底部操作条, 条目只是换了分组(刚点了暂存)则跟随到新分组,避免右侧继续显示已经不存在的差异。
- 差异面板:行号 gutter、↑/↓ 在改动块之间跳转(当前改动的高亮只画在 gutter 上)、 改动计数、长行自动换行(不出现横向滚动,红/绿底色保留)。
提交区
提交信息、Amend(allowDangerous 控制)、Commit,行内还有「全选 / 全不选」与「清空」。
每一行改动前面的勾选框(零构建下自己画的方块)只切换勾选、不打开右侧差异;
只提交勾选的文件,未跟踪文件同样可选。
没有可提交的改动、或一个文件都没勾时 Commit 置灰(悬停说明原因),不会"点了才报错"。
Amend 语义是修补上一次提交,忽略勾选(按钮文案里已注明)。
Log
列顺序对齐 IDEA:时间 · 提交树 · Message · Author · Commit。
- 提交树列:多泳道图,画法对齐 IDEA 与
git log --graph——每条分支一条自己的泳道, 分叉/汇合画在真正分叉的那次提交上(基于历史提交拉出来的分支不会被画成当前分支的延续)。 泳道算法:维护「每条泳道下一个期待的提交(= 上一条提交的父提交)」, 没有泳道期待本提交就是分支起点(新开一条泳道),多条泳道同时期待本提交就在本行汇合; 第一个父提交留在原泳道,合并提交的其余父提交各开一条斜线泳道。 圆点用 SVG 真圆(外径 10px、圆环 2px),HEAD 提交实心、其余空心; 泳道颜色按分支名排序取模分配(当前分支优先,跨渲染稳定),圆环用深色、连线用同色系不透明浅色 (不透明才敢让上下两段各越界 1px 重叠消接缝,半透明会叠出更深的色带)。 竖直段用 div 画(calc(50% ± 半径)精确止于圆环外沿,圆点内部不画线), 跨泳道的斜线画在一张铺满泳道区的 SVG 里(纵向 0–100 的百分比坐标 +non-scaling-stroke, 行高随标签换行变化也不用重新测量)。 节点右侧标出所有指向该提交的分支标签(同色系描边 + 淡底,本地实线 / 远程虚线,当前 HEAD 分支加粗,origin/HEAD符号引用跳过),一眼看出每个分支停在哪次提交;标签区统一对齐在泳道区右侧(不跟着泳道左右跳)。 该列宽度按泳道数与标签内容自适应(64–420px)且不可拖动。 - 行交互:鼠标移到任意提交行即高亮(零构建下没有 CSS
:hover,用状态模拟),光标为pointer,点击展开该提交详情。 - 右键菜单(屏蔽浏览器原生菜单):按功能分组并带分组标题与分隔线,支持多级子菜单
(父项 hover 或点击展开,按可用宽度自动向左翻转;点菜单外 /
Esc关闭):- 查看:展开提交详情
- 复制:复制完整 ID / 短 ID / 提交信息 / 作者与邮箱
- 分支 ▸:基于此提交新建分支(自动展开详情并聚焦分支名输入框)、检出此提交(分离 HEAD)、 把此提交合并到当前分支
- 修改历史(危险) ▸:拣选(Cherry-Pick)、回滚(Revert)、重置到此提交 ▸(Soft / Mixed / Hard,Hard 走二次确认)
- 受
allowWrite/allowDangerous门禁的项自动置灰禁用。
- Commit 列:显示短哈希,直接点击短哈希即复制完整提交 ID(无独立按钮)。 复制结果走右下角浮动提示条(绝对定位、不参与流式布局,出现/消失不会顶动高度造成抖动;2 秒后自动消失, 失败为红底);提交详情里的完整哈希同样可点。
- 列宽:时间 / Message / Author / Commit 四列表头右侧有拖动手柄(
col-resize,pointer capture, 不依赖 window 监听),拖动范围各自钳制(92–360 / 120–900 / 60–280 / 64–220px); Message 列默认吃掉剩余宽度,被拖动后改为固定宽度,总宽超出面板即横向滚动。 - 范围与懒加载:列表覆盖所有分支(本地 + 远程)的节点,不只是当前分支——host 侧走
git log --exclude=refs/stash --all,stash 的提交被挡在外面(它有独立页签)。 一页 50 条,滚动到底自动续拉下一页(--skip接着上一页的窗口取,按 hash 去重), 最多渲染 2000 行;刷新(含自动刷新)按「已加载条数」一次拉回,滚到深处不会因为刷新弹回第一页。 列表底部的续拉提示行本身可点(面板很高、50 行撑不出滚动条时的那条入口)。 刻意不用--date-order/--topo-order:这两种排序要先把整段可达历史读进内存排序, 大仓库上首屏会明显变慢,而默认的反向时间序是流式的——它才是「按需加载」成立的前提。
提交详情
默认不显示,点条目才展开;面板在 Log 下方(上下布局),顶边有拖拽条可改高度
(默认 300px,上拖变高,钳制 140–720px 且不超过容器 80%),右上角 × 关闭。
详情内含 Revert / Cherry-Pick / Reset --soft / Reset --hard、「新分支名」→ 基于该提交建分支(不切换),
以及变更文件列表——默认不展示任何文件的差异,文件行有 hover 高亮、光标 pointer,
点某个文件才按需拉取该文件的 diff(show/file 端点),再点一次收起。
点提交时只取元数据与文件列表(show + noPatch),不再一次性传输整次提交的 patch;
文件差异区同样有行号 gutter、↑/↓ 改动块跳转与改动计数。
Branches
本地 / 远程分支。当前分支用首列 * + 绿色加粗分支名标识(不做"选中"背景高亮——这里没有选中态),
行只在 hover 时变色、光标为默认箭头(操作都在行内按钮上,行本身不可点)。
远程分支列表会过滤符号引用——refs/remotes/origin/HEAD 的 %(refname:short) 会退化成 origin,
不过滤就会和真正的 origin/main 一起显示成两条。
每行操作:本地分支 推送(有 upstream 时 git push ;没有 upstream 时按钮变成
「推送并设 upstream」,走 git push --set-upstream ,remote 取自该分支的 upstream,
缺省 origin)、切换、合并、删除;顶部还有「新建并切换」。推送与工具栏 Push 同一道门禁(allowPush)。
Remotes
git remote -v 的全部远程与 fetch / push 地址;顶部表单可填名字 + URL(+ 可选 push URL)→ Add Remote,
并且可顺手填用户名 / 密码·Token(可选)——填了会在加好远程之后一并存进凭据助手
(保存失败只警告,不影响远程已经加好的事实)。每个远程行有 Remove 危险按钮(点击走内联二次确认)。
Stash(贮藏)
git stash 的入口 —— 把当前未提交的改动整体收起来、工作区回到干净状态,之后用
「应用(apply,保留记录)/ 弹出(pop,取回并删除记录)/ 删除(drop)」处理;
与 Local Changes 里的「暂存」不是一回事(那是 git add 放进索引)。
- 当前实现用
git stash push [-m 说明],不带-u:未跟踪的新文件不会被收走,会留在工作区。 - 列表里的引用是
stash@{n}数字选择器(由列表下标合成,与 git 编号一致)——不能用%gd配--date=iso-strict,那会生成stash@{2026-09-15T…}时间戳选择器,而 git 对它会打印Dropped …、 退出码 0,却什么也不删;host 现在还会核对操作前后的贮藏条数,把这种静默失败变成明确错误。
Console
面板发起的每条 git 命令(argv、退出码、耗时、stderr、是否截断)。
反馈与状态
- 所有提示(写操作结果、复制反馈、失败与 git 报错)统一走右下角浮动 toast(绝对定位、不参与流式布局), 没有顶部错误横幅;失败的完整命令与 stderr 可在 Console 页回看。
- 底部状态栏只在忙碌时出现,显示当前阶段(读取仓库 / 暂存中 / 提交中 / 刷新列表…)。
- 所有等待都有文案:顶部 2px 进度条 + 阶段文案 + 分区占位(读取差异… / 读取提交详情… / 读取远程仓库…)。
性能约定(改动前先读)
面板流畅度取决于「客户端→宿主往返次数」与「git 进程创建次数」,两者在 Windows 上都是几十到上百毫秒级:
- 一次刷新只发一次 RPC:
repo/snapshot在 host 侧把 status / log / for-each-ref(本地+远程合并) / stash / 版本 / remote 六项并发探测后一次返回(含 Console 流水)。旧实现要 6 次往返、约 16 次串行进程创建, 单次刷新 1.5–2.5s;现在是 1 次往返、6 次同波进程。 - 缓存:
git可执行文件路径(resolveExecutable)、仓库根(rev-parse --show-toplevel,按路径)、git --version、remote.origin.url各只取一次;init会写回仓库根缓存。
配置
插件行的 config(见 cordis.patch.yml,改后触发热替换):
| 字段 | 默认 | 说明 |
|---|---|---|
allowWrite | true | 关闭后所有写操作(暂存/提交/分支/贮藏…)直接报 git-vcs/write-disabled |
allowPush | true | 是否允许 push;置 false 时按钮禁用并提示 |
allowDangerous | true | 是否允许 reset / revert / cherry-pick / 删分支 / 回滚文件 |
gitPath | '' | git 可执行文件绝对路径,空 = 从 PATH 解析 |
repoRoot | '' | 限定可操作的仓库根;空 = 允许任意会话工作目录 |
diffContextLines | 3 | 统一 diff 上下文行数(钳制 0–200) |
maxOutputBytes | 2097152 | 单条命令输出内存上限(超出保留尾部并标记截断,钳制 4096–64MiB) |
timeoutMs | 120000 | 单条命令超时(钳制 1s–30min) |
autoRefreshSeconds | 0 | 面板自动刷新间隔,0 = 关闭(钳制 0–3600) |
consoleLimit | 200 | 命令流水保留条数(钳制 10–2000) |
本插件刻意零运行时依赖:host 半区不 import 任何
@deepseek-ai/*值,subprocess/connection/webServer都通过inject声明 +ctx.get()读取;因此配置没有走 SchemasteryConfigschema, 而是在代码内normalizeConfig()合并默认值并做范围钳制(未知键忽略、类型不符即用默认值)。