1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
#!/usr/bin/env bash
set -euo pipefail
# Unset git env vars that leak from parent hook context into submodule commands
unset GIT_INDEX_FILE GIT_DIR
SUBMODULE_DIR="sqlglot-integration-tests"
# Graceful no-op when the submodule is absent (public contributors)
[ -e "$SUBMODULE_DIR/.git" ] || exit 0
# Ensure the submodule is on a branch matching the parent's branch name.
# Creates the branch if it doesn't exist.
ensure_branch() {
local branch
branch=$(git rev-parse --abbrev-ref HEAD)
[ "$branch" = "HEAD" ] && return 0
local current
current=$(git -C "$SUBMODULE_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [ "$current" != "$branch" ]; then
# Stash any uncommitted/untracked changes so branch switch doesn't fail
local stashed=0
if [ -n "$(git -C "$SUBMODULE_DIR" status --porcelain)" ]; then
git -C "$SUBMODULE_DIR" stash push --include-untracked --quiet 2>/dev/null && stashed=1
fi
if git -C "$SUBMODULE_DIR" show-ref --verify --quiet "refs/heads/$branch"; then
git -C "$SUBMODULE_DIR" checkout "$branch" --quiet
else
git -C "$SUBMODULE_DIR" checkout -b "$branch" --quiet
fi
# Restore stashed changes on the new branch
if [ "$stashed" = "1" ]; then
git -C "$SUBMODULE_DIR" stash pop --quiet 2>/dev/null || true
fi
fi
}
case "${1:-}" in
checkout)
# No-op: branch creation is deferred to commit/post-commit to avoid
# creating empty branches when switching parent branches.
;;
commit)
# Skip if submodule has no changes
[ -n "$(git -C "$SUBMODULE_DIR" status --porcelain)" ] || exit 0
# Only auto-commit submodule changes when the submodule pointer is already
# staged in the parent (i.e. the user explicitly `git add`-ed it). This
# prevents surprise commits of unrelated WIP in the submodule working tree.
git diff --cached --quiet -- "$SUBMODULE_DIR" && exit 0
ensure_branch
if [ -n "$(git -C "$SUBMODULE_DIR" status --porcelain)" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git -C "$SUBMODULE_DIR" add -A
git -C "$SUBMODULE_DIR" commit -m "Sync: $BRANCH"
fi
# Stage the updated submodule pointer in the parent
if ! git diff --quiet -- "$SUBMODULE_DIR"; then
git add "$SUBMODULE_DIR"
fi
;;
post-commit)
# The pre-commit framework's stashing undoes submodule changes made during
# pre-commit. Re-commit the submodule and amend the parent to include it.
# Only act if the most recent parent commit references the submodule. This
# prevents the post-commit hook from auto-committing unrelated submodule WIP
# (e.g. when switching branches or pulling on main).
if ! git diff-tree --no-commit-id --name-only -r HEAD | grep -q "^${SUBMODULE_DIR}$"; then
exit 0
fi
# Skip if submodule has no changes and pointer is up to date
if [ -z "$(git -C "$SUBMODULE_DIR" status --porcelain)" ] && git diff --quiet -- "$SUBMODULE_DIR"; then
exit 0
fi
ensure_branch
if [ -n "$(git -C "$SUBMODULE_DIR" status --porcelain)" ]; then
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git -C "$SUBMODULE_DIR" add -A
git -C "$SUBMODULE_DIR" commit -m "Sync: $BRANCH"
fi
if ! git diff --quiet -- "$SUBMODULE_DIR"; then
git add "$SUBMODULE_DIR"
git commit --amend --no-edit --no-verify
fi
;;
push)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
case "$BRANCH" in main|master) exit 0 ;; esac
ensure_branch
cd "$SUBMODULE_DIR"
# Check if there are local commits to push
if git rev-parse --verify "@{upstream}" >/dev/null 2>&1; then
UNPUSHED=$(git rev-list --count "@{upstream}..HEAD")
else
UNPUSHED=$(git rev-list --count "origin/main..HEAD" 2>/dev/null || echo "0")
fi
[ "$UNPUSHED" = "0" ] && exit 0
# Push the branch, rebasing first if needed
git push -u origin "$BRANCH" 2>/dev/null || {
git pull --rebase --autostash || {
git rebase --abort 2>/dev/null || true
echo "ERROR: Conflicts pushing integration tests. Resolve with:" >&2
echo " cd $SUBMODULE_DIR && git pull --rebase" >&2
exit 1
}
git push -u origin "$BRANCH"
}
# Create a PR if one doesn't exist (requires gh CLI)
if command -v gh >/dev/null 2>&1; then
EXISTING=$(gh pr list --head "$BRANCH" --json number --jq 'length' 2>/dev/null || echo "0")
if [ "$EXISTING" = "0" ]; then
PARENT_URL=$(git -C .. remote get-url origin 2>/dev/null | sed 's/\.git$//' | sed 's|git@github.com:|https://github.com/|')
BODY="Parent branch: ${PARENT_URL}/tree/${BRANCH}"
gh pr create \
--title "$BRANCH" \
--body "$BODY" \
--head "$BRANCH" 2>/dev/null || true
fi
fi
;;
merge)
# Sync submodule to the parent's pointer
git submodule update --init 2>/dev/null || true
ensure_branch
cd "$SUBMODULE_DIR"
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
[ "$BRANCH" = "HEAD" ] && exit 0
git fetch origin --quiet 2>/dev/null || exit 0
BEHIND=$(git rev-list --count "HEAD..origin/$BRANCH" 2>/dev/null || echo "0")
[ "$BEHIND" = "0" ] && exit 0
if ! git pull --rebase --autostash --quiet 2>/dev/null; then
git rebase --abort 2>/dev/null || true
echo "WARNING: Rebase conflicts in $SUBMODULE_DIR." >&2
echo "Resolve with: cd $SUBMODULE_DIR && git pull --rebase" >&2
fi
;;
*)
echo "Usage: $0 {checkout|commit|post-commit|push|merge}" >&2
exit 1
;;
esac
|