1+ #! /usr/bin/env bash
2+ set -euo pipefail
3+
4+ echo " Starting submodule sweep: fetch, align to master/main, and push."
5+
6+ # Ensure submodules are initialized and synced
7+ GIT_EDITOR=true git submodule sync --recursive || true
8+ GIT_EDITOR=true git submodule update --init --recursive || true
9+
10+ # Collect submodule paths from .gitmodules
11+ SUBMODULES=$( git config -f .gitmodules --get-regexp ' ^submodule\..*\.path' | awk ' {print $2}' )
12+
13+ resolve_default_branch () {
14+ local remote=" $1 "
15+ local head
16+ if git ls-remote --heads " $remote " master | grep -q ' refs/heads/master' ; then
17+ echo master; return
18+ fi
19+ if git ls-remote --heads " $remote " main | grep -q ' refs/heads/main' ; then
20+ echo main; return
21+ fi
22+ head=$( git remote show " $remote " | sed -n ' /HEAD branch/s/.*: //p' | head -n1 || true)
23+ if [ -n " $head " ]; then
24+ echo " $head " ; return
25+ fi
26+ echo master
27+ }
28+
29+ if [ -z " ${SUBMODULES:- } " ]; then
30+ echo " No submodules found in .gitmodules; nothing to sweep."
31+ else
32+ for sm in $SUBMODULES ; do
33+ echo " \n—— Processing submodule: $sm ——"
34+ if [ ! -d " $sm /.git" ]; then
35+ echo " Initializing submodule $sm "
36+ GIT_EDITOR=true git submodule update --init " $sm "
37+ fi
38+ (
39+ cd " $sm "
40+ git fetch --all --prune
41+ # Prefer 'upstream' if present, otherwise 'origin'
42+ if git remote | grep -q ' ^upstream$' ; then REMOTE=upstream; else REMOTE=origin; fi
43+ TARGET_BRANCH=$( resolve_default_branch " $REMOTE " )
44+ echo " Remote: $REMOTE | Target branch: $TARGET_BRANCH "
45+ # Checkout local tracking branch at remote
46+ if git show-ref --verify --quiet refs/heads/" $TARGET_BRANCH " ; then
47+ GIT_EDITOR=true git checkout " $TARGET_BRANCH "
48+ else
49+ GIT_EDITOR=true git checkout -B " $TARGET_BRANCH " " $REMOTE /$TARGET_BRANCH "
50+ fi
51+ # Fast-forward or hard reset to remote if divergent
52+ if GIT_EDITOR=true git merge --ff-only " $REMOTE /$TARGET_BRANCH " ; then
53+ echo " Fast-forwarded $sm to $REMOTE /$TARGET_BRANCH "
54+ else
55+ echo " Non-FF merge; resetting $sm to $REMOTE /$TARGET_BRANCH "
56+ GIT_EDITOR=true git reset --hard " $REMOTE /$TARGET_BRANCH "
57+ fi
58+ # Ensure branch tracks remote
59+ GIT_EDITOR=true git branch --set-upstream-to=" $REMOTE /$TARGET_BRANCH " " $TARGET_BRANCH " || true
60+ # Push if local ahead of remote
61+ AHEAD=$( git rev-list --left-right --count " $TARGET_BRANCH ...$REMOTE /$TARGET_BRANCH " 2> /dev/null | awk ' {print $1}' )
62+ if [ " ${AHEAD:- 0} " -gt 0 ]; then
63+ echo " Local ahead by $AHEAD ; pushing $sm to $REMOTE /$TARGET_BRANCH "
64+ GIT_EDITOR=true git push " $REMOTE " " $TARGET_BRANCH "
65+ else
66+ echo " No local commits to push for $sm "
67+ fi
68+ )
69+ # Record updated pointer in superproject
70+ GIT_EDITOR=true git add " $sm "
71+ done
72+ fi
73+
74+ # Commit pointer updates in superproject if any
75+ if ! git diff --cached --quiet; then
76+ GIT_EDITOR=true git commit -m " chore(submodules): sweep to latest master/main across submodules"
77+ else
78+ echo " No pointer changes in superproject; nothing to commit."
79+ fi
80+
81+ # Push superproject to origin
82+ CURRENT_BRANCH=$( git symbolic-ref --short HEAD 2> /dev/null || echo " " )
83+ if [ -n " $CURRENT_BRANCH " ]; then
84+ echo " Pushing superproject branch $CURRENT_BRANCH to origin"
85+ GIT_EDITOR=true git push --set-upstream origin " $CURRENT_BRANCH "
86+ else
87+ echo " Detached HEAD; skipping superproject push."
88+ fi
89+
90+ echo " Sweep complete."
0 commit comments