fix(hooks): wt-пары — позиция, switch-приоритет, корень/предок, --reason (T1.1-T1.4, #63)
This commit is contained in:
parent
8d8dd89b8a
commit
e064de7c9d
@ -269,14 +269,14 @@ def _branch(w):
|
|||||||
class _State:
|
class _State:
|
||||||
def __init__(self, cwd, out, depth, wts):
|
def __init__(self, cwd, out, depth, wts):
|
||||||
self.cur, self.out, self.depth, self.dirs = cwd, out, depth, []
|
self.cur, self.out, self.depth, self.dirs = cwd, out, depth, []
|
||||||
self.wts = wts # [(путь нового worktree, ветка)] из git worktree add -b|-B в этой же команде
|
self.wts = wts # [(путь, ветка, позиция в out)] из git worktree add -b|-B в этой же команде
|
||||||
|
|
||||||
|
|
||||||
def parse(cmd, cwd, _out=None, _depth=0, _wts=None):
|
def parse(cmd, cwd, _out=None, _depth=0, _wts=None):
|
||||||
"""Цели записи команды Bash в порядке выполнения. Чистая функция: файловую систему не читает.
|
"""Цели записи команды Bash в порядке выполнения. Чистая функция: файловую систему не читает.
|
||||||
cwd — каталог, от которого считаются относительные пути (None — неизвестен).
|
cwd — каталог, от которого считаются относительные пути (None — неизвестен).
|
||||||
Новые worktree из git worktree add -b попадают в _wts (путь → ветка): decide_guard оценивает
|
Новые worktree из git worktree add -b попадают в _wts (путь → ветка + позиция): decide_guard
|
||||||
по ним цели под ещё не созданным каталогом worktree."""
|
оценивает по ним только цели, идущие в команде после создания worktree."""
|
||||||
out = [] if _out is None else _out
|
out = [] if _out is None else _out
|
||||||
if _depth > 5:
|
if _depth > 5:
|
||||||
return out
|
return out
|
||||||
@ -525,13 +525,13 @@ def _git(args, st, cur):
|
|||||||
if found:
|
if found:
|
||||||
add(Target("switch", path, name))
|
add(Target("switch", path, name))
|
||||||
elif sub == "worktree":
|
elif sub == "worktree":
|
||||||
_worktree(rest, path, st.wts)
|
_worktree(rest, path, st)
|
||||||
elif sub == "push":
|
elif sub == "push":
|
||||||
_push(rest, path, add)
|
_push(rest, path, add)
|
||||||
|
|
||||||
|
|
||||||
def _worktree(rest, repo, wts):
|
def _worktree(rest, repo, st):
|
||||||
"""git worktree add <путь> -b|-B <ветка>: (путь нового worktree, ветка) для целей той же команды."""
|
"""git worktree add <путь> -b|-B <ветка>: (путь, ветка, позиция в целях) для последующих целей команды."""
|
||||||
if not rest or rest[0][0] != "add":
|
if not rest or rest[0][0] != "add":
|
||||||
return
|
return
|
||||||
branch, pos, k = None, [], 1
|
branch, pos, k = None, [], 1
|
||||||
@ -541,12 +541,15 @@ def _worktree(rest, repo, wts):
|
|||||||
branch = _branch(rest[k + 1])
|
branch = _branch(rest[k + 1])
|
||||||
k += 2
|
k += 2
|
||||||
continue
|
continue
|
||||||
|
if t in ("--reason",) and k + 1 < len(rest):
|
||||||
|
k += 2
|
||||||
|
continue
|
||||||
if not t.startswith("-"):
|
if not t.startswith("-"):
|
||||||
pos.append(rest[k])
|
pos.append(rest[k])
|
||||||
k += 1
|
k += 1
|
||||||
if branch is None or not pos or pos[0][1] or repo is None:
|
if branch is None or not pos or pos[0][1] or repo is None:
|
||||||
return
|
return
|
||||||
wts.append((_join(repo, pos[0][0]), branch))
|
st.wts.append((_join(repo, pos[0][0]), branch, len(st.out)))
|
||||||
|
|
||||||
|
|
||||||
def _push(rest, path, add):
|
def _push(rest, path, add):
|
||||||
@ -639,17 +642,17 @@ WT_HINT = "Работай в worktree на ветке (superpowers:using-git-wor
|
|||||||
def decide_guard(tool, ti, cwd):
|
def decide_guard(tool, ti, cwd):
|
||||||
"""Сообщение блокировки guard-main или None."""
|
"""Сообщение блокировки guard-main или None."""
|
||||||
cwd = cwd or None
|
cwd = cwd or None
|
||||||
wts = [] # [(реальный путь нового worktree, ветка)] из git worktree add -b|-B в этой же команде
|
wts = [] # [(реальный путь, ветка, позиция в targets)] из git worktree add -b|-B в этой же команде
|
||||||
if tool == "Bash":
|
if tool == "Bash":
|
||||||
targets = parse(ti.get("command") or "", cwd, _wts=wts)
|
targets = parse(ti.get("command") or "", cwd, _wts=wts)
|
||||||
wts = [(os.path.realpath(p), b) for p, b in wts]
|
wts = [(os.path.realpath(p), b, pos) for p, b, pos in wts]
|
||||||
elif tool in ("Edit", "Write", "MultiEdit"):
|
elif tool in ("Edit", "Write", "MultiEdit"):
|
||||||
fp = ti.get("file_path") or ""
|
fp = ti.get("file_path") or ""
|
||||||
targets = [Target("file", _abs(cwd, fp), tool)] if fp else []
|
targets = [Target("file", _abs(cwd, fp), tool)] if fp else []
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
over = {} # корень → ветка после git switch/checkout -b в этой же команде (None — не определена)
|
over = {} # корень → ветка после git switch/checkout -b в этой же команде (None — не определена)
|
||||||
for t in targets:
|
for idx, t in enumerate(targets):
|
||||||
if t.kind == "switch":
|
if t.kind == "switch":
|
||||||
r = resolve(t.path) if t.path else None
|
r = resolve(t.path) if t.path else None
|
||||||
if r:
|
if r:
|
||||||
@ -666,11 +669,14 @@ def decide_guard(tool, ti, cwd):
|
|||||||
if not r:
|
if not r:
|
||||||
continue
|
continue
|
||||||
root, branch, real = r
|
root, branch, real = r
|
||||||
wt = next((b for p, b in reversed(wts) if real == p or real.startswith(p + os.sep)), None)
|
if root in over: # явный switch/checkout в этой же команде выигрывает у пары worktree (R-1)
|
||||||
if wt is not None: # цель — в worktree, который эта же команда создаёт на новой ветке (#63)
|
|
||||||
branch = wt
|
|
||||||
elif root in over:
|
|
||||||
branch = over[root]
|
branch = over[root]
|
||||||
|
else:
|
||||||
|
wt = next((b for p, b, pos in reversed(wts)
|
||||||
|
if idx >= pos and not (p == root or root.startswith(p + os.sep))
|
||||||
|
and (real == p or real.startswith(p + os.sep))), None)
|
||||||
|
if wt is not None: # цель идёт после worktree add и лежит в нём — ветка из add (#63)
|
||||||
|
branch = wt
|
||||||
if t.kind == "git":
|
if t.kind == "git":
|
||||||
if t.op.startswith("push>") or t.op == "push --all":
|
if t.op.startswith("push>") or t.op == "push --all":
|
||||||
return "BLOCKED [guard-main]: git %s — репозиторий %s. В main только через PR." % (
|
return "BLOCKED [guard-main]: git %s — репозиторий %s. В main только через PR." % (
|
||||||
|
|||||||
@ -30,6 +30,14 @@ assert_eq "git worktree add .claude/worktrees/… на main" "$(rc "$S/n" Bash '
|
|||||||
assert_eq "worktree add -b && cd && openspec archive (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && cd .claude/worktrees/z && openspec archive y -y')" 0
|
assert_eq "worktree add -b && cd && openspec archive (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && cd .claude/worktrees/z && openspec archive y -y')" 0
|
||||||
assert_eq "worktree add -C && cd && openspec archive (#63)" "$(rc "$S/ws" Bash "git -C $S/n worktree add .claude/worktrees/q -b feat/q origin/main && cd $S/n/.claude/worktrees/q && openspec archive y -y")" 0
|
assert_eq "worktree add -C && cd && openspec archive (#63)" "$(rc "$S/ws" Bash "git -C $S/n worktree add .claude/worktrees/q -b feat/q origin/main && cd $S/n/.claude/worktrees/q && openspec archive y -y")" 0
|
||||||
assert_eq "worktree add -b без cd: openspec archive в main — блок (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && openspec archive y -y')" 2
|
assert_eq "worktree add -b без cd: openspec archive в main — блок (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && openspec archive y -y')" 2
|
||||||
|
# fix-round 1 ревью PR #71 (T1.1–T1.4, R-1): пара «путь→ветка» — только для целей после add; switch и корень выигрывают
|
||||||
|
assert_eq "wt-пара не действует на цель до worktree add (T1.1)" "$(rc "$S/m" Bash 'echo x > .claude/worktrees/pre/f.md && git worktree add .claude/worktrees/pre -b feat/pre origin/main')" 2
|
||||||
|
assert_eq "явный switch после worktree add — мутация по main (T1.2)" "$(rc "$S/m" Bash 'git worktree add .claude/worktrees/s -b feat/s origin/main && cd .claude/worktrees/s && git checkout main && git commit -m x')" 2
|
||||||
|
assert_eq "worktree add . — пара не действует (T1.3)" "$(rc "$S/m" Bash 'git worktree add . -b feat/dot && git commit -m x')" 2
|
||||||
|
assert_eq "worktree add .. — пара не действует (T1.3)" "$(rc "$S/m" Bash 'git worktree add .. -b feat/up || git commit -m x')" 2
|
||||||
|
assert_eq "worktree add --reason <value> (T1.4)" "$(rc "$S/m" Bash 'git worktree add --reason foo x -b feat/x origin/main && cd x && openspec archive y -y')" 0
|
||||||
|
assert_eq "wt-пара не действует на основной checkout через -C (T1.6e)" "$(rc "$S/m" Bash "git worktree add .claude/worktrees/e -b feat/e origin/main && git -C $S/m commit -m x")" 2
|
||||||
|
assert_eq "граница префикса пути пары (T1.6f)" "$(rc "$S/m" Bash 'git worktree add foo -b feat/foo && echo x > foobar/f.md')" 2
|
||||||
# файловые цели Bash
|
# файловые цели Bash
|
||||||
assert_eq "echo > в main" "$(rc "$S/m" Bash 'echo x > notes.md')" 2
|
assert_eq "echo > в main" "$(rc "$S/m" Bash 'echo x > notes.md')" 2
|
||||||
assert_eq "make 2>build.log в main" "$(rc "$S/m" Bash 'make 2>build.log')" 2
|
assert_eq "make 2>build.log в main" "$(rc "$S/m" Bash 'make 2>build.log')" 2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user