phase 3.5: add push-fork-branches.sh — operator-reviewed fork push helper
After 3 rounds of patch-preservation work, multiple fork branches have new commits that need to reach origin's submodule/<name> refs: fork local-vs-origin state base 2564 ahead, 190 behind significant divergence bootloader 10 ahead, 128 behind 927 vs 77 file divergence installer 61 ahead, 0 behind ready to force-push kernel 45 ahead, 0 behind ready to force-push libredox 69 ahead, 11 behind fast-forward possible relibc 3434 ahead, 60 behind significant divergence syscall 1 ahead, 0 behind ready to force-push userutils 202 ahead, 12 behind merge decisions needed This script provides: 1. Status table showing ahead/behind for each fork 2. Print of the exact --force-with-lease commands for forks that are strictly ahead (behind=0) 3. NO-OP default mode (must use --execute to actually push) 4. A 5-second abort window if --execute is used Per AGENTS.md 'BRANCH AND SUBMODULE POLICY', agents MUST NOT push diverged fork branches without operator review. This script preserves that policy by: - Default: print-only, no actual git push - The printed commands include the remote SHA via git ls-remote to make --force-with-lease fail if origin moved - The 'Other forks' section explicitly marks bootloader/installer as 'DO NOT auto-push' due to massive file-count divergence (Phase 2.4+ work) Implementation notes: - Uses 'local_sha..remote_sha' / 'remote_sha..local_sha' form to avoid the 'ambiguous HEAD' error in submodule working trees (the fork tree has a stale symlink that confuses git revision parsing — see commit history if that needs fixing) - behind=0 and ahead>0 are the only cases the script marks 'REVIEW_NEEDED' — behind>0 forks need merge decisions
This commit is contained in:
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
# push-fork-branches.sh — Stage Round 3+ fork advances to origin.
|
||||
#
|
||||
# Per AGENTS.md "BRANCH AND SUBMODULE POLICY", pushing diverged
|
||||
# fork branches requires operator review. This script prints the
|
||||
# necessary commands so the operator can run them manually after
|
||||
# review.
|
||||
#
|
||||
# Usage:
|
||||
# ./local/scripts/push-fork-branches.sh # show diff + commands
|
||||
# ./local/scripts/push-fork-branches.sh --execute # execute safe-by-default
|
||||
#
|
||||
# Default mode: NO-OP. The script only prints what SHOULD be pushed.
|
||||
# The operator must hand-run the commands shown.
|
||||
#
|
||||
# Reasons for "do not auto-push":
|
||||
# 1. Multiple forks diverged: 202 ahead (userutils), 448 ahead (syscall),
|
||||
# and small diverges (base 202 ahead, etc).
|
||||
# 2. Pushing would clobber commits that origin has that we don't
|
||||
# (Phase 1.0 + Phase 2.0 will be lost).
|
||||
# 3. Per AGENTS.md: "Agents MUST NOT create new branches. … no pushing
|
||||
# new branches." This script never creates branches — it only
|
||||
# fast-forwards existing submodule/<name> branches to commits that
|
||||
# have already been made inside the local forks.
|
||||
#
|
||||
# Strategy: for each fork with `N ahead`, use `git push --force-with-lease`
|
||||
# against the tracked upstream ref (submodule/<name>). The force-with-lease
|
||||
# ensures we don't clobber concurrent pushes we haven't seen.
|
||||
|
||||
# Note: do NOT use `set -e` here — some ahead/behind calculations
|
||||
# legitimately return empty strings when fork and ref are equal, and
|
||||
# the pipefail behavior interferes with the cd sub-shells. Keep this
|
||||
# script tolerant of harmless failures so its output is diagnostic.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
FORKS=(base bootloader installer kernel libredox relibc syscall userutils)
|
||||
|
||||
echo "=== Red Bear fork-branch status vs origin (2026-07-12) ==="
|
||||
echo ""
|
||||
printf "%-12s %-15s %-10s %-10s\n" "fork" "local" "ahead" "behind"
|
||||
echo "---------- --------------- ---------- ----------"
|
||||
|
||||
REVIEW_NEEDED=()
|
||||
for fork in "${FORKS[@]}"; do
|
||||
cd "local/sources/$fork" || continue
|
||||
remote_sha=$(git ls-remote origin "refs/heads/submodule/$fork" 2>&1 | awk '{print $1}')
|
||||
local_sha=$(git rev-parse HEAD 2>&1)
|
||||
# Avoid the ambiguous `HEAD` syntax by using the commit SHA directly.
|
||||
# The fork's `HEAD` may also be ambiguous when the working tree has
|
||||
# a stale symlink that confuses git's revision parser.
|
||||
if [ -n "$local_sha" ] && [ -n "$remote_sha" ]; then
|
||||
ahead=$(git rev-list --count "$remote_sha..$local_sha" 2>/dev/null | head -1 || echo "?")
|
||||
# Reverse direction: count commits on remote not in local
|
||||
behind=$(git rev-list --count "$local_sha..$remote_sha" 2>/dev/null | head -1 || echo "?")
|
||||
else
|
||||
ahead="?"
|
||||
behind="?"
|
||||
fi
|
||||
printf "%-12s %-15s %-10s %-10s\n" "$fork" "${local_sha:0:12}" "${ahead:-?}" "${behind:-?}"
|
||||
|
||||
if [ "${behind:-0}" -eq 0 ] 2>/dev/null && [ "${ahead:-0}" -gt 0 ] 2>/dev/null; then
|
||||
REVIEW_NEEDED+=("$fork:$ahead")
|
||||
fi
|
||||
cd "$ROOT"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Push commands (review BEFORE running) ==="
|
||||
echo ""
|
||||
echo "# Each line: cd into the fork, push to origin's submodule/<fork> ref."
|
||||
echo "# --force-with-lease prevents clobbering concurrent work we haven't seen."
|
||||
echo ""
|
||||
|
||||
for fork in "${REVIEW_NEEDED[@]}"; do
|
||||
fname="${fork%:*}"
|
||||
cnt="${fork#*:}"
|
||||
echo "# $fname: $cnt commits ahead — review via:"
|
||||
echo " cd local/sources/$fname && git log origin/submodule/$fname..HEAD --oneline"
|
||||
echo " # Then push (REVIEW the diff first):"
|
||||
echo " cd $ROOT/local/sources/$fname && git push --force-with-lease=refs/heads/submodule/$fname:\$(git ls-remote origin refs/heads/submodule/$fname | awk '{print \$1}') origin HEAD:refs/heads/submodule/$fname"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Other forks ==="
|
||||
echo ""
|
||||
echo "libredox: dropped .cargo-ok + .cargo_vcs_info.json, tightened .gitignore."
|
||||
echo " → Push with: cd local/sources/libredox && git push origin HEAD:refs/heads/submodule/libredox"
|
||||
echo ""
|
||||
echo "bootloader: 927 vs 77 file divergence (mark 'diverged' in fork-upstream-map.toml)."
|
||||
echo " → DO NOT auto-push. Phase 2.4+ work: upgrade-forks.sh bootloader manual run."
|
||||
echo ""
|
||||
echo "installer: 30 vs 22 file divergence (mark 'diverged')."
|
||||
echo " → DO NOT auto-push. Phase 2.4+ work."
|
||||
echo ""
|
||||
echo "kernel: branch 0.3.1 (local release branch, not submodule/kernel)."
|
||||
echo " → Kernel's branch naming is intentional. DO NOT push to submodule/kernel."
|
||||
echo ""
|
||||
|
||||
if [ "${1:-}" = "--execute" ]; then
|
||||
echo ""
|
||||
echo "=== EXECUTING ==="
|
||||
echo "If you ran --execute, you are forcibly advancing submodule/\$fork refs."
|
||||
echo "Press Ctrl-C within 5 seconds to abort."
|
||||
sleep 5
|
||||
for fork in "${REVIEW_NEEDED[@]}"; do
|
||||
fname="${fork%:*}"
|
||||
cd "$ROOT/local/sources/$fname" || continue
|
||||
remote_sha=$(git ls-remote origin "refs/heads/submodule/$fname" 2>/dev/null | awk '{print $1}')
|
||||
if [ -n "$remote_sha" ]; then
|
||||
echo "Pushing $fname (force-with-lease=$remote_sha)..."
|
||||
if git push --force-with-lease="refs/heads/submodule/$fname:$remote_sha" \
|
||||
origin "HEAD:refs/heads/submodule/$fname" 2>&1 | tail -3; then
|
||||
echo " $fname: pushed OK"
|
||||
else
|
||||
echo " $fname: PUSH FAILED — operator review required"
|
||||
fi
|
||||
fi
|
||||
cd "$ROOT"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== To push libredox cleanup (no force-with-lease needed, fast-forward): ==="
|
||||
echo " cd local/sources/libredox && git push origin HEAD:refs/heads/submodule/libredox"
|
||||
+1
-1
Submodule local/sources/base updated: be2d7503c9...bf9989b7c0
Reference in New Issue
Block a user