@minonl/dia 0.0.3 → 0.0.6
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/README.md +1 -1
- package/dist/cli.js +1551 -543
- package/docs/cli.md +3 -3
- package/docs/index.md +9 -1
- package/docs/msg-collab-m0-design.md +275 -0
- package/docs/msg-collab-m0.md +96 -0
- package/docs/msg-collab-m1-design.md +170 -0
- package/docs/msg-collab-m1.md +36 -0
- package/docs/research/msg-collaboration-grill.md +461 -0
- package/docs/review/service-noun-review-2026-11-05.md +134 -0
- package/docs/service-noun.md +354 -0
- package/docs/status.md +2 -0
- package/docs/worktree.md +106 -0
- package/kits/dia-msg/dia.json +33 -0
- package/kits/dia-msg/skills/dia-msg/SKILL.md +56 -0
- package/package.json +2 -1
- package/packages/job/config.example +2 -2
- package/packages/job/lib/json.mjs +16 -1
- package/packages/wt/bin/dia-wt +53 -48
- package/packages/wt/tests/adopt.test.sh +27 -18
- package/packages/wt/tests/route.test.sh +8 -8
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
|
|
@@ -939,7 +964,7 @@ cmd_clean() {
|
|
|
939
964
|
}
|
|
940
965
|
|
|
941
966
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
942
|
-
# route ——
|
|
967
|
+
# route —— 【开工前必查】合并通道是哪一种?(R1.7:所有仓都走 MR,仅合并通道不同)
|
|
943
968
|
#
|
|
944
969
|
# 为什么需要(2026-10-12 复发教训):R20 管「别污染 main checkout」(隔离);R1.7 管「要不要评审」——
|
|
945
970
|
# **两件事独立**。agent 做对了 A(隔离),却错误推导出 B(要 MR):在内部仓走 MR 全套打扰同事;
|
|
@@ -948,13 +973,13 @@ cmd_clean() {
|
|
|
948
973
|
# 为什么改成**配置驱动**(2026-10-16 用户指正):
|
|
949
974
|
# 原先把仓名硬编码在本脚本里(`yan-agent|personal-agent` 内部仓、`example-repo|*-qa|…` 业务仓)⇒
|
|
950
975
|
# ① 「哪些仓是我的/业务仓」是**环境事实**,换个人就全错;② 它含「**我是谁**」成分 ——
|
|
951
|
-
#
|
|
976
|
+
# 仓归属因人而异(本人的自持仓 / 别人的仓)⇒ **同一仓在不同人机器上合并通道不同**;
|
|
952
977
|
# ③ 每加一个仓都要改共享配方库 ⇒ 漂移源。⇒ **规则留在本脚本,数据只落本地**。
|
|
953
978
|
#
|
|
954
979
|
# 判定顺序(**每步都记录来源**、输出可审计;先否决后放行):
|
|
955
980
|
# ① 本地配置 `mr` 名单命中 → 走 MR(显式否决,优先级最高)
|
|
956
|
-
# ② 本地配置 `direct` 名单命中 →
|
|
957
|
-
# ③ remote 命名空间 ∈ `my_namespaces` →
|
|
981
|
+
# ② 本地配置 `direct` 名单命中 → 个人仓:MR + owner 自合
|
|
982
|
+
# ③ remote 命名空间 ∈ `my_namespaces` → 个人仓:MR + owner 自合("这是我的仓" = 可推导事实)
|
|
958
983
|
# ④ 其余 / 信息不足 → **走 MR**(安全侧兜底)
|
|
959
984
|
#
|
|
960
985
|
# 配置读取(**数据根为准**,可与个人仓基线合并;三个名单键取**并集**:本地只能增、不能删):
|
|
@@ -966,7 +991,7 @@ cmd_clean() {
|
|
|
966
991
|
# 只在输出里给「推测 + 如何确认」,由人核对(glab auth status)后写进配置。
|
|
967
992
|
#
|
|
968
993
|
# 用法:dia wt route <repo-path>
|
|
969
|
-
# 退出码:0 =
|
|
994
|
+
# 退出码:0 = MR + owner 自合(个人仓);1 = MR + LGTM + bot(团队仓);2 = 信息不足(按团队仓处理)
|
|
970
995
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
971
996
|
|
|
972
997
|
# ── remote 命名空间:`<host>/<ns>/<repo>` 里的 `<ns>`(无 remote → 空)────────
|
|
@@ -1065,46 +1090,45 @@ case "$name" in *-worktree--*) name="${name%%-worktree--*}"; repo="$(dirname "$r
|
|
|
1065
1090
|
verdict=mr; why="命名空间「${ns}」不在 my_namespaces(安全侧默认 MR)"
|
|
1066
1091
|
fi
|
|
1067
1092
|
|
|
1068
|
-
echo "── 仓库路由判定(R1.7 ·
|
|
1093
|
+
echo "── 仓库路由判定(R1.7 · 配置驱动:所有仓走 MR,仅合并通道不同)──"
|
|
1069
1094
|
echo " 仓库 : $name${ns:+(命名空间 ${ns})}"
|
|
1070
1095
|
printf ' 判定来源 : %s\n' "$(printf '%s' "$cfg" | node -e 'let s="";process.stdin.on("data",d=>{s+=d}).on("end",()=>{const d=JSON.parse(s);const srcs=(d.sources&&d.sources.length)?d.sources:["(无配置文件)"];console.log(srcs.join("、")+(d.errors.length?" ⚠️ "+d.errors.join(";"):""))})')"
|
|
1071
1096
|
|
|
1072
1097
|
case "$verdict" in
|
|
1073
|
-
direct)
|
|
1098
|
+
direct|personal)
|
|
1074
1099
|
cat <<EOS
|
|
1075
|
-
结论 : ✅
|
|
1100
|
+
结论 : ✅ **MR + owner 自合**(个人仓)|依据:$why
|
|
1076
1101
|
正确姿势:
|
|
1077
|
-
git worktree add <平级路径> -b
|
|
1078
|
-
# ... 开发 + 自测
|
|
1079
|
-
git push origin HEAD:
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1102
|
+
git worktree add <平级路径> -b feat/<feature> origin/main
|
|
1103
|
+
# ... 开发 + 自测 + rebase(落后必须为 0)...
|
|
1104
|
+
git push origin HEAD:feat/<feature>
|
|
1105
|
+
glab mr create ... --source-branch feat/<feature> --target-branch main --yes
|
|
1106
|
+
# pipeline 绿 → owner 直接合(无需 LGTM、不发 @bot mergeme)
|
|
1107
|
+
glab mr merge <iid> --yes --remove-source-branch
|
|
1108
|
+
提示 : 一律走 MR(2026-11-05 起没有「直推 main」);owner 自合省的是 LGTM,不是留痕
|
|
1084
1109
|
EOS
|
|
1085
1110
|
exit 0 ;;
|
|
1086
1111
|
mr)
|
|
1087
1112
|
cat <<EOS
|
|
1088
|
-
结论 : ✅
|
|
1113
|
+
结论 : ✅ **MR + LGTM + @bot mergeme**(组织/团队仓)|依据:$why
|
|
1089
1114
|
正确姿势:
|
|
1090
|
-
git worktree add <平级路径> -b
|
|
1115
|
+
git worktree add <平级路径> -b feat/<feature> origin/main
|
|
1091
1116
|
# ... 开发 + 自测 + rebase(落后必须为 0)...
|
|
1092
|
-
glab mr create ...
|
|
1093
|
-
# 等 LGTM → @bot mergeme
|
|
1094
|
-
提示 : example-repo 的 docs/** 纯文档改动,用户说「直推」时可直接特权合并(R1.7 常态授权)
|
|
1117
|
+
glab mr create ... --source-branch feat/<feature> --target-branch main --yes
|
|
1118
|
+
# 等 LGTM → @bot mergeme(yan-mr wait 可一键盯完)
|
|
1095
1119
|
EOS
|
|
1096
1120
|
exit 1 ;;
|
|
1097
1121
|
*)
|
|
1098
1122
|
cat <<EOS
|
|
1099
|
-
结论 : ⚠️
|
|
1123
|
+
结论 : ⚠️ **信息不足,按「MR + LGTM + bot」处理**(安全侧)|依据:$why
|
|
1100
1124
|
补齐判定(任选):
|
|
1101
1125
|
1) 让仓有 origin remote ⇒ 命名空间可推导;
|
|
1102
1126
|
2) 写配置(数据根为主入口,可与个人仓基线合并;名单取并集):
|
|
1103
1127
|
\$YAN_DATA_ROOT/route.json ← 本机;**可以是软链**(指向个人仓版本化文件)
|
|
1104
1128
|
\$PERSONAL_AGENT_DIR/config/yan-route.json ← 个人仓基线(跨设备)
|
|
1105
1129
|
{ "my_namespaces": ["<你的 gitlab 账号>"], "direct": ["<我自持的仓名>"], "mr": [] }
|
|
1130
|
+
(`direct` 现表示「个人仓 → owner 自合」;路径已不再直推)
|
|
1106
1131
|
EOS
|
|
1107
|
-
# 未配置命名空间时给「推测 + 如何确认」,但**绝不自动采用**(不瞎猜"我是谁")
|
|
1108
1132
|
if [ -n "$ns" ] && [ -z "$_cfg_ns" ]; then
|
|
1109
1133
|
local guess; guess="$(git -C "$repo" config --get user.email 2>/dev/null | sed 's/@.*//' || true)"
|
|
1110
1134
|
printf ' 💡 推测(**未采用**):origin 命名空间 =「%s」;%s\n' "$ns" "${guess:+你的 git user.email 前缀 =「${guess}」}"
|
|
@@ -1161,7 +1185,6 @@ prot=" main master develop dev "
|
|
|
1161
1185
|
probe="${DIA_WT_GUARD_PROBE:-0}"
|
|
1162
1186
|
wt_guard_scan() {
|
|
1163
1187
|
local ls="$1" rs="$2" range="" base="" hits=""
|
|
1164
|
-
[ "${DIA_WT_GUARD_SCAN:-1}" = "1" ] || return 0
|
|
1165
1188
|
if [ -n "$rs" ] && [ "$rs" != "0000000000000000000000000000000000000000" ] && git cat-file -e "$rs" 2>/dev/null; then
|
|
1166
1189
|
range="$rs..$ls"
|
|
1167
1190
|
else
|
|
@@ -1170,11 +1193,11 @@ else
|
|
|
1170
1193
|
fi
|
|
1171
1194
|
hits="$(git diff -U0 "$range" -- . 2>/dev/null | grep -E '^\+' | grep -vE '^\+\+\+' \
|
|
1172
1195
|
| grep -vE '/(Users|home)/(you|user|example|runner|shared|me)(/|$)' \
|
|
1173
|
-
| grep -nE '(/Users
|
|
1196
|
+
| grep -nE '(^|[+ "'\''`(=])/(Users|home)/[A-Za-z0-9._-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}' | head -5)"
|
|
1174
1197
|
if [ -n "$hits" ]; then
|
|
1175
1198
|
echo "[dia-wt] 推送包含敏感内容(已阻断):" >&2
|
|
1176
1199
|
printf '%s\n' "$hits" >&2
|
|
1177
|
-
echo "[dia-wt]
|
|
1200
|
+
echo "[dia-wt] 误报请修扫描规则(hooks 由 dia wt adopt 生成),不提供关闭开关" >&2
|
|
1178
1201
|
return 1
|
|
1179
1202
|
fi
|
|
1180
1203
|
}
|
|
@@ -1261,26 +1284,8 @@ cmd_adopt() {
|
|
|
1261
1284
|
esac; done
|
|
1262
1285
|
if [ "$all" = 1 ]; then
|
|
1263
1286
|
[ -z "$repo" ] || die "--all 不能与 <repo> 同时给(要么单个仓,要么全量)"
|
|
1264
|
-
local -a
|
|
1265
|
-
|
|
1266
|
-
dr="$(dia_wt_data_root 2>/dev/null || true)"
|
|
1267
|
-
rr="${DIA_WT_REPOS_ROOT:-$HOME/repos}"
|
|
1268
|
-
[ -n "$dr" ] && [ -d "$dr/repos" ] && roots+=("$dr/repos")
|
|
1269
|
-
[ -d "$rr" ] && roots+=("$rr")
|
|
1270
|
-
[ ${#roots[@]} -gt 0 ] || { echo "usage: dia wt adopt --all(未找到仓根:${rr})" >&2; exit 2; }
|
|
1271
|
-
local root sub gd cd_
|
|
1272
|
-
for root in "${roots[@]}"; do
|
|
1273
|
-
while IFS= read -r -d '' gd; do
|
|
1274
|
-
sub="$(dirname "$gd")"
|
|
1275
|
-
case "$(basename "$sub")" in *-worktree--*) continue ;; esac
|
|
1276
|
-
cd_="$(git -C "$sub" rev-parse --git-common-dir 2>/dev/null || true)"
|
|
1277
|
-
# 统一物理路径(macOS /tmp → /private/tmp),否则等式比较会假失败
|
|
1278
|
-
gd="$(cd "$sub" 2>/dev/null && cd "$gd" 2>/dev/null && pwd -P)"
|
|
1279
|
-
cd_="$(cd "$sub" 2>/dev/null && cd "$cd_" 2>/dev/null && pwd -P)"
|
|
1280
|
-
[ -n "$gd" ] && [ "$gd" = "$cd_" ] || continue # 只处理主 checkout
|
|
1281
|
-
repos+=("$sub")
|
|
1282
|
-
done < <(find "$root" -maxdepth 3 -name .git -type d -print0 2>/dev/null)
|
|
1283
|
-
done
|
|
1287
|
+
local -a repos=()
|
|
1288
|
+
while IFS= read -r r; do repos+=("$r"); done < <(wt_discover_main_checkouts)
|
|
1284
1289
|
[ ${#repos[@]} -gt 0 ] || { echo "usage: dia wt adopt --all(未匹配到任何主 checkout)" >&2; exit 2; }
|
|
1285
1290
|
local -a pass=()
|
|
1286
1291
|
local a
|
|
@@ -1402,7 +1407,7 @@ cmd_doctor() {
|
|
|
1402
1407
|
*) repos+=("$1"); shift ;;
|
|
1403
1408
|
esac; done
|
|
1404
1409
|
if [ "$all" = 1 ] && [ ${#repos[@]} -eq 0 ]; then
|
|
1405
|
-
while IFS= read -r
|
|
1410
|
+
while IFS= read -r d; do repos+=("$d"); done < <(wt_discover_main_checkouts)
|
|
1406
1411
|
fi
|
|
1407
1412
|
if [ ${#repos[@]} -eq 0 ]; then
|
|
1408
1413
|
echo "usage: dia wt doctor <repo>... | --all(--all 未匹配到任何仓库)" >&2
|
|
@@ -1519,11 +1524,11 @@ dia wt —— worktree 目录形式选型 + 创建(唯一实现)
|
|
|
1519
1524
|
# ★ 依赖体检(R20):软链/逃逸/悬空;--all 扫全部仓
|
|
1520
1525
|
dia wt adopt <repo>|--all [--apply|--check|--revert] [--template T] [--default-branch B] [--json]
|
|
1521
1526
|
# 配置路径模板 + 守卫钩子(默认 dry-run;--all 扫仓根)
|
|
1522
|
-
#
|
|
1527
|
+
# 守卫含推送前敏感扫描(不提供关闭开关;误报修规则)
|
|
1523
1528
|
dia wt doctor [<repo>...] [--all] [--json] [--fix]
|
|
1524
1529
|
# 巡检:配置/钩子/探针/主 checkout/worktree 卫生;--fix 重装守卫钩子
|
|
1525
1530
|
dia wt gate <repo> <feature> # 提交 MR 前:check + doctor + 干净 + ahead>0
|
|
1526
|
-
dia wt route <repo-path> # ★
|
|
1531
|
+
dia wt route <repo-path> # ★ 开工前必查:合并通道(owner 自合 / LGTM+bot;一律走 MR)
|
|
1527
1532
|
dia wt scope <repo|worktree> [--feature <slug>] [--session <id>] # ★ 产出作用域声明(设计 §4.5)
|
|
1528
1533
|
dia wt scope --show # 看与当前目录匹配的作用域声明
|
|
1529
1534
|
dia wt scope --list # 列出全部作用域文件(按 worktree 分文件)
|
|
@@ -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"
|
|
@@ -38,14 +38,20 @@ git -C "$R" reset -q
|
|
|
38
38
|
# ── 推送前敏感扫描(PII/凭据形态)──
|
|
39
39
|
BR="$TD/remote.git"; git init -q --bare "$BR"
|
|
40
40
|
git -C "$R" remote add origin "$BR" 2>/dev/null || true
|
|
41
|
-
git -
|
|
41
|
+
git --git-dir="$BR" fetch -q "$R" main:main 2>/dev/null || true
|
|
42
|
+
git -C "$R" fetch -q origin 2>/dev/null || true
|
|
42
43
|
git -C "$R" worktree add -q "$TD/wt" -b feat/scan 2>/dev/null || true
|
|
43
|
-
echo "/
|
|
44
|
-
git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm bad
|
|
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
|
|
45
46
|
if git -C "$TD/wt" push -q origin feat/scan 2>/dev/null; then bad "敏感推送未被拦截"; else ok "敏感推送被拦截"; fi
|
|
46
|
-
echo ok > "$TD/wt/bad.txt" && git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm fix
|
|
47
|
+
echo ok > "$TD/wt/bad.txt" && git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm fix
|
|
47
48
|
git -C "$TD/wt" push -q origin feat/scan 2>/dev/null && ok "干净推送放行" || bad "干净推送被拒"
|
|
48
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
|
+
|
|
49
55
|
bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
|
|
50
56
|
[ -z "$(git -C "$R" config --get core.hooksPath || true)" ] && ok "revert 还原 hooksPath" || bad "revert 未还原"
|
|
51
57
|
[ ! -d "$R/.git/dia" ] && ok "revert 删除 dia 目录" || bad "dia 目录残留"
|
|
@@ -53,7 +59,7 @@ bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
|
|
|
53
59
|
# ── 原 hooksPath 保留 + 二次 apply 不改写 hooksPrevious(review P2-7)──────────
|
|
54
60
|
R2="$TD/repo2"; mkdir -p "$R2" "$TD/orighooks"; git -C "$R2" init -q -b main
|
|
55
61
|
git -C "$R2" config user.email t@example.com; git -C "$R2" config user.name t
|
|
56
|
-
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
|
|
57
63
|
printf '#!/bin/sh\nexit 0\n' > "$TD/orighooks/pre-commit"; chmod +x "$TD/orighooks/pre-commit"
|
|
58
64
|
git -C "$R2" config --local core.hooksPath "$TD/orighooks"
|
|
59
65
|
bash "$WT" adopt "$R2" --apply >/dev/null 2>&1 || bad "R2 apply 失败"
|
|
@@ -74,7 +80,7 @@ git -C "$R2" config --local core.hooksPath "$R2/.git/dia/hooks"; bash "$WT" adop
|
|
|
74
80
|
# ── 自定义默认分支受保护(review P1-1 + P1-2)────────────────────────────────
|
|
75
81
|
R3="$TD/repo3"; mkdir -p "$R3"; git -C "$R3" init -q -b trunk
|
|
76
82
|
git -C "$R3" config user.email t@example.com; git -C "$R3" config user.name t
|
|
77
|
-
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
|
|
78
84
|
bash "$WT" adopt "$R3" --apply --default-branch trunk >/dev/null 2>&1 || bad "自定义分支 apply 失败"
|
|
79
85
|
bash "$WT" adopt "$R3" --check >/dev/null 2>&1 && ok "自定义默认分支 check 通过" || bad "自定义默认分支 check 失败"
|
|
80
86
|
bash "$WT" doctor "$R3" >/dev/null 2>&1 && ok "自定义默认分支 doctor 通过" || bad "自定义默认分支 doctor 失败"
|
|
@@ -86,7 +92,7 @@ git -C "$R3" reset -q
|
|
|
86
92
|
bash "$WT" doctor "$R3" >/dev/null 2>&1 || true
|
|
87
93
|
R4="$TD/repo4"; mkdir -p "$R4"; git -C "$R4" init -q -b main
|
|
88
94
|
git -C "$R4" config user.email t@example.com; git -C "$R4" config user.name t
|
|
89
|
-
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
|
|
90
96
|
if bash "$WT" doctor "$R4" >/dev/null 2>&1; then bad "未 adopt 的仓 doctor 误报通过"; else ok "未 adopt 的仓 doctor 失败"; fi
|
|
91
97
|
bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" || bad "doctor 无参数未 exit 2"
|
|
92
98
|
|
|
@@ -94,17 +100,18 @@ bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" ||
|
|
|
94
100
|
R5="$TD/repo5"; ORIGIN5="$TD/origin5.git"; git init -q --bare "$ORIGIN5"
|
|
95
101
|
mkdir -p "$R5"; git -C "$R5" init -q -b main
|
|
96
102
|
git -C "$R5" config user.email t@example.com; git -C "$R5" config user.name t
|
|
97
|
-
echo a > "$R5/a.txt"; git -C "$R5" add -A; git -C "$R5" commit -qm init
|
|
98
|
-
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
|
|
99
105
|
git -C "$R5" fetch -q origin
|
|
100
106
|
git -C "$R5" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
|
|
101
107
|
bash "$WT" adopt "$R5" --apply >/dev/null 2>&1 || bad "R5 apply 失败"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
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
|
|
105
112
|
git -C "$R5" worktree add -q -b feat/x "$TD/repo5-worktree--x" main
|
|
106
113
|
echo b > "$TD/repo5-worktree--x/b.txt"; git -C "$TD/repo5-worktree--x" add b.txt
|
|
107
|
-
git -C "$TD/repo5-worktree--x" commit -qm feat-x
|
|
114
|
+
git -C "$TD/repo5-worktree--x" commit -qm feat-x 2>/dev/null
|
|
108
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
|
|
109
116
|
|
|
110
117
|
# ── template 生效(P2-6):adopt 后 create 按 config.template 推导路径 ─────────
|
|
@@ -118,7 +125,7 @@ bash "$WT" adopt "$R5" --apply >/dev/null 2>&1
|
|
|
118
125
|
# ── 过期 origin/HEAD(远端分支已删)不误判默认分支 ──────────────────────────
|
|
119
126
|
R6="$TD/repo6"; mkdir -p "$R6"; git -C "$R6" init -q -b main
|
|
120
127
|
git -C "$R6" config user.email t@example.com; git -C "$R6" config user.name t
|
|
121
|
-
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
|
|
122
129
|
git -C "$R6" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/gone
|
|
123
130
|
out="$(bash "$WT" adopt "$R6" --json)"
|
|
124
131
|
printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "过期 origin/HEAD 回落到 main" || bad "过期 origin/HEAD: $out"
|
|
@@ -128,7 +135,7 @@ R7="$TD/repo7"; mkdir -p "$R7/.githooks"; git -C "$R7" init -q -b main
|
|
|
128
135
|
git -C "$R7" config user.email t@example.com; git -C "$R7" config user.name t
|
|
129
136
|
printf '#!/bin/sh\necho chained >> "$(git rev-parse --show-toplevel)/chain.log"\n' > "$R7/.githooks/pre-commit"
|
|
130
137
|
chmod +x "$R7/.githooks/pre-commit"
|
|
131
|
-
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
|
|
132
139
|
git -C "$R7" config --local core.hooksPath .githooks
|
|
133
140
|
bash "$WT" adopt "$R7" --apply >/dev/null 2>&1 || bad "R7 apply 失败"
|
|
134
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" \
|
|
@@ -145,7 +152,7 @@ AR="$TD/allroot"; mkdir -p "$AR/a" "$AR/b"
|
|
|
145
152
|
for r in a b; do
|
|
146
153
|
git -C "$AR/$r" init -q -b main
|
|
147
154
|
git -C "$AR/$r" config user.email t@example.com; git -C "$AR/$r" config user.name t
|
|
148
|
-
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
|
|
149
156
|
done
|
|
150
157
|
git -C "$AR/a" worktree add -q -b feat/w "$AR/a-worktree--w" main
|
|
151
158
|
out="$(DIA_WT_REPOS_ROOT="$AR" DIA_WT_DATA_ROOT="$TD/dr2" bash "$WT" adopt --all --apply --json 2>&1)"
|
|
@@ -154,13 +161,15 @@ for r in a b; do
|
|
|
154
161
|
[ -n "$(git -C "$AR/$r" config --get core.hooksPath || true)" ] && ok "adopt --all: $r 已配 hooksPath" || bad "adopt --all: $r 未配置"
|
|
155
162
|
done
|
|
156
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 未覆盖自定义仓根"
|
|
157
166
|
DIA_WT_REPOS_ROOT="$TD/empty-root" DIA_WT_DATA_ROOT="$TD/dr3" bash "$WT" adopt --all >/dev/null 2>&1
|
|
158
167
|
[ $? = 2 ] && ok "adopt --all 空结果 exit 2" || bad "adopt --all 空结果未 exit 2"
|
|
159
168
|
|
|
160
169
|
# ── doctor --fix:钩子被覆盖后能无损重建 ────────────────────────────────────
|
|
161
170
|
R8="$TD/repo8"; mkdir -p "$R8"; git -C "$R8" init -q -b main
|
|
162
171
|
git -C "$R8" config user.email t@example.com; git -C "$R8" config user.name t
|
|
163
|
-
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
|
|
164
173
|
bash "$WT" adopt "$R8" --apply >/dev/null 2>&1
|
|
165
174
|
rm -f "$R8/.git/dia/hooks/post-checkout"
|
|
166
175
|
printf '#!/bin/sh\nexit 0\n' > "$R8/.git/dia/hooks/pre-commit"; chmod +x "$R8/.git/dia/hooks/pre-commit"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# 覆盖(**为什么是这些**):
|
|
5
5
|
# · 无配置时**绝不放行**:即便命名空间看着像我的,也只能走 MR(不瞎猜"我是谁")
|
|
6
|
-
# · 命名空间推导:配置了 my_namespaces
|
|
6
|
+
# · 命名空间推导:配置了 my_namespaces 后,我的仓 → MR + owner 自合(rc 0)
|
|
7
7
|
# · 显式名单:direct 放行 / mr **否决优先**
|
|
8
8
|
# · 个人仓基线 + 数据根本地**并集合并**;数据根配置**可以是软链**
|
|
9
9
|
# · 空列表**不误命中**(`grep -qx ""` 匹配空行是这类脚本的经典假阳性)
|
|
@@ -45,13 +45,13 @@ echo "== 2. 无配置 + 命名空间看着像我的 ⇒ **仍 MR**(不瞎猜
|
|
|
45
45
|
[ "$(run "$MINE")" = 1 ] && ok "退出码 1(未因命名空间相似而放行)" || { bad "应 1:不能瞎猜"; out; }
|
|
46
46
|
see "不在 my_namespaces" && ok "说明未配置" || { bad "缺说明"; out; }
|
|
47
47
|
|
|
48
|
-
echo "== 3. 配置 my_namespaces ⇒
|
|
48
|
+
echo "== 3. 配置 my_namespaces ⇒ 我的仓 = MR + owner 自合 =="
|
|
49
49
|
setcfg "$CFG" '{ "my_namespaces": ["example-user"] }'
|
|
50
50
|
[ "$(run "$MINE")" = 0 ] && ok "退出码 0" || { bad "应 0"; out; }
|
|
51
51
|
see "∈ my_namespaces" && ok "判定来源 = 命名空间" || { bad "来源不对"; out; }
|
|
52
52
|
[ "$(run "$TEAM")" = 1 ] && ok "非我命名空间仍 MR" || { bad "应 1"; out; }
|
|
53
53
|
|
|
54
|
-
echo "== 4. direct 名单 ⇒
|
|
54
|
+
echo "== 4. direct 名单 ⇒ owner 自合(覆盖默认)=="
|
|
55
55
|
setcfg "$CFG" '{ "my_namespaces": ["example-user"], "direct": ["teamrepo"] }'
|
|
56
56
|
[ "$(run "$TEAM")" = 0 ] && ok "退出码 0" || { bad "应 0"; out; }
|
|
57
57
|
see "配置 direct 名单命中" && ok "判定来源 = 配置" || { bad "来源不对"; out; }
|
|
@@ -64,9 +64,9 @@ see "显式否决" && ok "给出否决理由" || { bad "缺理由"; out; }
|
|
|
64
64
|
echo "== 6. 个人仓基线 + 数据根 ⇒ 并集合并(判定来源列出两个文件)=="
|
|
65
65
|
setcfg "$CFG" '{ "direct": ["teamrepo"] }'
|
|
66
66
|
setcfg "$BASE" '{ "my_namespaces": ["example-user"], "direct": ["myrepo"] }'
|
|
67
|
-
[ "$(run "$TEAM")" = 0 ] && ok "本地新增生效(teamrepo
|
|
67
|
+
[ "$(run "$TEAM")" = 0 ] && ok "本地新增生效(teamrepo = owner 自合)" || { bad "本地配置未生效"; out; }
|
|
68
68
|
see "route.json" && ok "来源含数据根文件" || { bad "缺来源"; out; }
|
|
69
|
-
[ "$(run "$MINE")" = 0 ] && ok "基线条目保留(myrepo
|
|
69
|
+
[ "$(run "$MINE")" = 0 ] && ok "基线条目保留(myrepo = owner 自合)" || { bad "基线未生效"; out; }
|
|
70
70
|
see "yan-route.json" && ok "来源含基线文件" || { bad "缺基线来源"; out; }
|
|
71
71
|
[ "$(run "$NOREMOTE")" = 2 ] && ok "无 remote 的仓:信息不足(rc=2)" || { bad "应 2"; out; }
|
|
72
72
|
|
|
@@ -89,16 +89,16 @@ echo "== 10. \$DIA_WT_ROUTE_CONFIG 显式指定 ⇒ 最高优先(忽略其余
|
|
|
89
89
|
setcfg "$CFG" '{ "direct": ["myrepo"] }'
|
|
90
90
|
EX="$TD/explicit.json"; setcfg "$EX" '{ "direct": ["teamrepo"] }'
|
|
91
91
|
env DIA_WT_ROUTE_CONFIG="$EX" bash "$WT" route "$TEAM" >"$TD/out.txt" 2>&1; RC=$?
|
|
92
|
-
[ "$RC" = 0 ] && ok "显式配置生效(teamrepo
|
|
92
|
+
[ "$RC" = 0 ] && ok "显式配置生效(teamrepo = owner 自合)" || { bad "显式配置未生效(rc=${RC})"; out; }
|
|
93
93
|
env DIA_WT_ROUTE_CONFIG="$EX" bash "$WT" route "$MINE" >"$TD/out.txt" 2>&1; RC=$?
|
|
94
|
-
[ "$RC" = 1 ] && ok "其余配置被忽略(myrepo
|
|
94
|
+
[ "$RC" = 1 ] && ok "其余配置被忽略(myrepo 走 LGTM+bot)" || { bad "应忽略数据根配置(rc=${RC})"; out; }
|
|
95
95
|
rm -f "$EX"
|
|
96
96
|
|
|
97
97
|
echo "== 11. worktree 路径输入 ⇒ 归主仓判定 =="
|
|
98
98
|
setcfg "$CFG" '{ "direct": ["teamrepo"] }'
|
|
99
99
|
git -C "$TEAM" worktree add -q "$TD/teamrepo-worktree--t1" -b t1 >/dev/null 2>&1
|
|
100
100
|
RC="$(run "$TD/teamrepo-worktree--t1")"
|
|
101
|
-
[ "$RC" = 0 ] && ok "worktree 归主仓 →
|
|
101
|
+
[ "$RC" = 0 ] && ok "worktree 归主仓 → owner 自合" || { bad "worktree 判定错(rc=${RC})"; out; }
|
|
102
102
|
see "teamrepo" && ok "报出主仓名" || { bad "未报主仓名"; out; }
|
|
103
103
|
|
|
104
104
|
echo "== 12. 非 git 目录 ⇒ 信息不足(rc=2)=="
|