@minonl/dia 0.0.8 → 0.0.10

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.
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # adopt / doctor / gate 回归测试(2026-11-05)
3
- # 覆盖:dry-run 计划、apply 幂等、check 通过、主 checkout 守卫、doctor、revert。
2
+ # adopt / doctor / gate 回归测试(2026-11-06:每仓本地钩子 + simple-git-hooks RC)
4
3
  set -uo pipefail
5
4
  HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
5
  WT="$HERE/../bin/dia-wt"
@@ -8,26 +7,31 @@ TD="$(mktemp -d)"; trap 'rm -rf "$TD"' EXIT
8
7
  PASS=0; FAIL=0
9
8
  ok(){ echo " ✓ $1"; PASS=$((PASS+1)); }; bad(){ echo " ✗ $1"; FAIL=$((FAIL+1)); }
10
9
 
11
- R="$TD/repo"; mkdir -p "$R"; git -C "$R" init -q -b main
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
10
+ # 隔离 HOME / 全局 git 配置 / 数据根 / shell rc(避免污染真实机器)
11
+ export HOME="$TD/home"; mkdir -p "$HOME"
12
+ export GIT_CONFIG_GLOBAL="$TD/gitconfig"; : > "$GIT_CONFIG_GLOBAL"
13
+ export GIT_CONFIG_NOSYSTEM=1
14
+ export DIA_ROOT="$TD/dia"
15
+ export DIA_WT_SHELL_RC="$HOME/.zshenv"
14
16
 
17
+ mk_repo(){ local r="$1"; mkdir -p "$r"; git -C "$r" init -q -b main; git -C "$r" config user.email t@example.com; git -C "$r" config user.name t; echo a > "$r/a.txt"; git -C "$r" add -A; git -C "$r" commit -qm init; }
18
+
19
+ R="$TD/repo"; mk_repo "$R"
15
20
  out="$(bash "$WT" adopt "$R" --json)"
16
21
  printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "dry-run 识别默认分支" || bad "dry-run: $out"
17
22
  [ ! -f "$R/.git/dia/config.json" ] && ok "dry-run 不落盘" || bad "dry-run 落盘了"
18
23
 
19
24
  bash "$WT" adopt "$R" --apply >/dev/null 2>&1 || bad "apply 失败"
20
25
  [ -f "$R/.git/dia/config.json" ] && ok "config 已写" || bad "缺 config"
21
- hp="$(git -C "$R" config --get core.hooksPath)"
22
- case "$hp" in /*) ok "hooksPath 是绝对路径" ;; *) bad "hooksPath=$hp" ;; esac
23
- [ -x "$R/.git/dia/hooks/pre-commit" ] && ok "pre-commit 可执行" || bad "缺 pre-commit"
24
-
25
- md5_before="$(md5 -q "$R/.git/dia/config.json" 2>/dev/null || md5sum "$R/.git/dia/config.json" | cut -d' ' -f1)"
26
- sleep 1
27
- bash "$WT" adopt "$R" --apply >/dev/null 2>&1
28
- md5_after="$(md5 -q "$R/.git/dia/config.json" 2>/dev/null || md5sum "$R/.git/dia/config.json" | cut -d' ' -f1)"
29
- [ "$md5_before" != "$md5_after" ] && ok "重复 apply 会刷新时间戳(幂等语义:不重复建目录/钩子)" || bad "重复 apply 无变化"
30
-
26
+ hp="$(git -C "$R" config --local --get core.hooksPath)"
27
+ case "$hp" in */repo/.git/dia/hooks) ok "仓库本地 hooksPath 指向 dia 目录" ;; *) bad "hooksPath=$hp" ;; esac
28
+ for h in pre-commit pre-push post-checkout; do [ -x "$R/.git/dia/hooks/$h" ] || bad "缺 $h"; done
29
+ ok "三个钩子可执行"
30
+ python3 -c 'import json,sys;d=json.load(open(sys.argv[1]));sys.exit(0 if d.get("hooksManager")=="local" else 1)' "$R/.git/dia/config.json" \
31
+ && ok "config 记录 hooksManager=local" || bad "hooksManager 未记录"
32
+ [ -z "$(git config --global --get core.hooksPath 2>/dev/null || true)" ] && ok "未写全局 git 配置" || bad "仍写了全局 hooksPath"
33
+
34
+ bash "$WT" adopt "$R" --apply >/dev/null 2>&1 && ok "重复 apply 幂等" || bad "重复 apply 失败"
31
35
  bash "$WT" adopt "$R" --check >/dev/null 2>&1 && ok "check 通过" || bad "check 失败"
