-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgit-sync.sh
More file actions
executable file
·107 lines (88 loc) · 3.49 KB
/
Copy pathgit-sync.sh
File metadata and controls
executable file
·107 lines (88 loc) · 3.49 KB
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
#!/bin/bash
# scripts/git-sync.sh
#
# One-command sync: bring local `main` back in line with `origin/main`,
# then delete any local feature branches whose PRs have already been
# merged on GitHub. Run this anytime — it's idempotent.
#
# Why this exists: the repo uses squash-merge PRs, which give merged
# commits a brand-new SHA on `main`. After a merge, local `main` and
# the local feature branch are both "diverged" from origin even though
# the work has shipped. This script cleans that up in one step.
#
# Usage:
# ./scripts/git-sync.sh
#
# Requires: gh CLI authenticated against the same GitHub account that
# owns the repo (gh auth status to verify).
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
# --- preflight ------------------------------------------------------------
if ! command -v gh >/dev/null 2>&1; then
echo "❌ gh CLI not found. Install with: brew install gh"
exit 1
fi
if ! gh auth status >/dev/null 2>&1; then
echo "❌ gh CLI not authenticated. Run: gh auth login"
exit 1
fi
# Stash uncommitted changes so the checkout/reset can't lose work.
# We pop the stash at the end if it was created.
STASH_CREATED=0
if [ -n "$(git status --porcelain)" ]; then
echo "→ Stashing uncommitted changes (will pop at the end)"
git stash push -u -m "git-sync.sh auto-stash $(date -u +%FT%TZ)" >/dev/null
STASH_CREATED=1
fi
CURRENT_BRANCH="$(git branch --show-current)"
# --- sync main ------------------------------------------------------------
echo "→ Fetching origin + pruning stale remote refs"
git fetch origin --prune --quiet
echo "→ Resetting local main to origin/main"
git checkout main --quiet 2>/dev/null || git checkout -B main origin/main --quiet
git reset --hard origin/main --quiet
MAIN_SHA="$(git rev-parse --short HEAD)"
echo " main is at $MAIN_SHA"
# --- prune merged feature branches ---------------------------------------
echo "→ Scanning local branches for ones whose PR has merged"
# Get every local branch except main into a tempfile, then iterate.
# Why a tempfile: bash 3.2 (still the macOS default) has no `mapfile`,
# and piping into `while read` puts the loop body in a subshell so
# array assignments to DELETED/KEPT wouldn't persist.
BRANCHES_TMP="$(mktemp)"
trap 'rm -f "$BRANCHES_TMP"' EXIT
git for-each-ref --format='%(refname:short)' refs/heads | grep -v '^main$' > "$BRANCHES_TMP" || true
DELETED=()
KEPT=()
while IFS= read -r br; do
[ -z "$br" ] && continue
# Squash-merged PRs don't show as "merged" in git's view (different SHAs),
# so ask GitHub directly: is there a closed/merged PR whose head is this branch?
STATE="$(gh pr list --state all --head "$br" --json state --jq '.[0].state' 2>/dev/null || true)"
if [ "$STATE" = "MERGED" ]; then
git branch -D "$br" --quiet
DELETED+=("$br")
else
KEPT+=("$br${STATE:+ (PR: $STATE)}")
fi
done < "$BRANCHES_TMP"
if [ ${#DELETED[@]} -gt 0 ]; then
echo " Deleted (PRs merged):"
printf ' - %s\n' "${DELETED[@]}"
fi
if [ ${#KEPT[@]} -gt 0 ]; then
echo " Kept:"
printf ' - %s\n' "${KEPT[@]}"
fi
# --- restore prior context ------------------------------------------------
# If the user was on a branch that's still around, switch back to it.
if [ "$CURRENT_BRANCH" != "main" ] && git show-ref --verify --quiet "refs/heads/$CURRENT_BRANCH"; then
git checkout "$CURRENT_BRANCH" --quiet
echo "→ Returned to branch: $CURRENT_BRANCH"
fi
if [ "$STASH_CREATED" -eq 1 ]; then
echo "→ Popping stashed changes"
git stash pop --quiet
fi
echo "✓ Sync complete."