@minonl/dia 0.0.5 → 0.0.7
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/dist/cli.js +1818 -318
- package/docs/cli.md +1 -1
- package/docs/index.md +3 -0
- package/docs/msg-collab-m1-design.md +170 -0
- package/docs/msg-collab-m1.md +66 -0
- package/docs/worktree.md +106 -0
- package/package.json +1 -1
- package/packages/wt/bin/dia-wt +28 -21
- package/packages/wt/tests/adopt.test.sh +17 -14
package/docs/cli.md
CHANGED
|
@@ -100,7 +100,7 @@ dia persona remove dabo
|
|
|
100
100
|
| `update [--check]` | **已实现** | 查 npm `dist-tags`;`--check` 有新版 exit 1、领先/最新 exit 0;**按安装方式分流**:npm 全局包 → `npm i -g`;脚本安装 → 提示 `git pull && bun run build && bash install.sh`(避免装到不在 PATH 的 npm prefix) |
|
|
101
101
|
| `deps list / record` | **已实现** | `list` = 登记 + 实测派生视图(required 缺失 exit 2);`record` = 只追加一行(`--id/--kind/--version` 必填);`src/deps.ts` + `tests/deps*.test.ts` |
|
|
102
102
|
| `doctor` | **已实现** | 组合自检:persona verify / **persona.render(宿主产物只读漂移,warn + 修复命令)** / default_agent / kit / knowledge(**索引新鲜度三态**,过期给重建命令)/ dsh preset / **deps 登记校验** / **platform 版本(读 `docs/platforms/registry.json`)** / wt;exit 0/1/2。不指定 persona 且装了多个 → **逐个自检**(条目加 `<id>.` 前缀),不再整体 `E_SCOPE` |
|
|
103
|
-
| `wt …` | **已实现(需安装包)** | 转发 `packages/wt/bin/dia-wt
|
|
103
|
+
| `wt …` | **已实现(需安装包)** | 转发 `packages/wt/bin/dia-wt`(create/adopt/doctor/gate/list/status/check/clean/prune-merged;详见 [worktree.md](worktree.md));未装时 `E_WT`(exit 33),`install.sh` 会分发 |
|
|
104
104
|
| `kb index/search/query/get/lint/path/review` | **已实现** | persona 作用域下索引过期自动重建;显式 `--root` 保持「缺索引即报错」 |
|
|
105
105
|
| `kb <域命令>` | **已实现(委派)** | 未命中内置 → persona `commands` → 委派;argv 透传、exit code 原样 |
|
|
106
106
|
| `<persona-cmd>` / `cmd <name>` | **已实现** | 一级委派(内置优先);`dia cmd` 只查外部命令,可绕开同名内置 |
|
package/docs/index.md
CHANGED
|
@@ -23,6 +23,8 @@ Agent 入口:[`llms.txt`](https://github.com/minonl/dia/blob/main/llms.txt)(
|
|
|
23
23
|
| [dia monorepo:框架模块的归属](monorepo.md) | 结论:dia-job 回 dia monorepo,作为 packages/job;kb 作为 packages/kb。 |
|
|
24
24
|
| [dia msg M0:送达与可读(设计 v0.1)](msg-collab-m0-design.md) | 一句话:任意两个活会话互发消息,收件方无需用户手敲命令即可读到;正文持久、有 ack/reply/close。 |
|
|
25
25
|
| [dia msg M0 使用说明(送达与可读)](msg-collab-m0.md) | 一句话:任意两个活着的 dia 会话可以互发消息;收件方开工就能看到未读,正文不依赖临时文件。 |
|
|
26
|
+
| [dia msg M1:共享热点 claim + 任务依赖 + graph JSON(设计 v0.1)](msg-collab-m1-design.md) | 日期:2026-11-05 | 状态:设计(待实现) | 上游:docs/research/msg-collaboration-grill.md(C1–C3 / D1) |
|
|
27
|
+
| [dia claim:共享热点文件声明(M1a)](msg-collab-m1.md) | 一句话:在改共享文件前先 dia claim,让其它会话看见“谁在改、为什么、什么时候过期”;只告警,不阻断。 |
|
|
26
28
|
| [dia / yan / dabo 现状总览](overview.md) | 2026-11-03 实测。本条是三方合起来的「现在长什么样」;docs/status.md 管 dia 的阶段与证据, |
|
|
27
29
|
| [Patch:对已安装 persona / kit 的本地补丁](patching.md) | 参考 npm/patch-package,但只作用于本地安装副本,不改上游。 |
|
|
28
30
|
| [Persona / Kit 安装:用 dia 从 git 地址装](persona-install.md) | 命名已定:persona = 可被选中的 agent 定义,kit = 知识/技能/工具内容,profile = 宿主运行环境(dsh)。 |
|
|
@@ -35,6 +37,7 @@ Agent 入口:[`llms.txt`](https://github.com/minonl/dia/blob/main/llms.txt)(
|
|
|
35
37
|
| [团队章程:谁来做 dia(开发团队)](team.md) | 2026-10-28 升级:开发团队执行模式改为全自动循环(计划→实现→验证→对抗→修复),人只在验收阶段介入; |
|
|
36
38
|
| [版本与多会话(persona / kit)](versioning.md) | 问题:dia 要支持升级/本地迭代期间不同会话用不同版本 persona;kit 是否也要会话级版本? |
|
|
37
39
|
| [dia 长期定位:从"安装器"到 superagent / agent-team 工厂](vision.md) | 2026-11-03,方向性文档。它不是现状——现状看 overview.md 与 status.md; |
|
|
40
|
+
| [worktree 纪律(`dia wt`)](worktree.md) | 目标:把「任何 git 改动走 worktree」从靠人记变成脚本可断言。 |
|
|
38
41
|
|
|
39
42
|
## 平台与宿主
|
|
40
43
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# dia msg M1:共享热点 claim + 任务依赖 + graph JSON(设计 v0.1)
|
|
2
|
+
|
|
3
|
+
> 日期:2026-11-05 | 状态:**设计(待实现)** | 上游:`docs/research/msg-collaboration-grill.md`(C1–C3 / D1)
|
|
4
|
+
> 原则:** advisory,不阻断**;声明依赖与推断冲突分开;不做 daemon/TUI/全 worktree 锁。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 0. 目标与非目标
|
|
9
|
+
|
|
10
|
+
| | 内容 |
|
|
11
|
+
|---|---|
|
|
12
|
+
| **目标** | ① 共享热点文件 `claim`(谁在改、谁让路);② `dia task` 支持 owner / deps / touches / closure_reason;③ `dia graph --json` 输出任务/claim 依赖图,环检测。 |
|
|
13
|
+
| **非目标** | 全 worktree 单写者锁、自动改代码/合并、TUI/Web、跨机同步、daemon。 |
|
|
14
|
+
| **成功判据** | 两个会话 claim 同一路径时第二个看到持有者;`dia task` 能回答“谁负责/依赖谁/碰哪些文件”;`dia graph` 能画出依赖并检出环。 |
|
|
15
|
+
|
|
16
|
+
**为什么先做 claim 而不是全 worktree 锁**:agent 改文件不经过 dia,锁没有执行点;claim 是“可见的承诺 + 开工告警”,比假锁诚实。
|
|
17
|
+
|
|
18
|
+
### 0.1 开工闸门(planner 2026-11-05)
|
|
19
|
+
|
|
20
|
+
- M1 **以 M0 真实使用为前提**:两周内执行过 `dia in` 的会话 ≥30%,或至少有 3 个真实会话在用;未达则只定稿设计,不排实现。
|
|
21
|
+
- M1 拆两个 MR:**M1a claim**(本文件 §1)/ **M1b task+graph**(§2–§3);M1a 可先做,M1b 等 M1a 被真实使用。
|
|
22
|
+
- M1 不做:`dia in` 联动提示、`dia out --ref path=`、pre-commit/MR 强制点(这些留到 M2 或独立小 MR)。
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. 共享热点 claim
|
|
28
|
+
|
|
29
|
+
### 1.1 数据
|
|
30
|
+
|
|
31
|
+
`~/.dia/collab/claims.jsonl`,append-only;一行一个事件:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{"ts":"...","event":"claim","id":"c-...","path":"install.sh","scope":"repo:/Users/min/repos/github/dia",
|
|
35
|
+
"holder":"ses_...","reason":"发布拆分","expires_at":"...","created_at":"..."}
|
|
36
|
+
{"ts":"...","event":"release","id":"c-...","holder":"ses_..."}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `path`:相对 `scope` 的规范化路径(去 `./`、`..` 归一化,不允许越出 scope);scope 用 **repo slug**(如 `github.com/minonl/dia`)而不是绝对路径,避免把用户名/机器路径写进 JSONL;不在 git 仓时用 cwd basename。
|
|
40
|
+
- claim 默认 **4h 过期**(agent 会话通常 <1h;`--ttl` 可改);过期视为不活跃,但保留历史。
|
|
41
|
+
- 同一 `(scope,path)` 允许多个活跃 claim(可见即警告);不排队、不阻断;`--force` 可接管已过期/失效 holder。
|
|
42
|
+
- `release` 必须由 holder 或过期后执行;其他会话不能 unclaim 别人的活跃 claim。
|
|
43
|
+
- `claims.jsonl` 用 O_EXCL 锁 append(复用 collab 的 `withLock` 方案);超过 5000 行时提示 compact。
|
|
44
|
+
|
|
45
|
+
### 1.2 命令
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
dia claim <path> [--reason R] [--ttl 4h] [--force] [--session S] [--json]
|
|
49
|
+
dia claims [--all] [--mine] [--json] # 默认只列活跃
|
|
50
|
+
dia unclaim <path|id> [--force] [--session S]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `dia claim` 成功时打印**现有 holder**(包括自己);有他人持有 → 输出 `⚠ overlap: ...`,退出码 **0**(advisory)。
|
|
54
|
+
- 只有 `E_NO_SESSION` / 路径非法 / 参数错才非 0。
|
|
55
|
+
- `dia claims` 人类输出:`path holder reason expires`;`--json` 返回数组;`--mine` 只列当前会话。
|
|
56
|
+
|
|
57
|
+
### 1.3 不在 M1
|
|
58
|
+
|
|
59
|
+
- `dia in` 末尾追加 claim 提示、`dia out --ref path=`、pre-commit/MR 强制点:全部留到 M2;M1 只做 `claim/claims/unclaim` 命令。
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 2. `dia task` 扩展(不新建表)
|
|
64
|
+
|
|
65
|
+
在现有 `tasks/ledger.jsonl` 上扩展事件与字段,**保持旧数据可读**:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{"event":"add","id":"t-...","title":"...","role":"builder","judge":"...",
|
|
69
|
+
"owner":"ses_...","deps":["t-..."],"touches":["src/collab.ts"],"status":"todo"}
|
|
70
|
+
{"event":"link","id":"t-...","deps":["t-..."],"touches":["..."]}
|
|
71
|
+
{"event":"status","id":"t-...","status":"blocked","evidence":"...","blocked_reason":"..."}
|
|
72
|
+
{"event":"status","id":"t-...","status":"done","evidence":"...","closure_reason":"done"}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `Task` 新增可选:`owner?: string`、`deps: string[]`、`touches: string[]`、`closure_reason?: string`、`blocked_reason?: string`。
|
|
76
|
+
- `done` 仍要求 judge+evidence;`closure_reason` 是**元数据**(默认 `done`),不新增终态。
|
|
77
|
+
- 终态收敛:`done` = 执行完成;`accepted` = team/human 验收(现有 team 流程);两者不互相替代,`closure_reason` 只解释 `done/dropped` 的原因。
|
|
78
|
+
- 依赖未完成时允许 `doing`,但 `dia graph` 显示 `blocked_by` 派生信息;不自动改状态。
|
|
79
|
+
- `dia task list` 人类输出增加 `owner` 与 `deps` 数量;`--json` 全字段;旧任务 `deps/touches` 缺省为 `[]`。
|
|
80
|
+
|
|
81
|
+
### 2.1 命令
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
dia task add "标题" [--owner S] [--depends t-a,t-b] [--touches path1,path2] [--judge J]
|
|
85
|
+
dia task link <id> [--depends t-a,t-b] [--touches path1,path2]
|
|
86
|
+
dia task status <id> done --evidence E [--reason done|dropped|duplicate|escalated|no-follow-on]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. `dia graph`(先 JSON,后 UI)
|
|
92
|
+
|
|
93
|
+
### 3.1 数据契约
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"schema": 1,
|
|
98
|
+
"nodes": [
|
|
99
|
+
{"id":"task:t-1","kind":"task","label":"...","status":"doing","owner":"ses_..."},
|
|
100
|
+
{"id":"session:ses_...","kind":"session","label":"..."},
|
|
101
|
+
{"id":"path:src/collab.ts","kind":"path","label":"src/collab.ts"}
|
|
102
|
+
],
|
|
103
|
+
"edges": [
|
|
104
|
+
{"from":"task:t-2","to":"task:t-1","kind":"depends_on","source":"declared"},
|
|
105
|
+
{"from":"session:ses_...","to":"path:src/collab.ts","kind":"claims","source":"declared"},
|
|
106
|
+
{"from":"task:t-1","to":"path:src/collab.ts","kind":"touches","source":"declared"}
|
|
107
|
+
],
|
|
108
|
+
"cycles": [["task:t-1","task:t-2"]]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- 边类型:`depends_on`(declared)、`touches`(declared)、`claims`(declared);M1 **不把 `blocked_by` 作为边**,只在节点/人类输出里派生显示“被谁挡着”。
|
|
113
|
+
- **不把推断冲突画成事实**:M1 只有 declared 边;后续 `inferred` 必须带 `source: inferred` + `confidence`。
|
|
114
|
+
- 环检测:任务依赖图有环 → `cycles` 非空,命令退出码 **3**(2 留给参数错;仍输出 JSON)。
|
|
115
|
+
- `--task <id>` 只输出该任务的出向 `depends_on` + `touches` + 直接 claim 邻居;不做全图闭包。
|
|
116
|
+
|
|
117
|
+
### 3.2 命令
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
dia graph [--json] [--task t-...] [--session S]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- 默认输出全部 task + 活跃 claim + 相关 session/path;人类输出用缩进树(task → deps / touches)。
|
|
124
|
+
- `--task` 只输出该任务子图。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 4. 验收
|
|
129
|
+
|
|
130
|
+
| # | 判据 | 证据 |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| V1 | 两个会话 claim 同一路径:第二个看到持有者与 `⚠ overlap`,exit 0;claims.jsonl 有两条 claim | CLI 输出 + JSONL 行数 |
|
|
133
|
+
| V2 | `--ttl 1s` 后 sleep,`dia claims` 活跃列表为空;`unclaim` 后立即消失 | 真实验证 |
|
|
134
|
+
| V3 | `dia task add --depends/--touches` 落 ledger;旧 task(无新字段)仍可读,deps/touches 为 `[]` | 单测 |
|
|
135
|
+
| V4 | `done` 仍强制 judge+evidence;closure_reason 可查,且不改变 accepted 语义 | 单测 |
|
|
136
|
+
| V5 | `dia graph --json` 有 task/session/path 节点与 declared 边;两次运行 diff 为空(稳定契约) | JSON fixture 断言 |
|
|
137
|
+
| V6 | 依赖环返回 `cycles` 非空 + **exit 3** | 单测 |
|
|
138
|
+
| V7 | 无 session 时明确 `E_NO_SESSION`,exit 非 0 | CLI 断言 |
|
|
139
|
+
| V8 | claim 并发 append 20 次不丢行、不坏行 | 并发单测 |
|
|
140
|
+
| V9 | `dia graph --json` 契约稳定(fixture `tests/fixtures/graph.v1.json`) | 单测 |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 5. 实现拆分
|
|
145
|
+
|
|
146
|
+
### M1a(claim,可先做)
|
|
147
|
+
|
|
148
|
+
1. `src/claims.ts`:JSONL 读写 + `withLock` append、路径/scope 规范化、活跃过滤、TTL、`--force`。
|
|
149
|
+
2. `packages/cli/src/index.ts`:`claim / claims / unclaim`;help 同步。
|
|
150
|
+
3. `tests/claims.test.ts`:V1/V2/V7/V8。
|
|
151
|
+
|
|
152
|
+
### M1b(task + graph,等 M1a 被真实使用)
|
|
153
|
+
|
|
154
|
+
4. `src/task.ts`:fold 扩展 + `link` + `closure_reason`;保持旧事件兼容。
|
|
155
|
+
5. `src/graph.ts`:从 task/claims 构建节点/边/环;`--task` 子图。
|
|
156
|
+
6. `packages/cli/src/index.ts`:`task add/link` 新 flag;`graph`。
|
|
157
|
+
7. 测试:`tests/graph.test.ts`、扩展 `tests/task.test.ts`;V3–V6/V9。
|
|
158
|
+
8. 文档:`docs/msg-collab-m1.md` 使用说明;两批都要 `node scripts/docs-index.mjs`。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 6. 风险
|
|
163
|
+
|
|
164
|
+
| 风险 | 缓解 |
|
|
165
|
+
|---|---|
|
|
166
|
+
| claim 被误解为锁 | 文档/输出明确 “advisory,不阻断”;不做自动拒绝 |
|
|
167
|
+
| 路径规范化不一致 | 统一 `resolve` + repo root 前缀;拒绝越出 scope |
|
|
168
|
+
| 旧 ledger 事件不兼容 | fold 对未知事件忽略;新字段 optional |
|
|
169
|
+
| graph 变成“事实” | declared/inferred 分色;M1 只有 declared |
|
|
170
|
+
| 使用率低 | `dia claims` 先做“可见”,不做强制门禁 |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# dia claim:共享热点文件声明(M1a)
|
|
2
|
+
|
|
3
|
+
> 一句话:在改共享文件前先 `dia claim`,让其它会话看见“谁在改、为什么、什么时候过期”;只告警,不阻断。
|
|
4
|
+
> 设计:`docs/msg-collab-m1-design.md` §1
|
|
5
|
+
|
|
6
|
+
## 什么时候用
|
|
7
|
+
|
|
8
|
+
- 要改 `install.sh`、`AGENTS.md`、`launchd/`、`docs/index.md` 等**多个会话都可能碰**的文件;
|
|
9
|
+
- 交接/并行开发前,先声明路径,避免“改完才发现别人也在改”。
|
|
10
|
+
|
|
11
|
+
## 命令
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dia claim install.sh --reason "发布拆分" --ttl 4h
|
|
15
|
+
dia claims # 当前活跃 claim
|
|
16
|
+
dia claims --mine # 只看自己的
|
|
17
|
+
dia claims --all # 含已过期
|
|
18
|
+
dia unclaim install.sh # 释放自己的
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- claim 只在 `~/.dia/collab/claims.jsonl` 追加事实,**不锁文件、不排队、不阻断编辑**;
|
|
22
|
+
- 同一路径多人 claim 时,第二个会看到 `⚠ overlap: <holder>`,退出码仍是 0;
|
|
23
|
+
- 默认 4h 过期;`--ttl 30m` / `1d` 可调;
|
|
24
|
+
- 只能释放自己的 claim;`--force` 用于清理别人的过期 claim(明确的人工操作)。
|
|
25
|
+
|
|
26
|
+
## 路径与 scope
|
|
27
|
+
|
|
28
|
+
- scope 用仓库标识(如 `github.com/minonl/dia`),不写绝对路径;
|
|
29
|
+
- `path` 相对仓库根;`../` 越界会报 `E_CLAIM_PATH`;
|
|
30
|
+
- 不在 git 仓时用当前目录 basename,scope 为 `local:<dir>`。
|
|
31
|
+
|
|
32
|
+
## 边界
|
|
33
|
+
|
|
34
|
+
- 这是 **advisory**:不保证别人真的让路,也不自动改代码/合并;
|
|
35
|
+
- 不做全 worktree 锁;文件系统写操作仍以人工/MR 协调为准;
|
|
36
|
+
- M1a 不含 `dia in` 提示、pre-commit/MR 强制点(留到后续)。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
# dia task 依赖 / dia graph(M1b)
|
|
41
|
+
|
|
42
|
+
## 任务声明
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
dia task add "实现 X" --owner ses_... --depends t-a,t-b --touches src/x.ts,docs/x.md --judge "bun test"
|
|
46
|
+
dia task link t-xxx --depends t-new --touches src/y.ts
|
|
47
|
+
dia task link t-xxx --remove-depends t-old
|
|
48
|
+
dia task done t-xxx --evidence "bun test 通过" --reason done
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- `owner` / `deps` / `touches` 都是可选;旧 task 没有这些字段时自动为 `[]`。
|
|
52
|
+
- `done` 仍强制 judge + evidence;`--reason` 只记录 closure_reason,不新增终态。
|
|
53
|
+
- `pending`/`blocked` 用 `task status <id> blocked --blocked-reason ...`。
|
|
54
|
+
|
|
55
|
+
## 依赖图
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
dia graph --json
|
|
59
|
+
dia graph # 人类可读:task → deps + claims
|
|
60
|
+
dia graph --json --task t-xxx
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- 只输出 **declared** 边:`depends_on` / `touches` / `claims`;
|
|
64
|
+
- `blocked_by` 只在 task 节点里做派生提示,不是边;
|
|
65
|
+
- 任务依赖成环:`cycles` 非空,退出码 **3**(参数错仍是 2);
|
|
66
|
+
- `--task` 只输出该任务的直接依赖/touches/claim 邻居,不做全图闭包。
|
package/docs/worktree.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# worktree 纪律(`dia wt`)
|
|
2
|
+
|
|
3
|
+
> 目标:把「任何 git 改动走 worktree」从**靠人记**变成**脚本可断言**。
|
|
4
|
+
> 实现:`packages/wt/bin/dia-wt`(`install.sh` 分发到 `<install>/packages/wt`,`dia wt` 转发)。
|
|
5
|
+
|
|
6
|
+
## 1. 一条命令接管仓库
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
dia wt adopt <repo> # 默认 dry-run:打印计划,不落盘
|
|
10
|
+
dia wt adopt <repo> --apply # 写入配置 + 三个守卫钩子
|
|
11
|
+
dia wt adopt --all --apply # 批量:仓根下所有主 checkout(跳过 *-worktree--*)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
写入内容(幂等):
|
|
15
|
+
|
|
16
|
+
| 项 | 位置 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| 策略 | `<git-common-dir>/dia/config.json`(`defaultBranch` / `template` / `hooksPath` / `hooksPrevious` / `hooksChain`) |
|
|
19
|
+
| 钩子 | `<git-common-dir>/dia/hooks/{pre-commit,pre-push,post-checkout}` |
|
|
20
|
+
| `core.hooksPath` | 指向上面目录,**必须是绝对路径**(相对路径在 worktree 会静默失效) |
|
|
21
|
+
|
|
22
|
+
守卫行为:
|
|
23
|
+
|
|
24
|
+
| 钩子 | 规则 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `pre-commit` | 主 checkout 上、分支属于 protected(默认分支 + main/master/develop/dev)→ 拒绝 |
|
|
27
|
+
| `pre-push` | 主 checkout 上**任何 push**(含 tag / feature 分支)→ 拒绝;推 `main/master/develop/dev` → 拒绝 |
|
|
28
|
+
| `post-checkout` | 主 checkout 切到非默认分支 → 黄色提示(不阻断) |
|
|
29
|
+
|
|
30
|
+
**钩子链**:`hooksChain`(首次 adopt 前的 hooksPath,如 `.husky` / `.githooks`)→ `<common>/hooks` → 默认放行。
|
|
31
|
+
所以 husky / simple-git-hooks / lefthook 的钩子不会被吞。
|
|
32
|
+
|
|
33
|
+
> ⚠️ `simple-git-hooks` 的 `postinstall` 会重写 `core.hooksPath` 下的钩子。install 后若守卫被覆盖:
|
|
34
|
+
> `dia wt doctor --fix`(或重跑 `dia wt adopt --apply`)。
|
|
35
|
+
|
|
36
|
+
## 2. 巡检
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
dia wt doctor <repo>... # 单仓/多仓
|
|
40
|
+
dia wt doctor --all # 扫描仓根(<data>/repos、$DIA_WT_REPOS_ROOT,默认 ~/repos)
|
|
41
|
+
dia wt doctor --all --fix # 无损修复:重装三个钩子 + 校正 hooksPath(不删分支/不动 worktree/不提交)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
判据(FAIL 会让进程 exit 1):
|
|
45
|
+
|
|
46
|
+
| ID | 断言 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| D01 | 已 adopt(`config.json` 存在) |
|
|
49
|
+
| D02 | `core.hooksPath` 绝对路径,三个钩子可执行 |
|
|
50
|
+
| D03 | 钩子静态包含默认分支(`prot=`),动态探针能拦住主 checkout 的默认分支提交 |
|
|
51
|
+
| D04 | 主 checkout 干净且位于默认分支(WARN) |
|
|
52
|
+
| D05 | worktree 路径都存在 |
|
|
53
|
+
| D06 | 无 prunable / locked |
|
|
54
|
+
| D07 | 同一分支不被多个 worktree 占用 |
|
|
55
|
+
| D11 | 默认分支可解析(config > origin/HEAD(校验仍存在)> main > master) |
|
|
56
|
+
|
|
57
|
+
挂成日巡检(mac launchd / linux cron 由 `dia job` 统一):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
dia job install dia-wt-doctor --calendar 09:00 \
|
|
61
|
+
--cmd "$HOME/.local/bin/dia wt doctor --all --json" \
|
|
62
|
+
--desc "worktree 纪律巡检" --allow-unstable
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 3. 提交 MR 前:`gate`
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
dia wt gate <repo> <feature>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
顺序:`check`(依赖体检 R20)→ `doctor` → 目标 worktree 无未提交改动 → `ahead origin/<default> > 0`。
|
|
72
|
+
**只做本地门禁,不做 merge / push**——合并仍走 MR + `@bot mergeme`。
|
|
73
|
+
|
|
74
|
+
## 4. 日常命令
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
dia wt create <repo> <feature> [--base main] [--install] [--dry-run]
|
|
78
|
+
dia wt list [<repo>]
|
|
79
|
+
dia wt status [<repo>] [--stale-days N]
|
|
80
|
+
dia wt check [<repo>...] [--all] [--deep]
|
|
81
|
+
dia wt clean <repo> <feature|--branch B> [--force] [--keep-branch]
|
|
82
|
+
dia wt prune-merged [<repo>] [--all] [--apply]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
路径模板默认 `{{parent}}/{{repo}}-worktree--{{feature}}`(与历史布局一致,零迁移);
|
|
86
|
+
`adopt --template` 可改(支持 `{{parent}}` `{{repo}}` `{{feature}}` `{{wtRoot}}`),`create/list/clean` 都按它解析。
|
|
87
|
+
|
|
88
|
+
## 5. 退出码
|
|
89
|
+
|
|
90
|
+
| 码 | 含义 |
|
|
91
|
+
|---|---|
|
|
92
|
+
| 0 | 干净 / 成功 |
|
|
93
|
+
| 1 | 违规或漂移(doctor FAIL、gate 未通过、check 有违规) |
|
|
94
|
+
| 2 | 用法错误(含 `--all` 未匹配到仓库) |
|
|
95
|
+
| 3 | 未 adopt |
|
|
96
|
+
| 4 | worktree 歧义(多个候选) |
|
|
97
|
+
|
|
98
|
+
## 6. 没有逃生口:钩子慢就优化
|
|
99
|
+
|
|
100
|
+
本仓**不提供**跳过钩子的开关,也不在文档/脚本里给绕过示例:
|
|
101
|
+
|
|
102
|
+
- 钩子执行慢 → **先量**(`time git commit` / `time git push`),定位到具体钩子;
|
|
103
|
+
- 优化手段:缓存(如 lint 增量、`nx affected`)、把重活下沉到 CI、减少扫描范围;
|
|
104
|
+
- 优化前宁可短暂变慢,也不跳过——一次跳过等于把质量门禁变成装饰。
|
|
105
|
+
|
|
106
|
+
自动化脚本(release 等)同理:**必须在 worktree 里跑**,不得在主 checkout 上做提交/推送。
|
package/package.json
CHANGED
package/packages/wt/bin/dia-wt
CHANGED
|
@@ -215,6 +215,31 @@ JS
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
# adopt 写入的路径模板(<git-common-dir>/dia/config.json 的 template);未 adopt → 空
|
|
218
|
+
# 仓根集合:<data>/repos + DIA_WT_REPOS_ROOTS(冒号分隔;默认 ~/repos + ~/.yan-agent/repos)
|
|
219
|
+
wt_repo_roots() {
|
|
220
|
+
local dr r
|
|
221
|
+
local -a out=()
|
|
222
|
+
dr="$(dia_wt_data_root 2>/dev/null || true)"
|
|
223
|
+
[ -n "$dr" ] && [ -d "$dr/repos" ] && out+=("$dr/repos")
|
|
224
|
+
local rr="${DIA_WT_REPOS_ROOTS:-${DIA_WT_REPOS_ROOT:-$HOME/repos:$HOME/.yan-agent/repos}}"
|
|
225
|
+
while IFS= read -r r; do [ -n "$r" ] && [ -d "$r" ] && out+=("$r"); done < <(printf '%s\n' "$rr" | tr ':' '\n')
|
|
226
|
+
[ ${#out[@]} -gt 0 ] && printf '%s\n' "${out[@]}"
|
|
227
|
+
return 0
|
|
228
|
+
}
|
|
229
|
+
# 所有主 checkout(git-dir == common-dir);跳过 *-worktree--*
|
|
230
|
+
wt_discover_main_checkouts() {
|
|
231
|
+
local root sub gd cd_
|
|
232
|
+
while IFS= read -r root; do
|
|
233
|
+
while IFS= read -r -d '' gd; do
|
|
234
|
+
sub="$(dirname "$gd")"
|
|
235
|
+
case "$(basename "$sub")" in *-worktree--*) continue ;; esac
|
|
236
|
+
cd_="$(git -C "$sub" rev-parse --git-common-dir 2>/dev/null || true)"
|
|
237
|
+
gd="$(cd "$sub" 2>/dev/null && cd "$gd" 2>/dev/null && pwd -P)"
|
|
238
|
+
cd_="$(cd "$sub" 2>/dev/null && cd "$cd_" 2>/dev/null && pwd -P)"
|
|
239
|
+
[ -n "$gd" ] && [ "$gd" = "$cd_" ] && printf '%s\n' "$sub"
|
|
240
|
+
done < <(find "$root" -maxdepth 3 -name .git -type d -print0 2>/dev/null)
|
|
241
|
+
done < <(wt_repo_roots)
|
|
242
|
+
}
|
|
218
243
|
wt_repo_template() {
|
|
219
244
|
local repo="$1" common cfg
|
|
220
245
|
common="$(wt_common_dir "$repo" 2>/dev/null)" || return 1
|
|
@@ -1259,26 +1284,8 @@ cmd_adopt() {
|
|
|
1259
1284
|
esac; done
|
|
1260
1285
|
if [ "$all" = 1 ]; then
|
|
1261
1286
|
[ -z "$repo" ] || die "--all 不能与 <repo> 同时给(要么单个仓,要么全量)"
|
|
1262
|
-
local -a
|
|
1263
|
-
|
|
1264
|
-
dr="$(dia_wt_data_root 2>/dev/null || true)"
|
|
1265
|
-
rr="${DIA_WT_REPOS_ROOT:-$HOME/repos}"
|
|
1266
|
-
[ -n "$dr" ] && [ -d "$dr/repos" ] && roots+=("$dr/repos")
|
|
1267
|
-
[ -d "$rr" ] && roots+=("$rr")
|
|
1268
|
-
[ ${#roots[@]} -gt 0 ] || { echo "usage: dia wt adopt --all(未找到仓根:${rr})" >&2; exit 2; }
|
|
1269
|
-
local root sub gd cd_
|
|
1270
|
-
for root in "${roots[@]}"; do
|
|
1271
|
-
while IFS= read -r -d '' gd; do
|
|
1272
|
-
sub="$(dirname "$gd")"
|
|
1273
|
-
case "$(basename "$sub")" in *-worktree--*) continue ;; esac
|
|
1274
|
-
cd_="$(git -C "$sub" rev-parse --git-common-dir 2>/dev/null || true)"
|
|
1275
|
-
# 统一物理路径(macOS /tmp → /private/tmp),否则等式比较会假失败
|
|
1276
|
-
gd="$(cd "$sub" 2>/dev/null && cd "$gd" 2>/dev/null && pwd -P)"
|
|
1277
|
-
cd_="$(cd "$sub" 2>/dev/null && cd "$cd_" 2>/dev/null && pwd -P)"
|
|
1278
|
-
[ -n "$gd" ] && [ "$gd" = "$cd_" ] || continue # 只处理主 checkout
|
|
1279
|
-
repos+=("$sub")
|
|
1280
|
-
done < <(find "$root" -maxdepth 3 -name .git -type d -print0 2>/dev/null)
|
|
1281
|
-
done
|
|
1287
|
+
local -a repos=()
|
|
1288
|
+
while IFS= read -r r; do repos+=("$r"); done < <(wt_discover_main_checkouts)
|
|
1282
1289
|
[ ${#repos[@]} -gt 0 ] || { echo "usage: dia wt adopt --all(未匹配到任何主 checkout)" >&2; exit 2; }
|
|
1283
1290
|
local -a pass=()
|
|
1284
1291
|
local a
|
|
@@ -1400,7 +1407,7 @@ cmd_doctor() {
|
|
|
1400
1407
|
*) repos+=("$1"); shift ;;
|
|
1401
1408
|
esac; done
|
|
1402
1409
|
if [ "$all" = 1 ] && [ ${#repos[@]} -eq 0 ]; then
|
|
1403
|
-
while IFS= read -r
|
|
1410
|
+
while IFS= read -r d; do repos+=("$d"); done < <(wt_discover_main_checkouts)
|
|
1404
1411
|
fi
|
|
1405
1412
|
if [ ${#repos[@]} -eq 0 ]; then
|
|
1406
1413
|
echo "usage: dia wt doctor <repo>... | --all(--all 未匹配到任何仓库)" >&2
|
|
@@ -10,7 +10,7 @@ ok(){ echo " ✓ $1"; PASS=$((PASS+1)); }; bad(){ echo " ✗ $1"; FAIL=$((FAIL
|
|
|
10
10
|
|
|
11
11
|
R="$TD/repo"; mkdir -p "$R"; git -C "$R" init -q -b main
|
|
12
12
|
git -C "$R" config user.email t@example.com; git -C "$R" config user.name t
|
|
13
|
-
echo a > "$R/a.txt"; git -C "$R" add -A; git -C "$R" commit -qm init
|
|
13
|
+
echo a > "$R/a.txt"; git -C "$R" add -A; git -C "$R" commit -qm init
|
|
14
14
|
|
|
15
15
|
out="$(bash "$WT" adopt "$R" --json)"
|
|
16
16
|
printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "dry-run 识别默认分支" || bad "dry-run: $out"
|
|
@@ -59,7 +59,7 @@ bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
|
|
|
59
59
|
# ── 原 hooksPath 保留 + 二次 apply 不改写 hooksPrevious(review P2-7)──────────
|
|
60
60
|
R2="$TD/repo2"; mkdir -p "$R2" "$TD/orighooks"; git -C "$R2" init -q -b main
|
|
61
61
|
git -C "$R2" config user.email t@example.com; git -C "$R2" config user.name t
|
|
62
|
-
echo a > "$R2/a.txt"; git -C "$R2" add -A; git -C "$R2" commit -qm init
|
|
62
|
+
echo a > "$R2/a.txt"; git -C "$R2" add -A; git -C "$R2" commit -qm init
|
|
63
63
|
printf '#!/bin/sh\nexit 0\n' > "$TD/orighooks/pre-commit"; chmod +x "$TD/orighooks/pre-commit"
|
|
64
64
|
git -C "$R2" config --local core.hooksPath "$TD/orighooks"
|
|
65
65
|
bash "$WT" adopt "$R2" --apply >/dev/null 2>&1 || bad "R2 apply 失败"
|
|
@@ -80,7 +80,7 @@ git -C "$R2" config --local core.hooksPath "$R2/.git/dia/hooks"; bash "$WT" adop
|
|
|
80
80
|
# ── 自定义默认分支受保护(review P1-1 + P1-2)────────────────────────────────
|
|
81
81
|
R3="$TD/repo3"; mkdir -p "$R3"; git -C "$R3" init -q -b trunk
|
|
82
82
|
git -C "$R3" config user.email t@example.com; git -C "$R3" config user.name t
|
|
83
|
-
echo a > "$R3/a.txt"; git -C "$R3" add -A; git -C "$R3" commit -qm init
|
|
83
|
+
echo a > "$R3/a.txt"; git -C "$R3" add -A; git -C "$R3" commit -qm init
|
|
84
84
|
bash "$WT" adopt "$R3" --apply --default-branch trunk >/dev/null 2>&1 || bad "自定义分支 apply 失败"
|
|
85
85
|
bash "$WT" adopt "$R3" --check >/dev/null 2>&1 && ok "自定义默认分支 check 通过" || bad "自定义默认分支 check 失败"
|
|
86
86
|
bash "$WT" doctor "$R3" >/dev/null 2>&1 && ok "自定义默认分支 doctor 通过" || bad "自定义默认分支 doctor 失败"
|
|
@@ -92,7 +92,7 @@ git -C "$R3" reset -q
|
|
|
92
92
|
bash "$WT" doctor "$R3" >/dev/null 2>&1 || true
|
|
93
93
|
R4="$TD/repo4"; mkdir -p "$R4"; git -C "$R4" init -q -b main
|
|
94
94
|
git -C "$R4" config user.email t@example.com; git -C "$R4" config user.name t
|
|
95
|
-
echo a > "$R4/a.txt"; git -C "$R4" add -A; git -C "$R4" commit -qm init
|
|
95
|
+
echo a > "$R4/a.txt"; git -C "$R4" add -A; git -C "$R4" commit -qm init
|
|
96
96
|
if bash "$WT" doctor "$R4" >/dev/null 2>&1; then bad "未 adopt 的仓 doctor 误报通过"; else ok "未 adopt 的仓 doctor 失败"; fi
|
|
97
97
|
bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" || bad "doctor 无参数未 exit 2"
|
|
98
98
|
|
|
@@ -100,17 +100,18 @@ bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" ||
|
|
|
100
100
|
R5="$TD/repo5"; ORIGIN5="$TD/origin5.git"; git init -q --bare "$ORIGIN5"
|
|
101
101
|
mkdir -p "$R5"; git -C "$R5" init -q -b main
|
|
102
102
|
git -C "$R5" config user.email t@example.com; git -C "$R5" config user.name t
|
|
103
|
-
echo a > "$R5/a.txt"; git -C "$R5" add -A; git -C "$R5" commit -qm init
|
|
104
|
-
git -C "$R5" remote add origin "$ORIGIN5"; git -C "$R5" push -q
|
|
103
|
+
echo a > "$R5/a.txt"; git -C "$R5" add -A; git -C "$R5" commit -qm init
|
|
104
|
+
git -C "$R5" remote add origin "$ORIGIN5"; git -C "$R5" push -q origin main
|
|
105
105
|
git -C "$R5" fetch -q origin
|
|
106
106
|
git -C "$R5" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
|
|
107
107
|
bash "$WT" adopt "$R5" --apply >/dev/null 2>&1 || bad "R5 apply 失败"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
git -C "$R5"
|
|
108
|
+
# 主 checkout 的 pre-push 拒绝的是「从这里发起的任何 push」(含 feature 分支),不需要脏提交
|
|
109
|
+
git -C "$R5" branch feat/push-probe origin/main
|
|
110
|
+
if git -C "$R5" push origin feat/push-probe >/dev/null 2>&1; then bad "主 checkout push 未被拦截"; else ok "主 checkout push 被拦截"; fi
|
|
111
|
+
git -C "$R5" branch -D feat/push-probe >/dev/null 2>&1 || true
|
|
111
112
|
git -C "$R5" worktree add -q -b feat/x "$TD/repo5-worktree--x" main
|
|
112
113
|
echo b > "$TD/repo5-worktree--x/b.txt"; git -C "$TD/repo5-worktree--x" add b.txt
|
|
113
|
-
git -C "$TD/repo5-worktree--x" commit -qm feat-x
|
|
114
|
+
git -C "$TD/repo5-worktree--x" commit -qm feat-x 2>/dev/null
|
|
114
115
|
if git -C "$TD/repo5-worktree--x" push -q origin feat/x >/dev/null 2>&1; then ok "worktree push 放行"; else bad "worktree push 被误拦"; fi
|
|
115
116
|
|
|
116
117
|
# ── template 生效(P2-6):adopt 后 create 按 config.template 推导路径 ─────────
|
|
@@ -124,7 +125,7 @@ bash "$WT" adopt "$R5" --apply >/dev/null 2>&1
|
|
|
124
125
|
# ── 过期 origin/HEAD(远端分支已删)不误判默认分支 ──────────────────────────
|
|
125
126
|
R6="$TD/repo6"; mkdir -p "$R6"; git -C "$R6" init -q -b main
|
|
126
127
|
git -C "$R6" config user.email t@example.com; git -C "$R6" config user.name t
|
|
127
|
-
echo a > "$R6/a.txt"; git -C "$R6" add -A; git -C "$R6" commit -qm init
|
|
128
|
+
echo a > "$R6/a.txt"; git -C "$R6" add -A; git -C "$R6" commit -qm init
|
|
128
129
|
git -C "$R6" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/gone
|
|
129
130
|
out="$(bash "$WT" adopt "$R6" --json)"
|
|
130
131
|
printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "过期 origin/HEAD 回落到 main" || bad "过期 origin/HEAD: $out"
|
|
@@ -134,7 +135,7 @@ R7="$TD/repo7"; mkdir -p "$R7/.githooks"; git -C "$R7" init -q -b main
|
|
|
134
135
|
git -C "$R7" config user.email t@example.com; git -C "$R7" config user.name t
|
|
135
136
|
printf '#!/bin/sh\necho chained >> "$(git rev-parse --show-toplevel)/chain.log"\n' > "$R7/.githooks/pre-commit"
|
|
136
137
|
chmod +x "$R7/.githooks/pre-commit"
|
|
137
|
-
echo a > "$R7/a.txt"; git -C "$R7" add -A; git -C "$R7" commit -qm init
|
|
138
|
+
echo a > "$R7/a.txt"; git -C "$R7" add -A; git -C "$R7" commit -qm init
|
|
138
139
|
git -C "$R7" config --local core.hooksPath .githooks
|
|
139
140
|
bash "$WT" adopt "$R7" --apply >/dev/null 2>&1 || bad "R7 apply 失败"
|
|
140
141
|
python3 -c 'import json,sys;d=json.load(open(sys.argv[1]));sys.exit(0 if d.get("hooksChain")==".githooks" else 1)' "$R7/.git/dia/config.json" \
|
|
@@ -151,7 +152,7 @@ AR="$TD/allroot"; mkdir -p "$AR/a" "$AR/b"
|
|
|
151
152
|
for r in a b; do
|
|
152
153
|
git -C "$AR/$r" init -q -b main
|
|
153
154
|
git -C "$AR/$r" config user.email t@example.com; git -C "$AR/$r" config user.name t
|
|
154
|
-
echo x > "$AR/$r/x.txt"; git -C "$AR/$r" add -A; git -C "$AR/$r" commit -qm init
|
|
155
|
+
echo x > "$AR/$r/x.txt"; git -C "$AR/$r" add -A; git -C "$AR/$r" commit -qm init
|
|
155
156
|
done
|
|
156
157
|
git -C "$AR/a" worktree add -q -b feat/w "$AR/a-worktree--w" main
|
|
157
158
|
out="$(DIA_WT_REPOS_ROOT="$AR" DIA_WT_DATA_ROOT="$TD/dr2" bash "$WT" adopt --all --apply --json 2>&1)"
|
|
@@ -160,13 +161,15 @@ for r in a b; do
|
|
|
160
161
|
[ -n "$(git -C "$AR/$r" config --get core.hooksPath || true)" ] && ok "adopt --all: $r 已配 hooksPath" || bad "adopt --all: $r 未配置"
|
|
161
162
|
done
|
|
162
163
|
[ ! -d "$AR/a-worktree--w/.git/dia" ] && ok "adopt --all 跳过 worktree 目录" || bad "adopt --all 误改 worktree"
|
|
164
|
+
out="$(DIA_WT_REPOS_ROOT="$AR" DIA_WT_DATA_ROOT="$TD/dr4" bash "$WT" doctor --all --json 2>/dev/null)"
|
|
165
|
+
printf '%s' "$out" | grep -q "$AR/a" && ok "doctor --all 覆盖 DIA_WT_REPOS_ROOT" || bad "doctor --all 未覆盖自定义仓根"
|
|
163
166
|
DIA_WT_REPOS_ROOT="$TD/empty-root" DIA_WT_DATA_ROOT="$TD/dr3" bash "$WT" adopt --all >/dev/null 2>&1
|
|
164
167
|
[ $? = 2 ] && ok "adopt --all 空结果 exit 2" || bad "adopt --all 空结果未 exit 2"
|
|
165
168
|
|
|
166
169
|
# ── doctor --fix:钩子被覆盖后能无损重建 ────────────────────────────────────
|
|
167
170
|
R8="$TD/repo8"; mkdir -p "$R8"; git -C "$R8" init -q -b main
|
|
168
171
|
git -C "$R8" config user.email t@example.com; git -C "$R8" config user.name t
|
|
169
|
-
echo a > "$R8/a.txt"; git -C "$R8" add -A; git -C "$R8" commit -qm init
|
|
172
|
+
echo a > "$R8/a.txt"; git -C "$R8" add -A; git -C "$R8" commit -qm init
|
|
170
173
|
bash "$WT" adopt "$R8" --apply >/dev/null 2>&1
|
|
171
174
|
rm -f "$R8/.git/dia/hooks/post-checkout"
|
|
172
175
|
printf '#!/bin/sh\nexit 0\n' > "$R8/.git/dia/hooks/pre-commit"; chmod +x "$R8/.git/dia/hooks/pre-commit"
|