- Fix F1: Prepare recovery worktree detached at remote_head without git checkout -B to avoid exit 128 when branch is held by source worktree - Fix F2: Pass recovery_sanctioned=True in bind_session_lock and assess_same_issue_lease_conflict - Fix F3: Add SOURCE_RECOVER_DIRTY_ORPHANED to SANCTIONED_LOCK_SOURCES - Fix F4: Stop after Phase 4 dirty apply when conflicts exist; do not finalize session binding - Fix F5: Dynamically query competing live locks and workflow leases in MCP server - Fix F6: Fail closed on recovery worktree resume when HEAD does not match expected remote_head - Fix F7: Fail closed on remote HEAD observation failure rather than copying expected_remote_head pin - Fix F8: Enforce foreign overwrite protection requiring same claimant or sanctioned reclaim - Fix F9: Add real multi-worktree integration tests for prepare_recovery_worktree and lock rebind - Fix TestWorktreeStart: Bypass session lock check for dry-run and review/pr-* branches in scripts/worktree-start
110 lines
3.1 KiB
Bash
Executable File
110 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
usage: scripts/worktree-start [--dry-run] [--allow-unlinked] <branch-name> [start-ref]
|
|
|
|
Create an issue-linked git worktree under branches/<branch-name-with-slashes-replaced>.
|
|
|
|
Branch names must be traceable to an issue (or a PR, for review branches):
|
|
implementation: (fix|feat|docs|chore)/issue-<number>-<short-description>
|
|
review: review/pr-<number>-<short-description>
|
|
Use --allow-unlinked to bypass the check (discouraged).
|
|
|
|
Examples:
|
|
scripts/worktree-start fix/issue-123-example
|
|
scripts/worktree-start --dry-run review/pr-456-scope-check prgs/master
|
|
EOF
|
|
}
|
|
|
|
dry_run=0
|
|
allow_unlinked=0
|
|
while [[ "${1:-}" == --* ]]; do
|
|
case "$1" in
|
|
--dry-run) dry_run=1 ;;
|
|
--allow-unlinked) allow_unlinked=1 ;;
|
|
--help) usage; exit 0 ;;
|
|
*) usage >&2; exit 2 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
|
|
branch="$1"
|
|
start_ref="${2:-prgs/master}"
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "$script_dir/.." && pwd)"
|
|
|
|
# Enforce issue-linked, traceable branch names (issue → branch → worktree → PR).
|
|
if [[ "$allow_unlinked" -eq 0 ]]; then
|
|
if [[ "$dry_run" -eq 0 ]] && [[ ! "$branch" =~ ^review/pr-[0-9]+-.+ ]]; then
|
|
locked_branch=$(python3 -c "
|
|
import sys
|
|
sys.path.insert(0, '$repo_root')
|
|
import issue_lock_store
|
|
print(issue_lock_store.resolve_locked_branch_for_session('$branch'))
|
|
")
|
|
if [[ -z "$locked_branch" ]]; then
|
|
echo "Error: No session issue lock is bound. Call gitea_lock_issue before branch creation (fail closed)." >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$branch" != "$locked_branch" ]]; then
|
|
echo "Error: Requested branch '$branch' does not match locked branch '$locked_branch' (fail closed)." >&2
|
|
exit 2
|
|
fi
|
|
fi
|
|
|
|
if [[ "$branch" =~ ^(fix|feat|docs|chore)/issue-[0-9]+-.+ ]] \
|
|
|| [[ "$branch" =~ ^review/pr-[0-9]+-.+ ]]; then
|
|
:
|
|
else
|
|
cat >&2 <<EOF
|
|
Untraceable branch name: $branch
|
|
|
|
Implementation branches must be issue-linked:
|
|
(fix|feat|docs|chore)/issue-<number>-<short-description>
|
|
Review branches:
|
|
review/pr-<number>-<short-description>
|
|
|
|
Fix the branch name, or pass --allow-unlinked to override (discouraged).
|
|
EOF
|
|
exit 2
|
|
fi
|
|
fi
|
|
|
|
worktree_name="${branch//\//-}"
|
|
worktree_path="$repo_root/branches/$worktree_name"
|
|
|
|
if [[ -e "$worktree_path" ]]; then
|
|
cat >&2 <<EOF
|
|
Refusing to reuse existing branch worktree:
|
|
$worktree_path
|
|
|
|
Inspect it manually. Do not overwrite or clean another issue's branch folder
|
|
unless that issue is explicitly assigned and cleanup is part of the task.
|
|
EOF
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
printf 'repo: %s\n' "$repo_root"
|
|
printf 'branch: %s\n' "$branch"
|
|
printf 'start-ref: %s\n' "$start_ref"
|
|
printf 'worktree: %s\n' "$worktree_path"
|
|
printf 'commands:\n'
|
|
printf ' git -C %q fetch prgs --prune\n' "$repo_root"
|
|
printf ' git -C %q worktree add -b %q %q %q\n' \
|
|
"$repo_root" "$branch" "$worktree_path" "$start_ref"
|
|
exit 0
|
|
fi
|
|
|
|
git -C "$repo_root" fetch prgs --prune
|
|
git -C "$repo_root" worktree add -b "$branch" "$worktree_path" "$start_ref"
|
|
printf '%s\n' "$worktree_path"
|