feat(git): PR 目標分支改走階梯推導,既有 PR 改為校準不重開 #12
+126
-16
@@ -1,27 +1,51 @@
|
||||
#!/usr/bin/env sh
|
||||
# base-branch.sh — 決定 PR 的基底分支。
|
||||
# 用法: base-branch.sh [caller-base]
|
||||
# 規則: 呼叫方傳入的分支最優先;呼叫方沒傳,才依序試 develop、main、master。
|
||||
# 先 git fetch --prune origin,再確認選中的分支存在於遠端。
|
||||
# base-branch.sh --derive [branch]
|
||||
#
|
||||
# 模式一(既有): 呼叫方傳入的分支最優先;呼叫方沒傳,才依序試 develop、main、master。
|
||||
# 「有沒有傳」看參數個數,不看參數內容:呼叫方寫 base-branch.sh "$SOURCE_BRANCH"
|
||||
# 而變數沒設定時,那是空字串,不是沒傳。若當成沒傳就會悄悄退回 develop,
|
||||
# 把單一工作包直接合進 develop——這支腳本存在的目的就是擋這件事。
|
||||
#
|
||||
# 模式二(--derive): 從分支名推出唯一合法的上一階基底,禁止越級。
|
||||
# 沒傳 branch 就取目前分支。階梯如下:
|
||||
# feat、docs、style、refactor、perf、test、chore、revert:
|
||||
# {類型}/{功能}/{子功能} → {類型}/{功能}/main → develop → master
|
||||
# {子功能} 可以多層(例:feat/a/b/c),推導一律「去掉最後一段後接 /main」。
|
||||
# fix:
|
||||
# fix/{修改} → develop → master
|
||||
# 推不出唯一合法基底就中止(退出碼 7),由呼叫端問使用者,不猜、也不退回 develop。
|
||||
# 功能主幹 {類型}/{功能}/main 不在 origin 時,自動以 origin/develop 為起點建立並推上去,
|
||||
# 再把建立了哪一條分支印到 stderr。
|
||||
#
|
||||
# 分支名只允許 ASCII(a-z0-9 與 /、-)。中文簡述先交給同目錄的 slugify.sh 轉成 ASCII slug,
|
||||
# 再組成分支名,這支腳本不接受非 ASCII 分支名。
|
||||
#
|
||||
# 輸出: 選中的分支名(一行)。
|
||||
# 護欄: 參數過多回傳 2;抓不到遠端回傳 3;呼叫方指定的分支不在遠端回傳 4;
|
||||
# develop、main、master 都不在遠端回傳 5;呼叫方傳入空字串回傳 6。
|
||||
# develop、main、master 都不在遠端回傳 5;傳入空字串回傳 6;
|
||||
# 推不出唯一合法基底回傳 7;推導出的基底不在遠端又不能自動建立回傳 8;
|
||||
# 自動建立功能主幹失敗回傳 9。
|
||||
# 錯誤訊息一律印繁中到 stderr。
|
||||
set -u
|
||||
|
||||
MODE=caller
|
||||
if [ "$#" -ge 1 ] && [ "$1" = "--derive" ]; then
|
||||
MODE=derive
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ "$#" -gt 1 ]; then
|
||||
echo "用法: base-branch.sh [caller-base]" >&2
|
||||
echo "用法: base-branch.sh [caller-base] 或 base-branch.sh --derive [branch]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ARGC=$#
|
||||
CALLER=${1:-}
|
||||
ARG=${1:-}
|
||||
|
||||
if [ "$ARGC" -eq 1 ] && [ -z "$CALLER" ]; then
|
||||
echo "錯誤: 呼叫方傳入空字串當基底分支。請確認來源分支變數有值,或整個參數不要傳。" >&2
|
||||
if [ "$ARGC" -eq 1 ] && [ -z "$ARG" ]; then
|
||||
echo "錯誤: 傳入空字串當分支名。請確認分支變數有值,或整個參數不要傳。" >&2
|
||||
exit 6
|
||||
fi
|
||||
|
||||
@@ -34,21 +58,107 @@ remote_has() {
|
||||
[ -n "$(git ls-remote --heads origin "refs/heads/$1" 2>/dev/null)" ]
|
||||
}
|
||||
|
||||
if [ "$ARGC" -eq 1 ]; then
|
||||
if remote_has "$CALLER"; then
|
||||
printf '%s\n' "$CALLER"
|
||||
if [ "$MODE" = "caller" ]; then
|
||||
if [ "$ARGC" -eq 1 ]; then
|
||||
if remote_has "$ARG"; then
|
||||
printf '%s\n' "$ARG"
|
||||
exit 0
|
||||
fi
|
||||
echo "錯誤: 呼叫方指定的基底分支 $CALLER 不在 origin 上。請確認分支名,不要自行改用其他分支。" >&2
|
||||
echo "錯誤: 呼叫方指定的基底分支 $ARG 不在 origin 上。請確認分支名,不要自行改用其他分支。" >&2
|
||||
exit 4
|
||||
fi
|
||||
fi
|
||||
|
||||
for candidate in develop main master; do
|
||||
for candidate in develop main master; do
|
||||
if remote_has "$candidate"; then
|
||||
printf '%s\n' "$candidate"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo "錯誤: origin 上找不到 develop、main、master。請由呼叫方指定基底分支。" >&2
|
||||
exit 5
|
||||
echo "錯誤: origin 上找不到 develop、main、master。請由呼叫方指定基底分支。" >&2
|
||||
exit 5
|
||||
fi
|
||||
|
||||
# 以下是 --derive 模式。
|
||||
BRANCH=$ARG
|
||||
if [ -z "$BRANCH" ]; then
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ -z "$BRANCH" ] || [ "$BRANCH" = "HEAD" ]; then
|
||||
echo "錯誤: 取不到目前分支名(可能在斷頭狀態)。請先切到分支,或直接把分支名當參數傳進來。" >&2
|
||||
exit 7
|
||||
fi
|
||||
|
||||
if printf '%s' "$BRANCH" | LC_ALL=C grep -q '[^a-z0-9/-]'; then
|
||||
echo "錯誤: 分支名 $BRANCH 含分支名不允許的字元。分支名只允許 ASCII 小寫、數字、連字號與斜線;中文簡述請先交給 slugify.sh。" >&2
|
||||
exit 7
|
||||
fi
|
||||
|
||||
TYPE=${BRANCH%%/*}
|
||||
SEGMENTS=$(printf '%s' "$BRANCH" | tr '/' '\n' | grep -c '')
|
||||
LAST=${BRANCH##*/}
|
||||
HEAD_PART=${BRANCH%/*}
|
||||
|
||||
ladder_type() {
|
||||
case "$1" in
|
||||
feat|docs|style|refactor|perf|test|chore|revert) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
BASE=""
|
||||
TRUNK_AUTOCREATE=no
|
||||
|
||||
if [ "$BRANCH" = "develop" ]; then
|
||||
BASE=master
|
||||
elif [ "$BRANCH" = "master" ]; then
|
||||
echo "錯誤: master 已經是階梯頂端,推不出上一階基底分支。請確認是不是站錯分支。" >&2
|
||||
exit 7
|
||||
elif [ "$TYPE" = "fix" ]; then
|
||||
if [ "$SEGMENTS" -eq 2 ]; then
|
||||
BASE=develop
|
||||
else
|
||||
echo "錯誤: 分支 $BRANCH 不符合 fix 階梯。fix 只有 fix/{修改} → develop → master 一條路,不接受多層分支名。" >&2
|
||||
exit 7
|
||||
fi
|
||||
elif ladder_type "$TYPE"; then
|
||||
if [ "$SEGMENTS" -lt 3 ]; then
|
||||
echo "錯誤: 分支 $BRANCH 少了功能層,推不出唯一合法基底。$TYPE 階梯為 $TYPE/{功能}/{子功能} → $TYPE/{功能}/main → develop → master,請補上功能層再重試。" >&2
|
||||
exit 7
|
||||
elif [ "$LAST" = "main" ]; then
|
||||
BASE=develop
|
||||
else
|
||||
BASE="$HEAD_PART/main"
|
||||
TRUNK_AUTOCREATE=yes
|
||||
fi
|
||||
else
|
||||
echo "錯誤: 分支 $BRANCH 的類型 $TYPE 不在階梯表內,推不出唯一合法基底。請改用 feat、fix、docs、style、refactor、perf、test、chore、revert 其中一種類型。" >&2
|
||||
exit 7
|
||||
fi
|
||||
|
||||
if remote_has "$BASE"; then
|
||||
printf '%s\n' "$BASE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$TRUNK_AUTOCREATE" = "no" ]; then
|
||||
echo "錯誤: 推導出的基底分支 $BASE 不在 origin 上,而且它不是可以自動建立的功能主幹。請先建立 $BASE,再重試。" >&2
|
||||
exit 8
|
||||
fi
|
||||
|
||||
# 功能主幹不存在就自動補一條:直接把 origin/develop 推成新分支,
|
||||
# 不動本機工作區,省下切分支與切回來的來回。
|
||||
if ! remote_has develop; then
|
||||
echo "錯誤: origin 上沒有 develop,無法自動建立功能主幹 $BASE。請先建立 develop,再重試。" >&2
|
||||
exit 9
|
||||
fi
|
||||
|
||||
if ! git push origin "refs/remotes/origin/develop:refs/heads/$BASE" >/dev/null 2>&1; then
|
||||
echo "錯誤: 自動建立功能主幹 $BASE 失敗。請確認遠端推送權限,或手動從 develop 建立 $BASE。" >&2
|
||||
exit 9
|
||||
fi
|
||||
|
||||
echo "已自動從 develop 建立功能主幹 $BASE,並推上 origin。" >&2
|
||||
printf '%s\n' "$BASE"
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user