#!/usr/bin/env sh # repo-sync.sh — 把單一存取庫同步到本機。 # 用法: repo-sync.sh / [target-dir] # target-dir 預設為 # 目錄不存在 → git clone(clone URL 取自 gitea.sh clone-url) # 目錄已存在 → git fetch、切到基準分支、git pull --ff-only # 有未提交變更 → 只回報 dirty 與基準分支,不動分支也不 pull # 基準分支的唯一優先序(呼叫端不必再判斷,也不要自己再推導一次): # 1. gitea.sh default-branch /(該分支要在遠端存在) # 2. 查不到時依序取遠端的 develop、main、master # 3. 都沒有 → failed no default branch # 輸出: 恰好一行,cloned、updated、dirty {分支} 或 failed {原因}。 # 前三種 exit 0;failed exit 1。 # dirty 會把解析好的基準分支一起帶出來,呼叫端直接拿去當 PR 的 base。 set -u script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) gitea="$script_dir/gitea.sh" fail() { # 原因壓成一行,避免呼叫端解析多行輸出 printf 'failed %s\n' "$(printf '%s' "$1" | tr '\n\r\t' ' ')" exit 1 } full="${1:-}" [ -n "$full" ] || fail "usage: repo-sync.sh / [target-dir]" case "$full" in */*/*|/*|*/) fail "not owner/repo: $full" ;; */*) ;; *) fail "not owner/repo: $full" ;; esac repo="${full#*/}" target="${2:-$repo}" remote_has() { # 遠端是否有這個分支 git -C "$target" rev-parse --verify --quiet "refs/remotes/origin/$1" >/dev/null 2>&1 } if [ ! -e "$target" ]; then # clone URL 只收 stdout:把 stderr 併進來會讓任何雜訊變成網址的一部分 err=$(mktemp) url=$("$gitea" clone-url "$full" 2>"$err") || url='' if [ -z "$url" ]; then reason=$(cat "$err"); rm -f "$err" fail "clone-url failed for $full: ${reason:-no clone URL}" fi rm -f "$err" out=$(git clone "$url" "$target" 2>&1) || fail "clone: $out" printf 'cloned\n' exit 0 fi git -C "$target" rev-parse --git-dir >/dev/null 2>&1 || fail "$target is not a git repo" dirty=0 [ -z "$(git -C "$target" status --porcelain 2>/dev/null)" ] || dirty=1 # dirty 的存取庫也要解析基準分支:呼叫端拿它當 PR 的 base,自己再推導一次就會 # 出現第二套優先序。fetch 只更新 refs,不動工作目錄,dirty 時照樣安全;但 dirty # 時網路失敗不算致命,還是要把 dirty 回報出去,改用本機既有的 remote refs 判斷。 if ! out=$(git -C "$target" fetch --prune origin 2>&1); then [ "$dirty" -eq 1 ] || fail "fetch: $out" fi branch=$("$gitea" default-branch "$full" 2>/dev/null) || branch='' if [ -z "$branch" ] || ! remote_has "$branch"; then branch='' for b in develop main master; do if remote_has "$b"; then branch="$b"; break; fi done fi [ -n "$branch" ] || fail "no default branch" if [ "$dirty" -eq 1 ]; then printf 'dirty %s\n' "$branch" exit 0 fi out=$(git -C "$target" checkout "$branch" 2>&1) || fail "checkout $branch: $out" out=$(git -C "$target" pull --ff-only origin "$branch" 2>&1) || fail "pull $branch: $out" printf 'updated\n'