From eecc8e126d596be64f7dfd37a2047a3018560980 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Sun, 1 Nov 2026 18:18:44 -0800 Subject: [PATCH] fix(skills): support git before 2.31 in the worktree hook scripts `git rev-parse --path-format=absolute` does not exist before git 2.31. In refuse-main-checkout.sh that failure, under set -e, failed the pre-commit hook everywhere, worktrees included, so --no-verify became the only way to commit. In worktree-remove.sh it exited 0 and silently left every worktree in place. Both now resolve paths physically with cd + pwd -P. Measured with a git shim that rejects --path-format: the guard refuses in the primary checkout and commits in a worktree, a worktree subdirectory, and under CI; the remove hook removes the worktree and its merged branch. Ported from taskless/taskless#420, where the cloud repo's review found it. --- .../worktrees-pnpm/scripts/refuse-main-checkout.sh | 10 +++++++--- .../skills/worktrees-pnpm/scripts/worktree-remove.sh | 7 ++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.agents/skills/worktrees-pnpm/scripts/refuse-main-checkout.sh b/.agents/skills/worktrees-pnpm/scripts/refuse-main-checkout.sh index afa41c48..f02a708f 100755 --- a/.agents/skills/worktrees-pnpm/scripts/refuse-main-checkout.sh +++ b/.agents/skills/worktrees-pnpm/scripts/refuse-main-checkout.sh @@ -22,9 +22,13 @@ if [ -n "${CI:-}" ]; then exit 0 fi -dirs=$(git rev-parse --path-format=absolute --git-dir --git-common-dir) -git_dir=$(printf '%s\n' "$dirs" | sed -n 1p) -common_dir=$(printf '%s\n' "$dirs" | sed -n 2p) +# Both paths are resolved physically rather than with +# `rev-parse --path-format=absolute`, which git before 2.31 rejects. Under +# `set -e` that rejection would fail the hook everywhere, worktrees included, +# leaving --no-verify as the only way to commit. `pwd -P` also settles symlinks +# and relative output, so the comparison is path to path. +git_dir=$(cd "$(git rev-parse --git-dir)" && pwd -P) +common_dir=$(cd "$(git rev-parse --git-common-dir)" && pwd -P) if [ "$git_dir" = "$common_dir" ]; then top=$(git rev-parse --show-toplevel) diff --git a/.agents/skills/worktrees-pnpm/scripts/worktree-remove.sh b/.agents/skills/worktrees-pnpm/scripts/worktree-remove.sh index 35a58a6b..bbd483f0 100755 --- a/.agents/skills/worktrees-pnpm/scripts/worktree-remove.sh +++ b/.agents/skills/worktrees-pnpm/scripts/worktree-remove.sh @@ -22,7 +22,12 @@ worktree_path=$(printf '%s' "$payload" | jq -r '.worktree_path // empty') [ -n "$worktree_path" ] || exit 0 [ -d "$worktree_path" ] || exit 0 -repo_root=$(git -C "$worktree_path" rev-parse --path-format=absolute --git-common-dir 2>/dev/null | xargs dirname) || exit 0 +# Resolved physically rather than with `rev-parse --path-format=absolute`, which +# git before 2.31 rejects; that failure exited 0 here and silently left every +# worktree in place. --git-common-dir can print a path relative to the worktree, +# so it is resolved from inside it. +common_dir=$(cd "$worktree_path" && cd "$(git rev-parse --git-common-dir 2>/dev/null)" && pwd -P) || exit 0 +repo_root=$(dirname "$common_dir") # Refuse to discard uncommitted changes. `git worktree remove` without --force # already refuses, but check explicitly so the reason is greppable in debug logs.