fix(gitflow): init on an existing repo under the machine-wide hooks lands the socle via a chore/gitflow-adopt merge (T2c)

This commit is contained in:
bastien
2026-09-24 20:25:39 +02:00
parent fe90291cc6
commit 1b20beccda
2 changed files with 42 additions and 8 deletions
+17 -1
View File
@@ -50,7 +50,7 @@ chk "tree CLEAN after init" '[ -z "$(git status --porcelain)" ]'
chk "hook TRACKED in commit" 'git ls-files --error-unmatch .githooks/pre-commit >/dev/null 2>&1' chk "hook TRACKED in commit" 'git ls-files --error-unmatch .githooks/pre-commit >/dev/null 2>&1'
chk "socle IN root commit" 'git show HEAD:.gitignore | grep -qxF ".claude/deploy/PENDING.json"' chk "socle IN root commit" 'git show HEAD:.gitignore | grep -qxF ".claude/deploy/PENDING.json"'
echo "T2b — init existing (master→main rename + adoption commit, hook inactive during it)" echo "T2b — init existing (master→main rename + adoption via chore/gitflow-adopt merge)"
newrepo existing newrepo existing
git symbolic-ref HEAD refs/heads/master # force the repo onto 'master' git symbolic-ref HEAD refs/heads/master # force the repo onto 'master'
echo a > a.txt; printf 'node_modules/\n' > .gitignore; git add -A echo a > a.txt; printf 'node_modules/\n' > .gitignore; git add -A
@@ -64,6 +64,22 @@ chk "existing tree CLEAN" '[ -z "$(git status --porcelain)" ]'
chk "existing hook tracked" 'git ls-files --error-unmatch .githooks/pre-commit >/dev/null 2>&1' chk "existing hook tracked" 'git ls-files --error-unmatch .githooks/pre-commit >/dev/null 2>&1'
chk "kept project rule" 'git show HEAD:.gitignore | grep -qxF "node_modules/"' chk "kept project rule" 'git show HEAD:.gitignore | grep -qxF "node_modules/"'
echo "T2c — init existing under a LIVE pre-commit (global hooks simulated): socle lands via merge"
newrepo live; git symbolic-ref HEAD refs/heads/master
echo a > a.txt; printf 'node_modules/\n' > .gitignore; git add -A
git -c core.hooksPath=/dev/null commit -q -m "pre-existing on master"
_gitflow_write_hook "$WORK/globalhooks" # the machine-wide hook set, as make link installs it
git config core.hooksPath "$WORK/globalhooks" # stands in for git's GLOBAL core.hooksPath during init
# shellcheck disable=SC2034
live_rc=0; GITFLOW_NO_PUSH=1 gitflow_init >/dev/null 2>&1 || live_rc=$?
chk "T2c init succeeds under the live hook (rc 0)" "[ $live_rc -eq 0 ]"
chk "T2c socle reached main via a merge commit" 'git log main --oneline -1 | grep -q "Merge chore/gitflow-adopt"'
chk "T2c .gitignore socle on main" 'git show main:.gitignore | grep -qxF ".claude/deploy/PENDING.json"'
chk "T2c hooks tracked on main" 'git ls-tree -r main --name-only | grep -q "^.githooks/pre-commit$"'
chk "T2c adoption branch deleted" '! git rev-parse --verify -q refs/heads/chore/gitflow-adopt >/dev/null'
chk "T2c develop created from main" '[ "$(git rev-parse develop)" = "$(git rev-parse main)" ]'
chk "T2c repo hook active afterwards" '[ "$(git config core.hooksPath)" = .githooks ]'
echo "T3 — hook blocks/permits after init" echo "T3 — hook blocks/permits after init"
cd "$WORK/fresh" || exit 1 cd "$WORK/fresh" || exit 1
git checkout -q main git checkout -q main
+25 -7
View File
@@ -293,20 +293,38 @@ _gitflow_init_existing() { # has commits → ensure main (rename master)
fi fi
fi fi
git checkout -q "$GITFLOW_MAIN" || return 1 git checkout -q "$GITFLOW_MAIN" || return 1
# commit the socle + versioned hook now, while hooksPath is NOT yet active # The socle (.gitignore + versioned hooks) reaches main through a MERGE: the
# (activation is the last step of gitflow_init) → never self-blocked. # pre-commit hook is global on the machine (BDR-095), so a direct commit on
# main is refused even during init, while a merge commit is hook-exempt.
# Any failure aborts BEFORE develop/hook activation so a partial run can't
# activate the hook and self-block every re-run.
git add -- .gitignore .githooks 2>/dev/null || true git add -- .gitignore .githooks 2>/dev/null || true
# socle commit failure is FATAL — abort BEFORE develop/hook-activation so a
# partial run can't activate the hook and self-block every re-run (was a bug:
# the `|| commit` form swallowed the failure, then init activated the hook).
if ! git diff --cached --quiet -- .gitignore .githooks 2>/dev/null; then if ! git diff --cached --quiet -- .gitignore .githooks 2>/dev/null; then
git commit -q -m "chore: adopt gitflow socle + pre-commit hook" \ _gitflow_adopt_socle || return 1
|| { echo "gitflow_init: socle commit failed — aborting before hook activation (recoverable)" >&2; return 1; }
fi fi
git rev-parse --verify -q "refs/heads/$GITFLOW_DEVELOP" >/dev/null \ git rev-parse --verify -q "refs/heads/$GITFLOW_DEVELOP" >/dev/null \
|| git branch "$GITFLOW_DEVELOP" "$GITFLOW_MAIN" || git branch "$GITFLOW_DEVELOP" "$GITFLOW_MAIN"
} }
# Commit the staged socle on chore/gitflow-adopt (a working branch, so the
# pre-commit allows it), merge it --no-ff into main (a merge commit runs no
# pre-commit), delete the branch. The branch forks off main because develop
# does not exist yet at init time.
_gitflow_adopt_socle() {
local br="chore/gitflow-adopt"
if git rev-parse --verify -q "refs/heads/$br" >/dev/null; then
echo "gitflow_init: '$br' already exists (earlier run) — merge or delete it, then re-run" >&2
return 1
fi
git checkout -q -b "$br" || return 1
git commit -q -m "chore: adopt gitflow socle + versioned hooks" \
|| { echo "gitflow_init: socle commit failed — aborting before hook activation (recoverable)" >&2; return 1; }
git checkout -q "$GITFLOW_MAIN" || return 1
git merge --no-ff -q -m "Merge $br into $GITFLOW_MAIN" "$br" \
|| { echo "gitflow_init: socle merge into $GITFLOW_MAIN failed — aborting before hook activation" >&2; return 1; }
git branch -q -d "$br"
}
# gitflow_init [msg] → idempotent. Order matters (full BLK-010 closure): # gitflow_init [msg] → idempotent. Order matters (full BLK-010 closure):
# reconcile .gitignore + write the versioned hook FIRST, so the fresh root # reconcile .gitignore + write the versioned hook FIRST, so the fresh root
# commit / existing adoption commit EMBED them; activate the hook LAST so the # commit / existing adoption commit EMBED them; activate the hook LAST so the