32
36
  bash "$WT" doctor "$R" >/dev/null 2>&1 && ok "doctor 通过" || bad "doctor 失败"
33
37
 
@@ -35,155 +39,77 @@ echo b > "$R/b.txt"; git -C "$R" add b.txt
35
39
  if git -C "$R" commit -qm should-block 2>/dev/null; then bad "主 checkout main 提交未被拦截"; else ok "主 checkout main 提交被拦截"; fi
36
40
  git -C "$R" reset -q
37
41
 
38
- # ── 推送前敏感扫描(PII/凭据形态)──
42
+ # 链式:仓库自己的 .git/hooks/pre-commit 仍要执行
43
+ printf '#!/bin/sh\necho chained >> "$(git rev-parse --show-toplevel)/chain.log"\n' > "$R/.git/hooks/pre-commit"; chmod +x "$R/.git/hooks/pre-commit"
44
+ git -C "$R" worktree add -q -b feat/y "$TD/repo-worktree--y" main
45
+ echo c > "$TD/repo-worktree--y/c.txt"; git -C "$TD/repo-worktree--y" add c.txt
46
+ git -C "$TD/repo-worktree--y" commit -qm feat-y >/dev/null 2>&1
47
+ [ -f "$TD/repo-worktree--y/chain.log" ] && ok "仓库自己的钩子被链式执行" || bad "仓库钩子未执行"
48
+ rm -f "$TD/repo-worktree--y/chain.log"
49
+
50
+ # simple-git-hooks 仓:走 RC 注入,不碰仓内文件与 .git/hooks
51
+ RN="$TD/npmrepo"; mk_repo "$RN"
52
+ printf '{"name":"r","devDependencies":{"simple-git-hooks":"^2.11.1"},"simple-git-hooks":{"pre-commit":"npx lint-staged"}}\n' > "$RN/package.json"
53
+ git -C "$RN" add -A; git -C "$RN" commit -qm pkg
54
+ before="$(md5 -q "$RN/package.json" 2>/dev/null || md5sum "$RN/package.json" | cut -d' ' -f1)"
55
+ bash "$WT" adopt "$RN" --apply >/dev/null 2>&1 || bad "npm 仓 adopt 失败"
56
+ after="$(md5 -q "$RN/package.json" 2>/dev/null || md5sum "$RN/package.json" | cut -d' ' -f1)"
57
+ [ "$before" = "$after" ] && ok "不修改仓库 package.json" || bad "package.json 被改动"
58
+ [ -z "$(git -C "$RN" config --local --get core.hooksPath || true)" ] && ok "不占用仓库 hooksPath(交给管理器)" || bad "仍设置了 hooksPath"
59
+ [ -f "$DIA_ROOT/hooks/simple-git-hooks-rc.sh" ] && ok "写入 simple-git-hooks RC" || bad "缺 RC 文件"
60
+ grep -q "SIMPLE_GIT_HOOKS_RC" "$HOME/.zshenv" 2>/dev/null && ok "shell rc 注入 SIMPLE_GIT_HOOKS_RC" || bad "未注入环境变量"
61
+ bash "$WT" doctor "$RN" >/dev/null 2>&1 && ok "npm 仓 doctor 通过" || bad "npm 仓 doctor 失败"
62
+ (cd "$RN" && DIA_WT_GUARD_PROBE=1 bash "$WT" guard pre-commit >/dev/null 2>&1) && bad "guard 未拦截 main 提交" || ok "guard 拦截 main 提交"
63
+
64
+ # 全局配置残留检查:旧版全局钩子目录 → doctor FAIL
65
+ LEGACY="$TD/legacy-hooks"; mkdir -p "$LEGACY"
66
+ printf '#!/usr/bin/env bash\n# dia-wt guard(旧)\nexit 0\n' > "$LEGACY/pre-commit"; chmod +x "$LEGACY/pre-commit"
67
+ git config --global core.hooksPath "$LEGACY"
68
+ bash "$WT" doctor "$R" >/tmp/d20.log 2>&1; grep -q "D20" /tmp/d20.log && ok "doctor 报全局钩子残留" || bad "doctor 未报全局残留: $(head -3 /tmp/d20.log | tr "\n" " ")"
69
+ git config --global --unset core.hooksPath
70
+
71
+ # 未 adopt 的仓不受影响
72
+ R3="$TD/repo3"; mk_repo "$R3"; echo z > "$R3/z.txt"; git -C "$R3" add -A
73
+ git -C "$R3" commit -qm ok >/dev/null 2>&1 && ok "未 adopt 仓不受守卫影响" || bad "未 adopt 仓被误拦"
74
+
75
+ # 敏感扫描(worktree push)
39
76
  BR="$TD/remote.git"; git init -q --bare "$BR"
