diff --git a/.delphi/manifest.yml b/.delphi/manifest.yml index fb1aa03..484f83d 100644 --- a/.delphi/manifest.yml +++ b/.delphi/manifest.yml @@ -6,7 +6,7 @@ # MINOR something new that existing clones need nothing for # MAJOR clones need cleanup (MINOR while 0.x): add .delphi/migrations/-.sh, which # setup.sh runs once per workspace in each clone (rules in Delphi's CLAUDE.md) -version: 2.1.0 +version: 2.1.1 files: # repo-relative paths; a trailing / means everything under it - .delphi/manifest.yml - .delphi/.gitattributes diff --git a/.delphi/new-worktree.sh b/.delphi/new-worktree.sh index 6e53461..ded2452 100755 --- a/.delphi/new-worktree.sh +++ b/.delphi/new-worktree.sh @@ -2,7 +2,8 @@ # .delphi/new-worktree.sh []: create or reuse worktrees// from # the bare store .delphi/setup.sh made at worktrees//.bare (never the clone at repos/), and # print its path. An existing branch (local or on origin) is checked out; a new one starts from -# (default: origin's default branch). Runs on bash 3.2 and Git Bash. +# (default: origin's default branch; a only on origin is used as origin/), and the script +# fails unless the worktree ends up on . Runs on bash 3.2 and Git Bash. set -euo pipefail # shellcheck source-path=SCRIPTDIR source=paths.sh . "$(dirname "$0")/paths.sh" @@ -18,7 +19,13 @@ elif g rev-parse -q --verify "refs/heads/$branch" >/dev/null; then g worktree add -q "$dest" "$branch" elif g rev-parse -q --verify "refs/remotes/origin/$branch" >/dev/null; then g worktree add -q --track -b "$branch" "$dest" "origin/$branch" -else +else # a only on origin goes as origin/: given the bare name, Git 2.43 checks out , not -b + if ! g rev-parse -q --verify "$base^{commit}" >/dev/null && g rev-parse -q --verify "refs/remotes/origin/$base" >/dev/null; then + base=origin/$base + fi g worktree add -q --no-track -b "$branch" "$dest" "$base" fi +on=$(git -C "$dest" branch --show-current) +[ "$on" = "$branch" ] || { echo "new-worktree: worktrees/$repo/$branch is on '${on:-a detached HEAD}', not '$branch';" \ + "remove it (git -C worktrees/$repo/.bare worktree remove ../$branch) and rerun" >&2 && exit 1; } echo "$dest" diff --git a/.delphi/versions b/.delphi/versions index 1c95e06..e21026d 100644 --- a/.delphi/versions +++ b/.delphi/versions @@ -19,3 +19,4 @@ 1.2.0 d139aaf9f4a5 worktrees come from their own bare store, worktrees//.bare, separate from the plain clone at repos/; setup.sh makes the store and the default-branch worktree again; an older bare store at repos/ is left as it is 2.0.0 750acf945979 a bare repos/ from before 1.1.0 becomes the worktree store, worktrees//.bare, with its worktrees, and repos/ is cloned normally (migration; skipped with a message if it has no origin or work not on origin) 2.1.0 84b00128fc22 add a harness-independent per-command workspace bootstrap for Codex +2.1.1 66714104d7a3 new-worktree.sh passes a only on origin as origin/ (Git 2.43 checked out instead of -b) and fails unless the worktree is on (#85) diff --git a/tests/e2e.sh b/tests/e2e.sh index fd9d04f..f2751e4 100644 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -1073,11 +1073,18 @@ nw code feat >/dev/null && [ "$(git -C "$w/worktrees/code/feat" rev-parse HEAD)" [ "$(git -C "$w/worktrees/code/feat" rev-parse --abbrev-ref '@{u}')" = origin/feat ] || fail "existing branch not tracking origin" nw code 2-dev origin/develop >/dev/null && [ "$(git -C "$w/worktrees/code/2-dev" rev-parse HEAD)" = "$(git -C "$t/code" rev-parse develop)" ] || fail " ignored" +! git -C "$w/worktrees/code/.bare" rev-parse -q --verify refs/heads/develop >/dev/null || fail "store already has a local develop" +nw code 3-dev develop >/dev/null && [ "$(git -C "$w/worktrees/code/3-dev" branch --show-current)" = 3-dev ] && + [ "$(git -C "$w/worktrees/code/3-dev" rev-parse HEAD)" = "$(git -C "$t/code" rev-parse develop)" ] || fail " only on origin: not on the new branch" +! git -C "$w/worktrees/code/.bare" rev-parse -q --verify refs/heads/develop >/dev/null || fail " only on origin: left a local develop" +git -C "$w/worktrees/code/.bare" worktree add -q --detach "$w/worktrees/code/4-off" origin/develop +! nw code 4-off >/dev/null 2>"$t/err" || fail "worktree off its branch not refused" +grep -q "is on 'a detached HEAD', not '4-off'" "$t/err" || fail "worktree off its branch refusal unexplained: $(cat "$t/err")" [ "$(nw code feat)" = "$w/worktrees/code/feat" ] || fail "rerun did not reuse" [ "$(git -C "$w/worktrees/code/$def" branch --show-current)" = "$def" ] || fail "default worktree left its branch" { [ "$(git -C "$w/repos/code" branch --show-current)" = "$def" ] && [ "$(git -C "$w/repos/code" worktree list | wc -l | tr -d " ")" = 1 ] && ! git -C "$w/repos/code" rev-parse -q --verify refs/heads/1-new >/dev/null; } || fail "new-worktree touched the clone at repos/code" -ok "new-worktree.sh makes new branches from the default or , checks out existing ones, reuses on rerun, never touching repos/" +ok "new-worktree.sh makes new branches from the default or (local or only on origin), checks out existing ones, reuses on rerun, refuses a worktree off its branch, never touching repos/" # skill scripts read the workspace from $DELPHI_ROOT (the session hook evals paths.sh's export line) and every folder from paths.sh (. "$w/.delphi/paths.sh" && [ "$DELPHI_ROOT" = "$w" ] && [ "$DELPHI_REPOS" = "$w/repos" ] && [ "$DELPHI_WORKTREES" = "$w/worktrees" ] && [ "$DELPHI_REFERENCES" = "$w/references" ] && [ "$DELPHI_WORKSPACES" = "$w/workspaces" ]) ||