@codyswann/lisa 4.34.2 → 4.34.4
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/all/copy-overwrite/scripts/lisa-work-item.mjs +14 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +6 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +10 -7
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +5 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +134 -0
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +134 -0
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +134 -0
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +134 -0
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +134 -0
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +35 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +35 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +35 -0
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.claude/hooks/worktree-create.sh +174 -12
|
@@ -199,6 +199,41 @@ classifyPreWorkCandidate({ laneType, labels, body, humanNeededLabel, statedBlock
|
|
|
199
199
|
|
|
200
200
|
### Phase 3 — Process the first eligible ready Issue
|
|
201
201
|
|
|
202
|
+
#### 3.0 Human-hold gate (absolute, and it runs before every other gate)
|
|
203
|
+
|
|
204
|
+
A person parks an item by putting `[lisa-human-gate]` in its description. That marker used to be
|
|
205
|
+
read only for candidates **outside** `$READY` (Phase 2's blocker re-probe), so an item already in
|
|
206
|
+
the ready lane was claimed with the hold never consulted — one was dispatched and fully implemented
|
|
207
|
+
before a human vetoed the merge. The check was correct; it was unreachable from the path that
|
|
208
|
+
matters.
|
|
209
|
+
|
|
210
|
+
Run this **first**, ahead of the repo-scope gate (3a.0) and the leaf-only gate (3a), for every ready
|
|
211
|
+
candidate. Ordering is load-bearing for the same reason it is inside `classifyPreWorkCandidate`: no
|
|
212
|
+
other gate's verdict — however conclusive — may promote an item a person parked.
|
|
213
|
+
|
|
214
|
+
1. **Classify with `classifyReadyCandidate(...)`** from `scripts/intake-blocker-reprobe.mjs`. It
|
|
215
|
+
shares the gate test with the pre-work classifier deliberately — two copies of a substring test
|
|
216
|
+
drift, and a drifted gate fails *silently*, by quietly ceasing to match. Do **not** re-implement
|
|
217
|
+
the test here, and do **not** key it on `reason=`: markers in the wild carry no `reason=` key at
|
|
218
|
+
all and sit anywhere in the body, so a structured parse would miss them while appearing to work
|
|
219
|
+
on every item that happens to have one.
|
|
220
|
+
2. **On `claimable: false` with reason `human-gate`, do not claim and do not dispatch.**
|
|
221
|
+
3. **Reconcile the lane; do not merely skip.** Skipping alone leaves the item in `$READY`, re-judged
|
|
222
|
+
and re-rejected every cycle forever and seen by nothing — `lisa-repair-intake` sweeps items that
|
|
223
|
+
are **not** in the ready role and excludes gated ones outright, so a ready-and-gated item falls
|
|
224
|
+
outside its filter twice over. Call
|
|
225
|
+
`planHumanGateReconciliation({ labels, body, humanNeededLabel, readyLabel, alreadyNotified })`
|
|
226
|
+
and apply exactly the actions it returns: remove `$READY`, add the configured human-needed
|
|
227
|
+
marker, and post `formatHumanGateNote()` once. The planner is idempotent by state, so an item
|
|
228
|
+
already out of the lane and already marked yields no second mutation and no second comment. This
|
|
229
|
+
is the same repair the leaf-only gate already performs for a ready item that must not be
|
|
230
|
+
dispatched.
|
|
231
|
+
4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
|
|
232
|
+
distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
|
|
233
|
+
mutation nobody can see afterwards is the same class of problem this gate exists to fix.
|
|
234
|
+
5. **Continue to the next candidate.** A held item does not end the cycle.
|
|
235
|
+
|
|
236
|
+
|
|
202
237
|
#### 3a.0 Repo-scope gate (claim only current-repo Issues)
|
|
203
238
|
|
|
204
239
|
A Linear team can oversee multiple repos (`frontend` / `backend` / `infrastructure`). This skill claims only Issues for the repo it is running in. Run this gate **before** the leaf-only gate (3a) and the claim (3b), per the `repo-scope-split` rule's "Claim-time repo scoping" section (cite it by slug; do not restate its decision table).
|
|
@@ -10,17 +10,28 @@
|
|
|
10
10
|
# so every git command redirects its output off stdout, and progress goes to
|
|
11
11
|
# stderr (NOT /dev/tty, which is "not configured" in headless `-p`/CI sessions).
|
|
12
12
|
#
|
|
13
|
-
# It mirrors Claude's default layout — <cwd>/.claude/worktrees/<name>
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# itself is done by the `post-checkout` hook that
|
|
17
|
-
# `claude -w` and standalone `cd && claude`
|
|
13
|
+
# It mirrors Claude's default layout — <cwd>/.claude/worktrees/<name> — but the
|
|
14
|
+
# new branch is cut from the repository's INTEGRATION branch, not from whatever
|
|
15
|
+
# the invoking checkout happened to be standing on. See "Choosing the base".
|
|
16
|
+
# The plugin bootstrap itself is done by the `post-checkout` hook that
|
|
17
|
+
# `git worktree add` fires, so `claude -w` and standalone `cd && claude`
|
|
18
|
+
# sessions converge on the same state.
|
|
18
19
|
#
|
|
19
20
|
# Payload (observed, Claude Code stdin):
|
|
20
21
|
# { "name": "<worktree>", "cwd": "<project root>", "hook_event_name": ... }
|
|
22
|
+
#
|
|
23
|
+
# Agent parity: `WorktreeCreate` is a Claude Code hook event and has no
|
|
24
|
+
# counterpart on Codex, Cursor, OpenCode, Copilot or Antigravity — none of them
|
|
25
|
+
# delegate worktree creation to the host project. There is deliberately no
|
|
26
|
+
# projection of this file for those agents; on them, worktrees are created by
|
|
27
|
+
# whatever invokes git directly, and the base-selection rules below do not
|
|
28
|
+
# apply. This is a genuine surface gap, not a parity omission.
|
|
21
29
|
|
|
22
30
|
_log() { echo "$*" >&2; }
|
|
23
31
|
|
|
32
|
+
# The remote whose default branch defines "the integration branch" here.
|
|
33
|
+
_remote="origin"
|
|
34
|
+
|
|
24
35
|
_payload="$(cat)"
|
|
25
36
|
|
|
26
37
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -43,7 +54,21 @@ case "$_name" in
|
|
|
43
54
|
;;
|
|
44
55
|
esac
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
# Which REPOSITORY this hook operates on is decided entirely by the payload's
|
|
58
|
+
# `cwd`. This hook cannot validate that choice — it is handed `{ name, cwd }`
|
|
59
|
+
# and nothing that names the intended repository, so it has no way to tell a
|
|
60
|
+
# correct `cwd` from one that drifted. What it can do is refuse to be silent
|
|
61
|
+
# about falling back to ambient process state, which is the one part of the
|
|
62
|
+
# repository choice that happens HERE rather than upstream.
|
|
63
|
+
if [ -n "${_cwd:-}" ]; then
|
|
64
|
+
_root="$_cwd"
|
|
65
|
+
else
|
|
66
|
+
_root="$(pwd)"
|
|
67
|
+
_log "WorktreeCreate: WARNING — payload carried no 'cwd'; falling back to the"
|
|
68
|
+
_log " process working directory: $_root"
|
|
69
|
+
_log " If that is not the repository you meant, this worktree is in the wrong one."
|
|
70
|
+
fi
|
|
71
|
+
|
|
47
72
|
_base="$_root/.claude/worktrees"
|
|
48
73
|
_path="$_base/$_name"
|
|
49
74
|
_wt_branch="worktree-$_name"
|
|
@@ -55,23 +80,160 @@ if [ -d "$_path" ]; then
|
|
|
55
80
|
exit 0
|
|
56
81
|
fi
|
|
57
82
|
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Choosing the base
|
|
85
|
+
#
|
|
86
|
+
# This used to be a literal `HEAD`, which made the base of every new worktree a
|
|
87
|
+
# property of ambient session state rather than of the work being handed over: a
|
|
88
|
+
# checkout parked on an unmerged feature branch silently handed each new
|
|
89
|
+
# worktree that other work item's commits, and a PR opened from it presented
|
|
90
|
+
# them as its own. Nothing errored, and no identity check catches it — the
|
|
91
|
+
# directory, the branch name and the binding are all exactly what was asked for.
|
|
92
|
+
# Only the base was wrong, and nothing inspects a base.
|
|
93
|
+
#
|
|
94
|
+
# Resolution is OFFLINE ONLY. This hook runs before a session starts and must
|
|
95
|
+
# never stall on the network, so it reads the LOCAL ref store and nothing else.
|
|
96
|
+
# Precedence, most explicit first:
|
|
97
|
+
#
|
|
98
|
+
# 1. $LISA_WORKTREE_BASE — operator override; wrong here is fatal
|
|
99
|
+
# 2. refs/remotes/<remote>/HEAD — the remote's own declared default branch
|
|
100
|
+
# 3. .lisa.config.json — deploy.branches.production, as a hint
|
|
101
|
+
# 4. HEAD — fallback, and never a silent one
|
|
102
|
+
#
|
|
103
|
+
# origin/HEAD outranks the config hint deliberately. `deploy.branches.production`
|
|
104
|
+
# names a DEPLOY target, which in a dev/staging/prod repository is not the branch
|
|
105
|
+
# work integrates onto; origin/HEAD is that repository's own statement of its
|
|
106
|
+
# default branch, which is.
|
|
107
|
+
|
|
108
|
+
# Fully qualified remote default-branch ref, or nothing when it cannot be
|
|
109
|
+
# resolved offline. Mirrors `remoteDefaultRef` in lisa-work-item.mjs: the symref
|
|
110
|
+
# must point INSIDE refs/remotes/<remote>/ and the ref it names must exist, so a
|
|
111
|
+
# crafted symref cannot redirect the base somewhere arbitrary.
|
|
112
|
+
_resolve_remote_head() {
|
|
113
|
+
_sym="$(git -C "$_root" symbolic-ref --quiet "refs/remotes/$_remote/HEAD" 2>/dev/null)" || return 1
|
|
114
|
+
[ -n "$_sym" ] || return 1
|
|
115
|
+
case "$_sym" in
|
|
116
|
+
"refs/remotes/$_remote/"*) ;;
|
|
117
|
+
*) return 1 ;;
|
|
118
|
+
esac
|
|
119
|
+
git -C "$_root" rev-parse -q --verify "$_sym" >/dev/null 2>&1 || return 1
|
|
120
|
+
printf '%s' "$_sym"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Lisa's configured production branch, resolved to a real ref — remote-tracking
|
|
124
|
+
# first, then local. A configured NAME that resolves to no ref is not usable.
|
|
125
|
+
_resolve_config_base() {
|
|
126
|
+
_cfg="$_root/.lisa.config.json"
|
|
127
|
+
[ -f "$_cfg" ] || return 1
|
|
128
|
+
_cfg_branch="$(jq -r '.deploy.branches.production // empty' <"$_cfg" 2>/dev/null)" || return 1
|
|
129
|
+
[ -n "$_cfg_branch" ] || return 1
|
|
130
|
+
for _cand in "refs/remotes/$_remote/$_cfg_branch" "refs/heads/$_cfg_branch"; do
|
|
131
|
+
if git -C "$_root" rev-parse -q --verify "$_cand" >/dev/null 2>&1; then
|
|
132
|
+
printf '%s' "$_cand"
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
done
|
|
136
|
+
return 1
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_base_ref=""
|
|
140
|
+
_base_source=""
|
|
141
|
+
|
|
142
|
+
if [ -n "${LISA_WORKTREE_BASE:-}" ]; then
|
|
143
|
+
# An explicit override that does not resolve is a configuration error the
|
|
144
|
+
# operator can see and fix. Quietly ignoring it would reintroduce this very
|
|
145
|
+
# bug — a base silently different from the one that was asked for.
|
|
146
|
+
if git -C "$_root" rev-parse -q --verify "$LISA_WORKTREE_BASE" >/dev/null 2>&1; then
|
|
147
|
+
_base_ref="$LISA_WORKTREE_BASE"
|
|
148
|
+
_base_source="LISA_WORKTREE_BASE"
|
|
149
|
+
else
|
|
150
|
+
_log "WorktreeCreate: LISA_WORKTREE_BASE='$LISA_WORKTREE_BASE' does not resolve"
|
|
151
|
+
_log " to a ref in $_root; refusing rather than silently using a different base."
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
if [ -z "$_base_ref" ]; then
|
|
157
|
+
_base_ref="$(_resolve_remote_head)" && _base_source="$_remote/HEAD"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
if [ -z "$_base_ref" ] && command -v jq >/dev/null 2>&1; then
|
|
161
|
+
_base_ref="$(_resolve_config_base)" && _base_source="deploy.branches.production"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
if [ -z "$_base_ref" ]; then
|
|
165
|
+
_base_ref="HEAD"
|
|
166
|
+
_base_source="fallback"
|
|
167
|
+
fi
|
|
168
|
+
|
|
58
169
|
mkdir -p "$_base" 2>/dev/null || true
|
|
59
|
-
_log "Creating worktree $_path (branch: $_wt_branch)…"
|
|
60
170
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
171
|
+
# Announce what was resolved, so a wrong repository or a wrong base is visible
|
|
172
|
+
# AT CREATION instead of being reconstructed hours later from a confused agent.
|
|
173
|
+
# The origin URL can embed a credential (https://user:token@host/...), so strip
|
|
174
|
+
# any userinfo before it reaches a log.
|
|
175
|
+
_origin_url="$(git -C "$_root" remote get-url "$_remote" 2>/dev/null | sed -e 's#://[^/@]*@#://#')"
|
|
176
|
+
_base_sha="$(git -C "$_root" rev-parse --short "$_base_ref" 2>/dev/null)"
|
|
177
|
+
|
|
178
|
+
_log "Creating worktree $_path"
|
|
179
|
+
_log " repository : $_root"
|
|
180
|
+
_log " origin : ${_origin_url:-<none>}"
|
|
181
|
+
_log " branch : $_wt_branch"
|
|
182
|
+
_log " base : $_base_ref @ ${_base_sha:-<unknown>} ($_base_source)"
|
|
183
|
+
|
|
184
|
+
if [ "$_base_source" = "fallback" ]; then
|
|
185
|
+
_log "WorktreeCreate: WARNING — no integration branch could be resolved for this"
|
|
186
|
+
_log " repository, so this worktree is based on whatever branch that checkout was"
|
|
187
|
+
_log " standing on. It may carry another work item's unmerged commits. Check with:"
|
|
188
|
+
_log " git -C $_path log --oneline -5"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Reuse the worktree branch if it already exists, else create it from the
|
|
192
|
+
# resolved base. All git output is kept off stdout (its "Preparing worktree…"
|
|
193
|
+
# line would corrupt the path contract and hang Claude); stderr is captured so a
|
|
194
|
+
# failure can say WHY instead of just that it happened.
|
|
195
|
+
#
|
|
196
|
+
# --no-track matters: without it, branching from refs/remotes/<remote>/<default>
|
|
197
|
+
# sets the integration branch as this branch's upstream, and a later bare
|
|
198
|
+
# `git push` from the worktree would target it.
|
|
64
199
|
if git -C "$_root" show-ref --verify --quiet "refs/heads/$_wt_branch" 2>/dev/null; then
|
|
65
|
-
|
|
200
|
+
_reused=1
|
|
201
|
+
_add_err="$(git -C "$_root" worktree add "$_path" "$_wt_branch" 2>&1 >/dev/null)"
|
|
66
202
|
else
|
|
67
|
-
|
|
203
|
+
_reused=0
|
|
204
|
+
_add_err="$(git -C "$_root" worktree add --no-track -b "$_wt_branch" "$_path" "$_base_ref" 2>&1 >/dev/null)"
|
|
68
205
|
fi
|
|
69
206
|
|
|
70
207
|
if [ ! -d "$_path" ]; then
|
|
71
208
|
_log "WorktreeCreate: git worktree add failed for $_path."
|
|
209
|
+
[ -n "$_add_err" ] && _log " git: $_add_err"
|
|
72
210
|
exit 1
|
|
73
211
|
fi
|
|
74
212
|
|
|
213
|
+
if [ "$_reused" = "1" ]; then
|
|
214
|
+
# A reused branch legitimately carries its own prior work, so its commits
|
|
215
|
+
# ahead of the integration branch are expected. Report, do not refuse.
|
|
216
|
+
_log " reused existing branch $_wt_branch"
|
|
217
|
+
elif [ "$_base_source" != "fallback" ]; then
|
|
218
|
+
# Post-condition. A freshly cut branch is AT its base, so this is 0 by
|
|
219
|
+
# construction — which is exactly why it is worth asserting: if base selection
|
|
220
|
+
# ever regresses, this is what catches it, loudly, instead of handing back a
|
|
221
|
+
# plausible-looking tree with someone else's commits in it.
|
|
222
|
+
_ahead="$(git -C "$_path" rev-list --left-right --count "$_base_ref...HEAD" 2>/dev/null | awk '{print $2}')"
|
|
223
|
+
case "${_ahead:-0}" in
|
|
224
|
+
"" | 0) ;;
|
|
225
|
+
*[!0-9]*) ;;
|
|
226
|
+
*)
|
|
227
|
+
_log "WorktreeCreate: REFUSING — the new worktree carries $_ahead commit(s) that"
|
|
228
|
+
_log " are not on $_base_ref. That means it inherited another work item's"
|
|
229
|
+
_log " unmerged work. Removing it rather than handing back a wrong tree."
|
|
230
|
+
git -C "$_root" worktree remove --force "$_path" >/dev/null 2>&1
|
|
231
|
+
git -C "$_root" branch -D "$_wt_branch" >/dev/null 2>&1
|
|
232
|
+
exit 1
|
|
233
|
+
;;
|
|
234
|
+
esac
|
|
235
|
+
fi
|
|
236
|
+
|
|
75
237
|
# THE ONLY thing on stdout: the absolute worktree path.
|
|
76
238
|
printf '%s\n' "$_path"
|
|
77
239
|
exit 0
|