dsh-git-branch-manage 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +165 -1
- package/lib/client.js +15 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录 `dsh-git-branch-manage` 的重要变更。版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [1.3.1] - 2026-11-15
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- **会话框分支菜单藏起已被工作树占用的本地分支**(决策 0048):`buildBranchRows` 曾把已被 linked worktree 检出的本地分支从「本地分支」组剔除,而主仓库会话开始后又整体不渲染「工作树」组,于是这些分支在菜单里完全不可见,容易被误认为分支丢失。现在菜单如实列出全部本地分支;同一分支在本目录检出仍由 git 自己拒绝并报错,不额外加标注或拦截。
|
|
10
|
+
|
|
5
11
|
## [1.3.0] - 2026-11-04
|
|
6
12
|
|
|
7
13
|
### 新增
|
package/README.md
CHANGED
|
@@ -1,5 +1,169 @@
|
|
|
1
1
|
# dsh-git-branch
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/dsh-git-branch-manage)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
DSH(DeepSeek Harness)插件:在对话框里提供 git 分支管理以及代码推送、拉取功能。
|
|
7
|
+
|
|
3
8
|
提供 git 分支与 worktree 管理:输入框工具栏显示当前分支、可原地切换;支持 IDEA 风格分支选择器、远程分支检出、右键操作菜单(新建/重命名/删除/复制)、推送与更新当前分支,以及把分支隔离到独立 worktree 工作区,并管理其创建与删除。
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
> npm 包名 [`dsh-git-branch-manage`](https://www.npmjs.com/package/dsh-git-branch-manage),插件 id `dsh-git-branch`,展示名「git 分支管理」([包名由来见决策 0003](./docs/decisions/0003-rename-package-to-dsh-git-branch-manage.md))。
|
|
11
|
+
|
|
12
|
+
## 目录
|
|
13
|
+
|
|
14
|
+
- [截图](#截图)
|
|
15
|
+
- [功能](#功能)
|
|
16
|
+
- [输入框分支工具栏](#输入框分支工具栏)
|
|
17
|
+
- [Git 管理面板(侧边栏)](#git-管理面板侧边栏)
|
|
18
|
+
- [worktree 隔离工作区](#worktree-隔离工作区)
|
|
19
|
+
- [凭据管理](#凭据管理)
|
|
20
|
+
- [远程管理](#远程管理)
|
|
21
|
+
- [网络可靠性](#网络可靠性)
|
|
22
|
+
- [AI 工具](#ai-工具)
|
|
23
|
+
- [安装](#安装)
|
|
24
|
+
- [配置](#配置)
|
|
25
|
+
- [开发](#开发)
|
|
26
|
+
- [文档](#文档)
|
|
27
|
+
- [许可](#许可)
|
|
28
|
+
|
|
29
|
+
## 截图
|
|
30
|
+
|
|
31
|
+
输入框工具栏的分支 chip 与 IDEA 风格分支选择器(右键菜单含新建、新建并检出、新建分支并创建工作树、重命名、复制):
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
侧边栏 Git 管理面板(未提交变更 / 分支 / 提交历史图):
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
## 功能
|
|
40
|
+
|
|
41
|
+
### 输入框分支工具栏
|
|
42
|
+
|
|
43
|
+
- 当前分支 chip 常驻输入框工具栏;会话目录不是 git 仓库时不渲染。
|
|
44
|
+
- IDEA 风格分支选择器:**本地分支 / 远程分支 / 工作树** 分组、分支搜索框、全部展开/折叠、一键定位当前分支。
|
|
45
|
+
- 分支行右键菜单:检出、跳转到该分支的工作树、新建、新建并检出、新建分支并创建工作树、重命名、删除、复制分支名 / 复制路径。
|
|
46
|
+
- 一键 **fetch**、**更新当前分支**、**推送当前分支**(无可推送提交时禁用,[决策 0009](./docs/decisions/0009-disable-empty-push.md))。
|
|
47
|
+
- 原地 `git switch`:失败时 toast 透出 git 原始错误并回显原分支;已被其他 worktree 检出的分支在菜单里预防性禁用。
|
|
48
|
+
- 会话启动后目录固定,切换只作用于当前会话自己的检出,不动主 checkout。
|
|
49
|
+
|
|
50
|
+
### Git 管理面板(侧边栏)
|
|
51
|
+
|
|
52
|
+
注册为右侧边栏的一个页签(通过可选插件 `dsh-better-sidebar` 发布的 `ctx.betterSidebar` 注册,[决策 0013](./docs/decisions/0013-git-history-panel.md))。**该插件是可选的**:未安装时本页签不出现,输入框 chip 与设置卡照常工作。
|
|
53
|
+
|
|
54
|
+
- **未提交变更**:勾选文件加入暂存区、提交信息输入、**AI 生成**提交信息、提交;顶部另有 提取 / 更新当前分支 / 推送当前分支。
|
|
55
|
+
- **分支**:搜索框、本地/远程分组树、全部展开/折叠、新建分支、管理远程。
|
|
56
|
+
- **提交历史**:提交连线图([决策 0019](./docs/decisions/0019-git-history-graph-panel.md))、按提交人 / 分支 / 范围筛选、右键推送标签([决策 0020](./docs/decisions/0020-push-tag-action.md))。
|
|
57
|
+
- 顶栏:仓库路径 pill、刷新、**国内加速** 配置浮层、**密钥配置** 入口。
|
|
58
|
+
|
|
59
|
+
### worktree 隔离工作区
|
|
60
|
+
|
|
61
|
+
- 勾选 worktree 后基于所选分支创建独立 worktree 并**注册为 DSH workspace、直接开始会话**;同名 worktree 已存在则幂等复用([决策 0015](./docs/decisions/0015-branch-context-menu-and-switch-target.md))。
|
|
62
|
+
- 目录规划 `<rootDir>/<仓库名>/<规范化分支名>`:`/` → `-`,剔除 Windows 非法字符与尾随点/空格,避免多仓库同名分支冲突。
|
|
63
|
+
- 创建前可选 fetch 所有远程(失败不阻断创建);创建后可选惰性清理陈旧 worktree,`keepWorktrees` 控制保留上限。
|
|
64
|
+
- 创建后可拷贝配置文件(`postCreateCopyFiles`);存在 `.worktreeinclude` 时不拷贝。
|
|
65
|
+
- **worktree 管理对话框**:全量列表、脏工作区检查、删除 / 清除目录、孤儿(已删目录的残留注册)识别;目录内仍有 RUNNING 会话时拒绝删除,删除会连同会话归档与 workspace 注册一起清理,被检出的分支本身保留([决策 0014](./docs/decisions/0014-panel-card-height-and-branch-collapse.md))。
|
|
66
|
+
|
|
67
|
+
### 凭据管理
|
|
68
|
+
|
|
69
|
+
- 面板里存一次,**直接读写本机 `~/.git-credentials`**(权限 0600),并经 `~/.gitconfig` 的 URL 作用域 `include` 装上 git 内置 `store` helper([决策 0037](./docs/decisions/0037-local-git-credentials-backend.md))——因此面板、任何 shell 里的 `git push/pull`、AI 工具走的是同一账号。
|
|
70
|
+
- 启动时**自动从本机导入**:按用户 gitconfig 声明过的域名逐个 `git credential fill` 探测(串行 + 20 秒超时),`gh`、GCM、`store` 里的账号一次接管([决策 0038](./docs/decisions/0038-import-machine-credentials.md));面板另有手动「从本机导入」。
|
|
71
|
+
- 表单支持**浏览器登录**与令牌创建入口([决策 0024](./docs/decisions/0024-credentials-browser-sign-in.md))。
|
|
72
|
+
- 文件里不属于本插件的行(注释、`ssh://` 等)原样保留,**卸载插件后凭据仍属于用户**;未存域名 fail-fast(`GIT_TERMINAL_PROMPT=0`),不弹凭据对话框。
|
|
73
|
+
- 面板元数据(`method` / `updatedAt`)另存不含 secret 的旁文件。
|
|
74
|
+
|
|
75
|
+
### 远程管理
|
|
76
|
+
|
|
77
|
+
面板内管理仓库的多个远程地址(新增 / 编辑 / 删除,[决策 0022](./docs/decisions/0022-remote-address-management.md))。
|
|
78
|
+
|
|
79
|
+
### 网络可靠性
|
|
80
|
+
|
|
81
|
+
面向大陆网络下 GitHub 直连不稳定的三层处理([设计文档](./docs/design/network-reliability.md)):
|
|
82
|
+
|
|
83
|
+
- **国内加速**:读操作(fetch / update)把 `https://github.com/` 重写为「加速前缀 + 完整 URL」(默认前缀 `https://gh-proxy.com/`,[决策 0040](./docs/decisions/0040-accelerate-complete-github-url.md));**推送永不走镜像**,镜像读失败自动回直连再走备用链([决策 0044](./docs/decisions/0044-mirror-as-read-accelerator-and-ssh-hint.md))。
|
|
84
|
+
- **显式代理**:`networkProxy` 支持 `http(s)://` / `socks5://`,另可只读探测系统代理给出建议。
|
|
85
|
+
- **IP 优选备用通道**:直连发生可重试故障时,对 GitHub 地址做运行时 DoH 查询并用真实 git 请求竞速选路([决策 0042](./docs/decisions/0042-ip-optimized-fallback-channel.md))。
|
|
86
|
+
- **分级预算与核验**:按探测 / 读取 / 写入 / 超时核验分别设预算;推送前探测远端,推送超时后核验远端 ref,按失败类型给出建议([决策 0041](./docs/decisions/0041-network-budgets-and-push-verification.md))。
|
|
87
|
+
- 面板「**检测网络**」诊断入口;SSH 远端不做无效的 HTTP 通道探测,错误信息改为提示 HTTPS 方案。
|
|
88
|
+
|
|
89
|
+
### AI 工具
|
|
90
|
+
|
|
91
|
+
插件向模型注册两个只读优先的工具([决策 0029](./docs/decisions/0029-ai-network-credentials-tool.md)):
|
|
92
|
+
|
|
93
|
+
| 工具 | 作用 | 边界 |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| `git_credentials` | 列出已配置的 git 主机与掩码后的凭据 | 只读;`includeSecret` 才返回明文 |
|
|
96
|
+
| `git_network` | 用本机凭据执行一条 git 网络命令 | 子命令白名单 `fetch` / `pull` / `push` / `ls-remote`;`repoPath` 必须绝对路径;禁止 `--force`、`--delete`、`-c`、`--upload-pack` / `--receive-pack` 等 |
|
|
97
|
+
|
|
98
|
+
凭据经 git 原生 `credential-store` 通道生效,**不进入命令行参数、不进入对话上下文**。
|
|
99
|
+
|
|
100
|
+
## 安装
|
|
101
|
+
|
|
102
|
+
前置条件:
|
|
103
|
+
|
|
104
|
+
- 已安装 DSH(DeepSeek Harness);
|
|
105
|
+
- `git` 在 PATH 中可用;
|
|
106
|
+
- Node.js `>= 22.19`(插件运行环境要求,见 `package.json` 的 `engines`)。
|
|
107
|
+
|
|
108
|
+
安装方式(任选其一):
|
|
109
|
+
|
|
110
|
+
1. DSH 的插件管理(Plugins)页签里搜索并安装 npm 包 **`dsh-git-branch-manage`**;
|
|
111
|
+
2. CLI:`dsh plugin add dsh-git-branch-manage`(见 [notes/FIRST-SPEC.md](./notes/FIRST-SPEC.md))。
|
|
112
|
+
|
|
113
|
+
peer 依赖(`@deepseek-ai/cordis`、`@deepseek-ai/dsh-settings`、`@deepseek-ai/dsh-host-webserver`、`@deepseek-ai/schemastery`)由 DSH 宿主提供,无需单独安装。侧边栏 Git 面板需要额外安装可选插件 `dsh-better-sidebar`。
|
|
114
|
+
|
|
115
|
+
## 配置
|
|
116
|
+
|
|
117
|
+
在 DSH 设置 → 插件 → **git 分支管理**(Host 条目 `dsh-git-branch`)的配置卡中修改;所有字段即时生效并写回 profile 的 `cordis.patch.yml`。未知配置键会被直接拒绝(防止拼写错误被默认值掩盖)。
|
|
118
|
+
|
|
119
|
+
| 配置项 | 默认值 | 说明 |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `rootDir` | `$DSH_HOME/gitworktree`(即 `~/.dsh/gitworktree`) | worktree 存放根路径;必须是绝对路径 |
|
|
122
|
+
| `groupSidebar` | `true` | 侧边栏把同一仓库的 worktree 归入一个分组 |
|
|
123
|
+
| `fetchBeforeCreate` | `false` | 创建 worktree 前 fetch 所有远程(失败不阻断创建) |
|
|
124
|
+
| `autoPruneWorktrees` | `false` | 创建后惰性清理陈旧 worktree |
|
|
125
|
+
| `keepWorktrees` | `30` | 清理保留上限,整数且 `>= 1`,只统计有效的 git worktree |
|
|
126
|
+
| `postCreateCopyFiles` | `[]` | 无 `.worktreeinclude` 时,创建后拷贝的配置文件列表 |
|
|
127
|
+
| `cnAccelerate` | `false` | 国内加速:仅重写 fetch / update 读操作,**push 永不重写** |
|
|
128
|
+
| `cnAccelerateUrl` | `https://gh-proxy.com/` | 加速前缀,须为不含空白的 http(s) URL |
|
|
129
|
+
| `networkProxy` | 空 | 显式代理,`http(s)` / `socks5` URL,不允许携带凭据与路径 |
|
|
130
|
+
|
|
131
|
+
## 开发
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pnpm install # 需要 pnpm 11.8.0(packageManager 字段)
|
|
135
|
+
pnpm run build # 服务端:tsc -p tsconfig.json → lib/
|
|
136
|
+
pnpm run build:client # 客户端:tsdown → lib/client.js
|
|
137
|
+
pnpm run build:all # 上面两步
|
|
138
|
+
pnpm run watch:client # 客户端增量构建
|
|
139
|
+
pnpm run test # vitest run
|
|
140
|
+
pnpm run typecheck # tsc -p tsconfig.client.json --noEmit
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`prepublishOnly` 会依次执行 `typecheck` → `test` → `build:all`。
|
|
144
|
+
|
|
145
|
+
目录速览:
|
|
146
|
+
|
|
147
|
+
| 路径 | 内容 |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `src/index.ts` | host 半边:设置 schema 与校验、HTTP 路由挂载、AI 工具注册 |
|
|
150
|
+
| `src/client/` | 浏览器半边:分支 chip、Git 面板、设置卡、worktree 对话框 |
|
|
151
|
+
| `src/wire.ts` | 双端共享的路由常量与请求/响应类型(零依赖契约) |
|
|
152
|
+
| `src/git.ts` | git 执行器:参数拼装 + porcelain 解析,exec 注入式 seam |
|
|
153
|
+
| `src/credential-service.ts` | 凭据唯一入口(store / include / shell 环境) |
|
|
154
|
+
| `src/network*.ts` | 网络通道:加速、代理、IP 优选、探测与预算 |
|
|
155
|
+
| `tests/*.spec.ts` | vitest 用例(37 个 spec),含真实 git 端到端回归 `shell-bridge-e2e` |
|
|
156
|
+
| `locale/*.json` | 插件展示元数据(标题/描述)的中英文词典;界面文案在 `src/client/locales.ts` |
|
|
157
|
+
| `docs/decisions/` | 决策记录(ADR),非平凡变更须随同一提交更新 |
|
|
158
|
+
| `.github/workflows/` | CI 与 tag 触发的 npm 发布([决策 0010](./docs/decisions/0010-github-actions-ci-and-npm-release.md)) |
|
|
159
|
+
|
|
160
|
+
## 文档
|
|
161
|
+
|
|
162
|
+
- [CHANGELOG.md](./CHANGELOG.md) — 版本变更记录(语义化版本)
|
|
163
|
+
- [docs/decisions/README.md](./docs/decisions/README.md) — 决策记录索引与规范
|
|
164
|
+
- [docs/design/network-reliability.md](./docs/design/network-reliability.md)、[docs/design/cn-accelerate.md](./docs/design/cn-accelerate.md) — 网络可靠性与国内加速设计
|
|
165
|
+
- [notes/FIRST-SPEC.md](./notes/FIRST-SPEC.md)、[notes/GIT-PANEL-SPEC.md](./notes/GIT-PANEL-SPEC.md) — 初始需求与面板规格
|
|
166
|
+
|
|
167
|
+
## 许可
|
|
168
|
+
|
|
169
|
+
[MIT](./LICENSE) © GaoHu1997
|
package/lib/client.js
CHANGED
|
@@ -2619,12 +2619,13 @@ window.__ModuleLoader__.load({
|
|
|
2619
2619
|
* a fact-spelling confirm (uncommitted count, ahead commits, sessions to
|
|
2620
2620
|
* be archived — the branch itself survives; a directory holding a running
|
|
2621
2621
|
* session withholds the verb). On the main checkout's BLANK session the
|
|
2622
|
-
* group
|
|
2623
|
-
*
|
|
2624
|
-
*
|
|
2625
|
-
*
|
|
2626
|
-
* gains the main checkout row (see
|
|
2627
|
-
* started MAIN-checkout session drops
|
|
2622
|
+
* group is also the direct hop into a branch held by another worktree —
|
|
2623
|
+
* that branch now ALSO keeps its local-group row (it is the user's branch
|
|
2624
|
+
* and hiding it read as missing), while the hop row is the way to open a
|
|
2625
|
+
* session already on it without touching the checkout. A linked-worktree
|
|
2626
|
+
* session keeps that group and gains the main checkout row (see
|
|
2627
|
+
* {@link buildLinkedWorktreeRows}); a started MAIN-checkout session drops
|
|
2628
|
+
* the group — its directory is fixed.
|
|
2628
2629
|
*/
|
|
2629
2630
|
/**
|
|
2630
2631
|
* Read the repository status for a directory; refetch on demand.
|
|
@@ -2675,11 +2676,13 @@ window.__ModuleLoader__.load({
|
|
|
2675
2676
|
}
|
|
2676
2677
|
/**
|
|
2677
2678
|
* Build the branch rows for the picker on the MAIN checkout, in three
|
|
2678
|
-
* kinds:
|
|
2679
|
-
*
|
|
2680
|
-
*
|
|
2681
|
-
*
|
|
2682
|
-
*
|
|
2679
|
+
* kinds: EVERY local branch, REMOTE branches, and one WORKTREE row per
|
|
2680
|
+
* linked worktree. A local branch held by another worktree STAYS in the
|
|
2681
|
+
* local group: hiding it made the picker look like branches had gone
|
|
2682
|
+
* missing. It is still listed as a worktree row too (the direct session
|
|
2683
|
+
* hop, see the owner's onAdoptWorktree) — the local row keeps the branch
|
|
2684
|
+
* visible and named; checking it out in place stays git's call, which
|
|
2685
|
+
* refuses a branch already checked out elsewhere.
|
|
2683
2686
|
*
|
|
2684
2687
|
* The worktree group is "every worktree EXCEPT the one this session sits
|
|
2685
2688
|
* in" — here that is the main checkout itself (the session IS the main
|
|
@@ -2695,10 +2698,9 @@ window.__ModuleLoader__.load({
|
|
|
2695
2698
|
* "not usable in this directory".
|
|
2696
2699
|
*/
|
|
2697
2700
|
function buildBranchRows$1(branches, worktrees) {
|
|
2698
|
-
const held = new Set(worktrees.flatMap((w) => w.main || w.branch === void 0 ? [] : [w.branch]));
|
|
2699
2701
|
const localMap = new Map(branches.filter((b) => b.kind === "local").map((b) => [b.name, b]));
|
|
2700
2702
|
return [
|
|
2701
|
-
...branches.filter((b) => b.kind === "local"
|
|
2703
|
+
...branches.filter((b) => b.kind === "local").map((b) => ({
|
|
2702
2704
|
name: b.name,
|
|
2703
2705
|
kind: "local",
|
|
2704
2706
|
...b.ahead === void 0 ? {} : { ahead: b.ahead },
|
package/package.json
CHANGED