40
77
  git -C "$R" remote add origin "$BR" 2>/dev/null || true
41
78
  git --git-dir="$BR" fetch -q "$R" main:main 2>/dev/null || true
42
- git -C "$R" fetch -q origin 2>/dev/null || true
43
- git -C "$R" worktree add -q "$TD/wt" -b feat/scan 2>/dev/null || true
44
- BAD_HOME="/Users"; echo "$BAD_HOME/alice/secret" > "$TD/wt/bad.txt"
45
- git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm bad
46
- if git -C "$TD/wt" push -q origin feat/scan 2>/dev/null; then bad "敏感推送未被拦截"; else ok "敏感推送被拦截"; fi
47
- echo ok > "$TD/wt/bad.txt" && git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm fix
48
- git -C "$TD/wt" push -q origin feat/scan 2>/dev/null && ok "干净推送放行" || bad "干净推送被拒"
49
-
50
- # 误报回归:`$TD/home/kits/...` 这种相对片段不该被判成 /home/<user>
51
- printf 'path=$TD/home/kits/dia-msg/dia.json\n' > "$TD/wt/vars.txt"
52
- git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm vars
53
- git -C "$TD/wt" push -q origin feat/scan 2>/dev/null && ok "相对片段 home/ 不误报" || bad "相对片段被误判为敏感路径"
54
-
55
- bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
56
- [ -z "$(git -C "$R" config --get core.hooksPath || true)" ] && ok "revert 还原 hooksPath" || bad "revert 未还原"
57
- [ ! -d "$R/.git/dia" ] && ok "revert 删除 dia 目录" || bad "dia 目录残留"
58
-
59
- # ── 原 hooksPath 保留 + 二次 apply 不改写 hooksPrevious(review P2-7)──────────
60
- R2="$TD/repo2"; mkdir -p "$R2" "$TD/orighooks"; git -C "$R2" init -q -b main
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
63
- printf '#!/bin/sh\nexit 0\n' > "$TD/orighooks/pre-commit"; chmod +x "$TD/orighooks/pre-commit"
64
- git -C "$R2" config --local core.hooksPath "$TD/orighooks"
65
- bash "$WT" adopt "$R2" --apply >/dev/null 2>&1 || bad "R2 apply 失败"
66
- hp1="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get("hooksPrevious") or "")' "$R2/.git/dia/config.json")"
67
- [ "$hp1" = "$TD/orighooks" ] && ok "首次 apply 记录 hooksPrevious" || bad "hooksPrevious=$hp1"
68
- bash "$WT" adopt "$R2" --apply >/dev/null 2>&1
69
- hp2="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get("hooksPrevious") or "")' "$R2/.git/dia/config.json")"
70
- [ "$hp2" = "$TD/orighooks" ] && ok "二次 apply 不覆盖 hooksPrevious" || bad "hooksPrevious 被改成 $hp2"
71
- bash "$WT" adopt "$R2" --revert >/dev/null 2>&1 || bad "R2 revert 失败"
72
- [ "$(git -C "$R2" config --get core.hooksPath)" = "$TD/orighooks" ] && ok "revert 还原原 hooksPath" || bad "revert 未还原原 hooksPath"
73
- # hooksPath 被第三方改动 → revert 拒绝(不抢配置)
74
- bash "$WT" adopt "$R2" --apply >/dev/null 2>&1
75
- git -C "$R2" config --local core.hooksPath "$TD/orighooks"
76
- if bash "$WT" adopt "$R2" --revert >/dev/null 2>&1; then bad "hooksPath 被改动后 revert 仍执行"; else ok "hooksPath 被改动后 revert 拒绝"; fi
77
- [ -f "$R2/.git/dia/config.json" ] && ok "拒绝 revert 时保留 config 备份" || bad "拒绝 revert 却删了 config"
78
- git -C "$R2" config --local core.hooksPath "$R2/.git/dia/hooks"; bash "$WT" adopt "$R2" --revert >/dev/null 2>&1
79
-
80
- # ── 自定义默认分支受保护(review P1-1 + P1-2)────────────────────────────────
81
- R3="$TD/repo3"; mkdir -p "$R3"; git -C "$R3" init -q -b trunk
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
84
- bash "$WT" adopt "$R3" --apply --default-branch trunk >/dev/null 2>&1 || bad "自定义分支 apply 失败"
85
- bash "$WT" adopt "$R3" --check >/dev/null 2>&1 && ok "自定义默认分支 check 通过" || bad "自定义默认分支 check 失败"
86
- bash "$WT" doctor "$R3" >/dev/null 2>&1 && ok "自定义默认分支 doctor 通过" || bad "自定义默认分支 doctor 失败"
87
- echo b > "$R3/b.txt"; git -C "$R3" add b.txt
88
- if git -C "$R3" commit -qm should-block 2>/dev/null; then bad "trunk 提交未被拦截"; else ok "自定义默认分支 trunk 提交被拦截"; fi
89
- git -C "$R3" reset -q
90
-
91
- # ── doctor:未 adopt / 无参数(review P1-4)──────────────────────────────────
92
- bash "$WT" doctor "$R3" >/dev/null 2>&1 || true
93
- R4="$TD/repo4"; mkdir -p "$R4"; git -C "$R4" init -q -b main
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
96
- if bash "$WT" doctor "$R4" >/dev/null 2>&1; then bad "未 adopt 的仓 doctor 误报通过"; else ok "未 adopt 的仓 doctor 失败"; fi
97
- bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" || bad "doctor 无参数未 exit 2"
98
-
99
- # ── pre-push 守卫(主 checkout 拒绝 / worktree 放行)──────────────────────────
100
- R5="$TD/repo5"; ORIGIN5="$TD/origin5.git"; git init -q --bare "$ORIGIN5"
101
- mkdir -p "$R5"; git -C "$R5" init -q -b main
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 origin main
105
- git -C "$R5" fetch -q origin
106
- git -C "$R5" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
107
- bash "$WT" adopt "$R5" --apply >/dev/null 2>&1 || bad "R5 apply 失败"
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
112
- git -C "$R5" worktree add -q -b feat/x "$TD/repo5-worktree--x" main
113
- echo b > "$TD/repo5-worktree--x/b.txt"; git -C "$TD/repo5-worktree--x" add b.txt
114
- git -C "$TD/repo5-worktree--x" commit -qm feat-x 2>/dev/null
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
116
-
117
- # ── template 生效(P2-6):adopt 后 create 按 config.template 推导路径 ─────────
118
- out="$(bash "$WT" create "$R5" t1 --dry-run 2>&1 || true)"
119
- printf '%s' "$out" | grep -q "repo5-worktree--t1" && ok "默认模板 create 路径" || bad "默认模板 create: $out"
120
- bash "$WT" adopt "$R5" --apply --template '{{parent}}/{{repo}}.wt.{{feature}}' >/dev/null 2>&1 || bad "template apply 失败"
121
- out="$(bash "$WT" create "$R5" t1 --dry-run 2>&1 || true)"
122
- printf '%s' "$out" | grep -q "repo5.wt.t1" && ok "自定义模板 create 路径生效" || bad "自定义模板: $out"
123
- bash "$WT" adopt "$R5" --apply >/dev/null 2>&1
124
-
125
- # ── 过期 origin/HEAD(远端分支已删)不误判默认分支 ──────────────────────────
126
- R6="$TD/repo6"; mkdir -p "$R6"; git -C "$R6" init -q -b main
127
- git -C "$R6" config user.email t@example.com; git -C "$R6" config user.name t
128
- echo a > "$R6/a.txt"; git -C "$R6" add -A; git -C "$R6" commit -qm init
129
- git -C "$R6" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/gone
130
- out="$(bash "$WT" adopt "$R6" --json)"
131
- printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "过期 origin/HEAD 回落到 main" || bad "过期 origin/HEAD: $out"
132
-
133
- # ── 钩子链:原 hooksPath(.githooks / husky)不被吞掉 ─────────────────────────
134
- R7="$TD/repo7"; mkdir -p "$R7/.githooks"; git -C "$R7" init -q -b main
135
- git -C "$R7" config user.email t@example.com; git -C "$R7" config user.name t
136
- printf '#!/bin/sh\necho chained >> "$(git rev-parse --show-toplevel)/chain.log"\n' > "$R7/.githooks/pre-commit"
137
- chmod +x "$R7/.githooks/pre-commit"
138
- echo a > "$R7/a.txt"; git -C "$R7" add -A; git -C "$R7" commit -qm init
139
- git -C "$R7" config --local core.hooksPath .githooks
140
- bash "$WT" adopt "$R7" --apply >/dev/null 2>&1 || bad "R7 apply 失败"
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" \
142
- && ok "config 记录 hooksChain" || bad "hooksChain 未记录"
143
- git -C "$R7" worktree add -q -b feat/y "$TD/repo7-worktree--y" main
144
- echo b > "$TD/repo7-worktree--y/b.txt"; git -C "$TD/repo7-worktree--y" add b.txt
145
- git -C "$TD/repo7-worktree--y" commit -qm feat-y >/dev/null 2>&1
146
- [ -f "$TD/repo7-worktree--y/chain.log" ] && ok "原 pre-commit 被链式执行" || bad "原 pre-commit 未执行(钩子被吞)"
147
- bash "$WT" adopt "$R7" --revert >/dev/null 2>&1
148
- [ "$(git -C "$R7" config --get core.hooksPath)" = ".githooks" ] && ok "revert 还原原 hooksPath(相对)" || bad "revert 未还原 .githooks"
149
-
150
- # ── adopt --all:批量主 checkout,跳过 worktree 目录 ─────────────────────────
79
+ git -C "$R" fetch -q origin 2>/dev/null || true
80
+ BAD="$(printf '/%s/alice/secret' Users)"; echo "$BAD" > "$TD/repo-worktree--y/bad.txt"
81
+ git -C "$TD/repo-worktree--y" add bad.txt; git -C "$TD/repo-worktree--y" commit -qm bad >/dev/null 2>&1
82
+ if git -C "$TD/repo-worktree--y" push -q origin feat/y 2>/dev/null; then bad "敏感推送未被拦截"; else ok "敏感推送被拦截"; fi
83
+ git -C "$TD/repo-worktree--y" reset -q --hard HEAD~1
84
+ printf 'path=$TD/home/kits/dia.json\n' > "$TD/repo-worktree--y/vars.txt"
85
+ git -C "$TD/repo-worktree--y" add vars.txt && git -C "$TD/repo-worktree--y" commit -qm vars >/dev/null 2>&1
86
+ git -C "$TD/repo-worktree--y" push -q origin feat/y 2>/dev/null && ok "相对片段 home/ 不误报" || bad "相对片段被误判"
87
+
88
+ # adopt --all
151
89
  AR="$TD/allroot"; mkdir -p "$AR/a" "$AR/b"
