dsh-git-tree
Verifieddsh-git-tree · v0.13.1 · MIT · Web UI
DeepSeek Harness plugin: a Sourcetree-style Git workspace — branches, diff, history, fetch/pull/push, AI-written commit messages and AI-assisted conflict resolution, with pinned and usage-ordered repositories.
Install
dsh plugin add dsh-git-tree Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-git-tree
一个 DeepSeek Harness 插件:把 Sourcetree 那套 Git 工作区搬进 Web GUI——占满主面板的三栏布局(左侧「工作区 / 分支 / 标签 / 远端」导航、已暂存 / 未暂存文件列表、diff 视图),就在对话旁边,不用再切到另一个 App。

35 秒介绍片(mp4,3.6 MB) · 海报
0.13.1 能做什么
这一版是本地 Git 工作台:文件状态、历史、搜索、本地分支——读改动、暂存、提交、切分支、看任意一次提交,全在面板里完成,支持推送当前分支,也可以让 DSH 里的模型来写提交信息。fetch / pull、远程分支和远端管理还没有做。
- 一个
main面板,入口是左侧图标栏里的Git一行——和「插件」页面用的是同一类座位,所以它自动拿到整个中栏、窗口顶部安全区和 DSH 的深浅色。 - 快捷键开合:任意位置按
⇧⌘V(Windows 是Ctrl+Shift+V)打开面板,面板已经打开时再按一下就回到对话。命令走 DSH 自带的快捷键服务注册,所以它会出现在「设置 → 键盘快捷键」(⌘/)里,可以搜索、改键、恢复默认;工具栏右侧的键帽徽标和 diff 空态的提示显示的就是你当前的绑定。面板自己的确认框和 AI 冲突预览打开时不响应;提交框里的草稿按仓库记住,所以误触开关也不会把刚打的字丢掉。命令没有绑定时空态那行会指向「设置 → 键盘快捷键」。 - 顶部仓库切换:列出 DSH 已注册的 workspace,选中哪个,所有路由就作用在哪个仓库;仓库可以置顶(置顶区按置顶顺序在前),其余按你切换过去的次数排序、次数相同按最近一次。
- 分支与同步状态:当前分支、upstream、ahead/behind、仓库根目录。
- Sourcetree 的改动模型:冲突 / 已暂存 / 未暂存三组带计数;重命名保留原路径;同一文件两侧都有改动时两栏各出现一次(数据来自
git status --porcelain=v2 --branch -z)。 - 结构化 diff:宿主解析成 files → hunks → lines,渲染双行号、hunk 头、整行底色与
+N −M。 - 暂存 / 取消暂存 / 提交:行内复选框、悬停菜单、diff 头部文件级动作,底部提交区(作者、分支、暂存数、⌘↵)。
- 历史视图:提交泳道图 + 描述 / 短 SHA / 作者 / 日期,「所有分支 / 仅当前分支」过滤,每页 200 条分页加载。
- 提交详情:选中提交看信息、作者、日期、父级、refs 与变更文件;点文件看该提交内 diff;父级链接可跳转。
- 搜索视图:按提交信息 / 作者 / 文件路径搜提交,结果复用历史列表与详情。
- 分支:单击看该分支历史、双击检出;可新建 / 重命名 / 删除本地分支;脏工作区检出会被拒绝,不会自动 stash。
- 右键操作:改动行可提交(暂存并聚焦提交框)、暂存/取消暂存、放弃更改、删除文件 / 删除未跟踪文件、复制路径;分支行可检出、重命名、删除、复制名称;提交行可复制 SHA、从此提交新建分支、复制提交信息。危险操作先确认,只有可执行的破坏项是红色。
- 获取 / 拉取:获取只更新远端跟踪引用(带
--prune),让 ahead/behind 是新鲜的;拉取把当前分支快进到 upstream,没有 upstream 时走origin/<同名分支>。两个动作分开,面板打开时静默获取一次;本地与远端分叉时先确认,同意后才把远端合并进来,冲突落到冲突视图。 - 推送:把当前分支推到它的 upstream(首次推送自动建立跟踪)。永不 force;认证失败和非快进会被翻译成可读的下一步提示。
- 提交并推送:提交卡片里的复选框,提交成功后立即推送;推送失败不会回滚提交。
- 提交区可折叠:折叠后只剩一条——作者、分支、暂存数、提交信息首行——把空间还给 diff。两种状态下右上角都是带文字的「收起 / 展开」按钮,点这条也能切换;折叠状态跨刷新记住,改动行右键「提交…」时会先自动展开并聚焦输入框。
- 冲突视图:冲突文件右侧显示工作区里带
<<<<<<<标记的内容(ours 蓝色、theirs 琥珀色逐行高亮),不再是「该文件没有差异」。 - 把冲突交给 DSH 会话:头部
✦ 交给 AI 助手会在该仓库所在的 workspace 打开会话,并把写好的提示词填进输入框——仓库、文件、分支、已数到的冲突块数,以及边界(只改这个文件、改完说明、不要git add/commit/checkout,由你手动标记为已解决)。不自动发送,面板也不写任何文件。仓库不在已注册 workspace 里时,自动复制提示词并弹框说明原因。原来的「面板内模型合并(预览 → 应用)」保留在冲突文件右键菜单的「用模型合并(预览)」;「标记为已解决」(git add)和「还原冲突」(git checkout --merge)仍是两个需要你点的独立动作。 - AI 生成提交信息:提交卡片里的 ✨ 按钮,按这次提交会包含的文件(有暂存就用暂存,没有就用全部改动)的 diff 生成提交信息;旁边的模型按钮从 DSH 的实时模型列表(所有已注册提供方,分组展示)里挑模型。选择按 DSH home 记住,调用本身走 DSH 自己的
llm服务,插件里不另配一套 provider。它只把文字填进输入框——仍然由你检查、点提交。所选文件的 diff 会发给你选中的模型。 - 本地动作:在 Finder 中显示、在终端打开(macOS)。
- 实时,但按你在做什么花代价:只在窗口聚焦且面板在前台时轮询——改动活跃时 3 秒一次,连续三次没有变化后退避到 10 秒,窗口失焦就完全停。空闲的一次轮询只跑一条
git status(仓库根、身份、remotes 缓存一分钟;引用树走自己的 10 秒节拍),写操作后仍然立即刷新。中等仓库实测:空闲 40 秒从 123 个 git 进程降到 9 个,CPU 从约 0.75% 降到约 0.25%。 - 写操作面很小:stage / unstage / commit、放弃更改、删除、分支新建-重命名-删除、检出、合并到当前分支,以及一个永不 force 的当前分支推送。没有
reset --hard、没有 force push、没有 amend、没有 rebase、没有 stash 路由。
状态
| 能力 | 0.13.1 |
|---|---|
main 面板 + 侧栏入口 |
已完成 |
快捷键(⇧⌘V) |
已完成 |
| 仓库切换(DSH workspaces) | 已完成 |
| 分支 / ahead / behind / 引用树 | 已完成 |
| 冲突 / 已暂存 / 未暂存分组 | 已完成 |
| 结构化单文件 diff | 已完成 |
| 暂存 / 取消暂存 / 提交 | 已完成 |
| AI 生成提交信息 + DSH 模型选择 | 已完成 |
| 历史视图(泳道 / 分页 / 过滤) | 已完成 |
| 提交详情 + 提交内 diff | 已完成 |
| 搜索(信息 / 作者 / 路径) | 已完成 |
| 分支导航 + 检出 | 已完成 |
| 分支新建 / 重命名 / 删除 | 已完成 |
| 右键操作 + 放弃更改 / 删除文件 | 已完成 |
| 推送 / 提交并推送 | 已完成 |
| 冲突视图 | 已完成 |
| 交给 AI 助手(会话交接) | 已完成 |
| 面板内模型合并(预览 → 应用) | 已完成(右键菜单) |
| Finder / 终端 | 已完成 |
| 自动刷新(聚焦感知 + 空闲退避) | 已完成 |
| 推送当前分支 | 已完成 |
| 获取 / 拉取 | 已完成 |
| 远程分支 / 远端管理 | 未做 |
| 变基 / 贮藏 / 手工三栏合并编辑器 | 不做 |
| 大仓库虚拟滚动 | 不做(按决定取消) |
| 与 Agent 共用操作 | 未做 |
完整计划(包括布局依据的实测 UI 规格)在 docs/PLAN.md。
设计取舍
面板抄的是 Sourcetree 的信息架构,不是它那层皮。布局数字是量出来的,不是估的:来自运行中 App 的 Accessibility 树,以及解码它的 Interface Builder 归档——见 docs/sourcetree-ui.md 和 docs/ib-summary.md。
两个值得一提的结论:
- Sourcetree 的 diff 没有语法高亮,只用整行底色区分增删。这个面板刻意保持一致——diff 里「哪几行变了」比「哪个 token 是关键字」重要。
- 所有颜色都是
--dsw-alias-*主题 token,深浅色不用做第二套设计。一旦有人写死颜色,npm test会直接失败。
AI 生成提交信息
提交卡片里的 ✨ 按钮和模型按钮,是一个刻意做小的功能:
- 用哪个模型:列表来自
ctx.llm的实时目录——DSH 注册的每个提供方,以及它声明的模型。和 DSH 模型选择器看到的是同一份数据,每次请求现取,所以你在 DSH 设置里新加的模型不用重开面板就能出现在这里。 - 用哪些文件:这次提交会包含的文件的 diff——有暂存就用暂存行,没有就用工作区行。未跟踪文件只贡献路径不贡献内容(
git diff没有可对比的一侧);diff 超过 24 KB 会被截断并标注。 - 会发出去什么:文件列表和这份 diff,发给你选中的模型,每按一次发一次。没有别的——不发送提交历史、凭据或会话上下文。
- 它不做什么:不提交、不暂存、不改文件。文字只填进输入框,由你修改或放弃。
- 选择存在哪:
~/.dsh/git-tree/ai-settings.json(或$DSH_HOME/git-tree/ai-settings.json),由宿主半写入。没有保存过就用 DSH 默认 Agent 模型;如果那条路由已不在列表里,卡片显示「选择模型」而不是去调用一条坏路由。 - 失败会明说:缺凭据、限流、模型想调用工具、空回复、超时,各自返回自己的提示,而不是一句笼统的错误。
在 DSH 里怎么接
| 半边 | 文件 | 作用 |
|---|---|---|
| Host | index.js(纯 ESM,无构建) |
用 ctx.connection.fetch.register 注册一组带鉴权的 exact Fetch 路由(见下),用 ctx.subprocess 跑 git,用 ctx.llm 生成提交信息 |
| Client | client.js(由 src/client/index.tsx 构建) |
把侧栏图标注册进 sidebar.panellist、面板注册进 main(共用 id git-tree),用 ctx.shortcuts 注册 panel.git-tree.toggle,并用 uiWorkspace.startSession 把冲突交给该仓库的会话(提示词预填、不发送) |
GET /api/git-tree/workspaces → DSH 已注册 workspace(带置顶与打开次数,已排好序)
POST /api/git-tree/repositories/pin → { path, pinned } → 置顶 / 取消置顶
POST /api/git-tree/repositories/open → { path } → 记录一次主动切换
GET /api/git-tree/status?cwd=… → 分支、upstream、ahead/behind、entries、counts
GET /api/git-tree/diff?path=…&side=… → files → hunks → lines、统计(或 commit=<sha>)
GET /api/git-tree/refs?cwd=… → 本地分支、标签
GET /api/git-tree/history?scope=… → 带泳道的提交、分页、过滤
GET /api/git-tree/commit-info?sha=… → 元信息、父级、refs、变更文件
GET /api/git-tree/ai/models → DSH 模型目录 + 已保存/默认路由
POST /api/git-tree/stage → { paths[] }
POST /api/git-tree/unstage → { paths[] }
POST /api/git-tree/commit → { message } → { sha, shortSha, subject }
POST /api/git-tree/commit-message → { side, files[] } → { message }(一次模型调用)
POST /api/git-tree/conflict/resolve → { path } → { content, diff }(只预览,不写盘)
POST /api/git-tree/conflict/apply → { path, content } → 写入工作区文件
POST /api/git-tree/conflict/revert → { path } → git checkout --merge
POST /api/git-tree/conflict/mark-resolved → { path } → git add(有标记时拒绝)
POST /api/git-tree/ai/model → { provider, model } → 记住选择
POST /api/git-tree/checkout → { branch } → 脏工作区拒绝
POST /api/git-tree/branch/create|rename|delete
POST /api/git-tree/open → { target: 'finder' | 'terminal' }
POST /api/git-tree/push → 当前分支 → 它的 upstream(永不 force)
POST /api/git-tree/fetch → git fetch <remote> --prune(不动工作区)
POST /api/git-tree/pull → { mode: 'ff-only' | 'merge' } → 先获取再快进
POST /api/git-tree/discard → { paths[], side } → git restore
POST /api/git-tree/remove → { paths[], untracked } → git rm / git clean
git 不走 shell;子进程环境带 GIT_TERMINAL_PROMPT=0(永不卡在认证)、GIT_OPTIONAL_LOCKS=0(不在你的仓库里拿可选锁)、GIT_CONFIG_COUNT=0,输出与耗时都有上限。绝对路径、含 ..、以 - 开头的路径在进入 argv 之前就被拒绝;工作区之外的 cwd 一律 403。
宿主机制的取证(slot id、Fetch 路由通道、subprocess 安全、主题 token)都在 docs/dsh-plugin-api.md,每条都带 app.asar 内的 file:line 引用。
安装
从 npm 按名称安装(包里同时带宿主半、客户端半和 bundle patch):
dsh plugin --profile <profile> add dsh-git-tree
或者在带 plugin manager 的会话里:
plugin_manager action=install_bundle target=dsh-git-tree
从本地检出安装:
dsh plugin --profile <profile> add /Library/aiProject/dsh-git-tree
装完要重启 DSH(宿主半在 App 启动时装配)。在你的按下暂存或提交之前,插件不会写你的仓库。
开发
node build.mjs # src/client/index.tsx → client.js(产物入库)
npm run check # 两个半边 node --check
npm test # 78 条断言:清单、宿主路由、解析器、客户端启动、导入守卫
build.mjs 先找本包的 node_modules 里的 esbuild,再找已安装的 dsh-workbench;都没有就停下并打印修复命令(npm i -D esbuild)。
测试不需要 DSH、不需要 git、不需要浏览器:
test/manifest.test.mjs—— 加载器会校验的东西(platform、exports、patch 行、图标、语言包)。test/routes.test.mjs—— 用假 context 跑apply():七条路由、真实改动模型、仓库切换、路径前有--、穿越/绝对/选项形路径被拒、提交前检查。test/host-modules.test.mjs—— 解析器作为纯函数:porcelain v2(rename / 冲突 / 双侧)、unified diff、refs。test/ui-contract.test.mjs—— 构建产物:loader 包装、恰好两次 slot 注册且共用 id、调用路由、只用主题 token、不碰document.body。test/client-boot.test.mjs—— 在 Node 里执行构建产物、物化插件,用「未声明服务即抛错」的 Cortis 式 ctx 调apply()。
介绍片与海报
docs/media/ 里是这套宣传素材:介绍短片、本页用的无声循环、竖版海报和横版头图。
| 文件 | 内容 |
|---|---|
promo.mp4 |
35 秒介绍片(1920×1080,H.264,3.6 MB) |
promo-loop.gif |
上方那张 6 秒无声循环(900 px 宽) |
poster.png |
竖版海报 1782×2353 |
cover.png |
横版头图 1600×900 |
片子与图片都取自真实面板,演示仓库是虚构的 aurora-notes(作者 Lin Wei <[email protected]>);安装段是在一次性 DSH profile 里对已发布包做的真实安装。
docs/design/ 里是实现对照的四张画板:
| 文件 | 内容 |
|---|---|
overview.png |
四张总览 |
frame1-docked.png |
对话旁边的窄面板形态 |
frame2-workspace.png |
全页工作区:导航 / 文件 / diff / 提交区 |
frame3-history.png |
历史泳道与提交详情 |
frame4-states.png |
行状态、尺寸栅格、主题 token、数据流 |
许可
MIT
