chore(git-workflow): add 3-repo feature branch skills + helper scripts
This commit is contained in:
parent
730147d690
commit
645f89b816
10 changed files with 500 additions and 0 deletions
33
scripts/git-workflow/assert-clean.sh
Executable file
33
scripts/git-workflow/assert-clean.sh
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
# Exit 0 if all 3 repos are clean (no staged, no unstaged, no untracked files).
|
||||
# Exit 1 with a readable message listing the dirty repos otherwise.
|
||||
# Untracked files count as dirty - this is intentional: starting a new feature
|
||||
# while you have uncommitted new files is almost always a mistake.
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=./repos.sh
|
||||
source "$SCRIPT_DIR/repos.sh"
|
||||
|
||||
dirty=()
|
||||
for repo in "${REPOS[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
# `git status --porcelain` is empty iff worktree clean (covers modified, staged, and untracked).
|
||||
if [ -n "$(git -C "$p" status --porcelain)" ]; then
|
||||
dirty+=("$repo")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#dirty[@]} -eq 0 ]; then
|
||||
echo "All 3 repos clean."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Dirty repos: ${dirty[*]}" >&2
|
||||
for repo in "${dirty[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
echo "--- $repo ---" >&2
|
||||
git -C "$p" status --short >&2
|
||||
done
|
||||
exit 1
|
||||
36
scripts/git-workflow/assert-on-branch.sh
Executable file
36
scripts/git-workflow/assert-on-branch.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
# Assert all 3 repos are on the given branch.
|
||||
# Exit 0 if all match. Exit 1 with details if any mismatch (including detached HEAD).
|
||||
# Usage: ./assert-on-branch.sh <branch-name>
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=./repos.sh
|
||||
source "$SCRIPT_DIR/repos.sh"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <branch-name>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
WANT="$1"
|
||||
mismatched=()
|
||||
for repo in "${REPOS[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
cur=$(git -C "$p" branch --show-current)
|
||||
if [ "$cur" != "$WANT" ]; then
|
||||
mismatched+=("$repo: on '${cur:-DETACHED-HEAD}' (want '$WANT')")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#mismatched[@]} -eq 0 ]; then
|
||||
echo "All 3 repos on branch: $WANT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Branch mismatch:" >&2
|
||||
for line in "${mismatched[@]}"; do
|
||||
echo " $line" >&2
|
||||
done
|
||||
exit 1
|
||||
26
scripts/git-workflow/for-each-repo.sh
Executable file
26
scripts/git-workflow/for-each-repo.sh
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
# Run a git command in each of the 3 repos. Echoes before each run.
|
||||
# Usage: ./for-each-repo.sh <git-subcommand-and-args>
|
||||
# e.g. ./for-each-repo.sh status --short
|
||||
# ./for-each-repo.sh fetch origin
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=./repos.sh
|
||||
source "$SCRIPT_DIR/repos.sh"
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <git-subcommand> [args...]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for repo in "${REPOS[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
echo "=== $repo ($p) ==="
|
||||
echo "+ git -C $p $*"
|
||||
if ! git -C "$p" "$@"; then
|
||||
echo "FAILED in repo: $repo" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
63
scripts/git-workflow/repo-status.sh
Executable file
63
scripts/git-workflow/repo-status.sh
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/bin/bash
|
||||
# Emit one JSON line per repo describing its state.
|
||||
# Fields: repo, path, branch, dirty, ahead, behind, head_sha, main, rebase_in_progress, up_to_date_with_main
|
||||
# "up_to_date_with_main" means origin/<main> is an ancestor of HEAD (i.e. rebase not needed).
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=./repos.sh
|
||||
source "$SCRIPT_DIR/repos.sh"
|
||||
|
||||
json_escape() {
|
||||
# Minimal JSON string escape: backslash and double-quote only. Branch/sha never contain control chars in practice.
|
||||
local s="$1"
|
||||
s="${s//\\/\\\\}"
|
||||
s="${s//\"/\\\"}"
|
||||
printf '%s' "$s"
|
||||
}
|
||||
|
||||
for repo in "${REPOS[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
main=$(repo_main "$repo")
|
||||
|
||||
branch=$(git -C "$p" branch --show-current || echo "")
|
||||
head_sha=$(git -C "$p" rev-parse --short HEAD)
|
||||
|
||||
if [ -z "$(git -C "$p" status --porcelain)" ]; then
|
||||
dirty="false"
|
||||
else
|
||||
dirty="true"
|
||||
fi
|
||||
|
||||
ahead=0
|
||||
behind=0
|
||||
if [ -n "$branch" ] && git -C "$p" rev-parse --verify --quiet "origin/$main" >/dev/null; then
|
||||
read -r behind ahead < <(git -C "$p" rev-list --left-right --count "origin/$main...HEAD" 2>/dev/null || echo "0 0")
|
||||
fi
|
||||
|
||||
if [ -d "$p/.git/rebase-merge" ] || [ -d "$p/.git/rebase-apply" ]; then
|
||||
rebase="true"
|
||||
else
|
||||
rebase="false"
|
||||
fi
|
||||
|
||||
up_to_date="false"
|
||||
if git -C "$p" rev-parse --verify --quiet "origin/$main" >/dev/null; then
|
||||
if git -C "$p" merge-base --is-ancestor "origin/$main" HEAD 2>/dev/null; then
|
||||
up_to_date="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
printf '{"repo":"%s","path":"%s","branch":"%s","dirty":%s,"ahead":%s,"behind":%s,"head_sha":"%s","main":"%s","rebase_in_progress":%s,"up_to_date_with_main":%s}\n' \
|
||||
"$(json_escape "$repo")" \
|
||||
"$(json_escape "$p")" \
|
||||
"$(json_escape "$branch")" \
|
||||
"$dirty" \
|
||||
"$ahead" \
|
||||
"$behind" \
|
||||
"$(json_escape "$head_sha")" \
|
||||
"$(json_escape "$main")" \
|
||||
"$rebase" \
|
||||
"$up_to_date"
|
||||
done
|
||||
34
scripts/git-workflow/repos.sh
Executable file
34
scripts/git-workflow/repos.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
# Sourced library. Single source of truth for the 3-repo layout.
|
||||
# Usage: source "$(dirname "$0")/repos.sh"
|
||||
|
||||
ROOT_DIR="/Users/torcsi/dev/kicad-wasm"
|
||||
|
||||
REPOS=(root kicad wxwidgets)
|
||||
|
||||
PATH_root="$ROOT_DIR"
|
||||
PATH_kicad="$ROOT_DIR/kicad"
|
||||
PATH_wxwidgets="$ROOT_DIR/wxwidgets"
|
||||
|
||||
MAIN_root="main"
|
||||
MAIN_kicad="wasm-port"
|
||||
MAIN_wxwidgets="wasm-port"
|
||||
|
||||
repo_path() {
|
||||
local var="PATH_$1"
|
||||
echo "${!var}"
|
||||
}
|
||||
|
||||
repo_main() {
|
||||
local var="MAIN_$1"
|
||||
echo "${!var}"
|
||||
}
|
||||
|
||||
run_git() {
|
||||
# Echo before run so the user always sees what we're doing.
|
||||
local repo="$1"; shift
|
||||
local p
|
||||
p=$(repo_path "$repo")
|
||||
echo "+ git -C $p $*" >&2
|
||||
git -C "$p" "$@"
|
||||
}
|
||||
Loading…
Reference in a new issue