152
- for r in a b; do
153
- git -C "$AR/$r" init -q -b main
154
- git -C "$AR/$r" config user.email t@example.com; git -C "$AR/$r" config user.name t
155
- echo x > "$AR/$r/x.txt"; git -C "$AR/$r" add -A; git -C "$AR/$r" commit -qm init
156
- done
90
+ for r in a b; do mk_repo "$AR/$r"; done
157
91
  git -C "$AR/a" worktree add -q -b feat/w "$AR/a-worktree--w" main
158
- out="$(DIA_WT_REPOS_ROOT="$AR" DIA_WT_DATA_ROOT="$TD/dr2" bash "$WT" adopt --all --apply --json 2>&1)"
92
+ out="$(DIA_WT_REPOS_ROOT="$AR" bash "$WT" adopt --all --apply --json 2>&1)"
159
93
  printf '%s' "$out" | grep -q '"repos":2,"ok":2,"fail":0' && ok "adopt --all 覆盖 2 个主 checkout" || bad "adopt --all: $out"
160
- for r in a b; do
161
- [ -n "$(git -C "$AR/$r" config --get core.hooksPath || true)" ] && ok "adopt --all: $r 已配 hooksPath" || bad "adopt --all: $r 未配置"
162
- done
163
94
  [ ! -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 未覆盖自定义仓根"
166
- DIA_WT_REPOS_ROOT="$TD/empty-root" DIA_WT_DATA_ROOT="$TD/dr3" bash "$WT" adopt --all >/dev/null 2>&1
95
+ out="$(DIA_WT_REPOS_ROOT="$AR" bash "$WT" doctor --all --json 2>/dev/null)"
96
+ printf '%s' "$out" | grep -q "$AR/a" && ok "doctor --all 覆盖自定义仓根" || bad "doctor --all 未覆盖"
97
+ DIA_WT_REPOS_ROOT="$TD/empty-root" bash "$WT" adopt --all >/dev/null 2>&1
167
98
  [ $? = 2 ] && ok "adopt --all 空结果 exit 2" || bad "adopt --all 空结果未 exit 2"
168
99
 
169
- # ── doctor --fix:钩子被覆盖后能无损重建 ────────────────────────────────────
170
- R8="$TD/repo8"; mkdir -p "$R8"; git -C "$R8" init -q -b main
171
- git -C "$R8" config user.email t@example.com; git -C "$R8" config user.name t
172
- echo a > "$R8/a.txt"; git -C "$R8" add -A; git -C "$R8" commit -qm init
173
- bash "$WT" adopt "$R8" --apply >/dev/null 2>&1
174
- rm -f "$R8/.git/dia/hooks/post-checkout"
175
- printf '#!/bin/sh\nexit 0\n' > "$R8/.git/dia/hooks/pre-commit"; chmod +x "$R8/.git/dia/hooks/pre-commit"
176
- if bash "$WT" doctor "$R8" >/dev/null 2>&1; then bad "钩子被覆盖后 doctor 仍通过"; else ok "钩子被覆盖后 doctor 报失败"; fi
177
- bash "$WT" doctor "$R8" --fix >/dev/null 2>&1 || bad "doctor --fix 失败"
178
- bash "$WT" doctor "$R8" >/dev/null 2>&1 && ok "doctor --fix 后恢复 ok" || bad "doctor --fix 后仍失败"
179
- [ -x "$R8/.git/dia/hooks/post-checkout" ] && ok "doctor --fix 补回 post-checkout" || bad "post-checkout 未补回"
100
+ # gate
101
+ rm -f "$TD/repo-worktree--y/chain.log"
102
+ if bash "$WT" gate "$R" y >/tmp/gate.log 2>&1; then ok "gate 成功"; else bad "gate 失败: $(tail -2 /tmp/gate.log | tr '\n' ' ')"; fi
103
+ if bash "$WT" gate "$R" yb >/dev/null 2>&1; then bad "gate 子串误匹配"; else ok "gate 精确匹配 feature"; fi
104
+ echo dirty >> "$TD/repo-worktree--y/c.txt"
105
+ if bash "$WT" gate "$R" y >/dev/null 2>&1; then bad "worktree 脏时 gate 通过"; else ok "worktree 脏时 gate 失败"; fi
106
+ git -C "$TD/repo-worktree--y" checkout -q -- c.txt
180
107
 
181
- # ── gate:成功 / 失败(P1-3 精确匹配)────────────────────────────────────────
182
- bash "$WT" gate "$R5" x >/dev/null 2>&1 && ok "gate 成功" || bad "gate 失败"
183
- if bash "$WT" gate "$R5" xb >/dev/null 2>&1; then bad "gate 子串误匹配(x 命中 xb)"; else ok "gate 精确匹配 feature"; fi
184
- echo dirty >> "$TD/repo5-worktree--x/b.txt"
185
- if bash "$WT" gate "$R5" x >/dev/null 2>&1; then bad "worktree 脏时 gate 通过"; else ok "worktree 脏时 gate 失败"; fi
186
- git -C "$TD/repo5-worktree--x" checkout -q -- b.txt
108
+ # revert
109
+ bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
110
+ [ ! -f "$R/.git/dia/config.json" ] && ok "revert 删除本仓配置" || bad "配置残留"
111
+ [ -z "$(git -C "$R" config --local --get core.hooksPath || true)" ] && ok "revert 还原本地 hooksPath" || bad "hooksPath 未还原"
112
+ git -C "$R" worktree remove --force "$TD/repo-worktree--y" >/dev/null 2>&1 || true
187
113
 
188
114
  echo; echo "── PASS $PASS · FAIL $FAIL ──"
189
115
  [ "$FAIL" = 0 ]