feat(gitflow): manual-push mode honoured by the lib, quiet unpushed-guard
`gitflow.autopush false` (human-set git config) now means "nothing is pushed" end to end, not only in the post-commit/post-merge hooks: - lib/gitflow.sh: `_gitflow_push_off` is the single reader of GITFLOW_NO_PUSH / gitflow.autopush for the lib's push sites; `start` and `finish` stop pushing in manual mode. `gitflow_delete` checks out the base that contains the branch and drops a lagging upstream before `git branch -d` (LRN-161: `-d` judges against the upstream when set). Skipped remote deletes say `left in place`; `_gitflow_sync_base` replaces the silent `pull --ff-only || true` and warns when a base is behind origin and cannot fast-forward. - hooks/unpushed-guard.sh: manual mode is silent at Stop and gives one `ℹ manual push mode:` line at SessionStart counting every local branch; an unparseable value is named and treated as auto. - CLAUDE.global.md: manual-push mode doctrine, "ahead = defect" scoped to auto mode. - Tests: gitflow-test T18m block (T18m0, T18i-T18o, 7 cases), unpushed-guard T10-T16. Follow-ups (TODO.md): run B push-guard hook + settings deny widening + banner; run C skills that push on their own (/close STEP 5C, …). Do not enable manual mode on the work machine before B and C land.
This commit is contained in:
+36
-1
@@ -9,6 +9,10 @@
|
||||
# SessionStart also reports uncommitted changes (a dead session leaves some
|
||||
# behind); Stop reports unpushed commits only, since a dirty tree mid-work is
|
||||
# the normal state at a turn end.
|
||||
#
|
||||
# Manual-push mode (git config gitflow.autopush false, human-set): unpushed
|
||||
# work is expected, so Stop stays silent; SessionStart gives one info line
|
||||
# counting every local branch, with the branches to push by hand.
|
||||
set -u
|
||||
|
||||
payload=$(cat 2>/dev/null)
|
||||
@@ -19,9 +23,37 @@ cd "$cwd" 2>/dev/null || exit 0
|
||||
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
||||
br=$(git symbolic-ref --short -q HEAD 2>/dev/null) || exit 0
|
||||
|
||||
raw=$(git config gitflow.autopush 2>/dev/null)
|
||||
manual=0; invalid=0
|
||||
[ "$(git config --bool --default true gitflow.autopush 2>/dev/null)" = false ] && manual=1
|
||||
[ -n "$raw" ] && ! git config --bool gitflow.autopush >/dev/null 2>&1 && invalid=1
|
||||
[ "$manual" = 1 ] && [ "$event" != SessionStart ] && exit 0 # BDR-087: info at start only
|
||||
|
||||
# Local branches holding commits no remote has, one per line.
|
||||
ahead_branches() {
|
||||
local b
|
||||
while IFS= read -r b; do
|
||||
[ "$(git rev-list --count "$b" --not --remotes 2>/dev/null)" -gt 0 ] && echo "$b"
|
||||
done < <(git for-each-ref --format='%(refname:short)' refs/heads)
|
||||
}
|
||||
|
||||
# Manual mode: commits on every local branch that no remote holds.
|
||||
manual_clause() {
|
||||
local n list first
|
||||
n=$(git rev-list --count --branches --not --remotes 2>/dev/null || echo 0)
|
||||
[ "$n" -gt 0 ] || return 0
|
||||
if ! git remote get-url origin >/dev/null 2>&1; then
|
||||
echo "no 'origin' remote, $n commit(s) on this disk only"
|
||||
return
|
||||
fi
|
||||
list=$(ahead_branches); first=$(printf '%s\n' "$list" | head -n 1)
|
||||
echo "$n commit(s) not on origin ($(printf '%s' "$list" | paste -sd, - | sed 's/,/, /g')), push by hand: git push -u origin $first"
|
||||
}
|
||||
|
||||
# Commits that no remote holds, as one clause; empty when everything is pushed.
|
||||
unpushed_clause() {
|
||||
local up n
|
||||
[ "$manual" = 1 ] && { manual_clause; return; }
|
||||
if ! git remote get-url origin >/dev/null 2>&1; then
|
||||
echo "no 'origin' remote, every commit lives on this disk only"
|
||||
return
|
||||
@@ -41,9 +73,12 @@ if [ "$event" = "SessionStart" ]; then
|
||||
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
||||
[ "$dirty" -gt 0 ] && msg="${msg:+$msg; }$dirty uncommitted change(s) in $cwd"
|
||||
fi
|
||||
if [ "$invalid" = 1 ] && [ "$event" = "SessionStart" ]; then
|
||||
msg="${msg:+$msg; }gitflow.autopush='$raw' is not a boolean, treated as auto (pushes run)"
|
||||
fi
|
||||
[ -n "$msg" ] || exit 0
|
||||
|
||||
msg="⚠ unpushed work: $msg"
|
||||
if [ "$manual" = 1 ]; then msg="ℹ manual push mode: $msg"; else msg="⚠ unpushed work: $msg"; fi
|
||||
if [ "$event" = "SessionStart" ]; then
|
||||
jq -cn --arg m "$msg" \
|
||||
'{systemMessage: $m, hookSpecificOutput: {hookEventName: "SessionStart", additionalContext: $m}}'
|
||||
|
||||
Reference in New Issue
Block a user