Last active
March 18, 2026 01:56
-
-
Save cesclong/370fa54002b7dcf306309ddd9d09ac08 to your computer and use it in GitHub Desktop.
split-pr-safe-worktree.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -euo pipefail | |
| # ============================================================================== | |
| # 功能: | |
| # 1. 使用 git worktree 完全隔离环境,不污染原仓库 | |
| # 2. 保留完整 commit 历史、信息、作者、时间 | |
| # 3. 自动拆分为「代码分支」+「配置分支」 | |
| # 4. 自动验证文件不丢失、内容完全一致 | |
| # 5. 无论成功/失败/中断,都自动清理 worktree,不留垃圾 | |
| # 6. 最终输出:本地两个干净分支,可直接 push 开 PR | |
| # ============================================================================== | |
| # 使用示例: | |
| # ./split-pr-safe-worktree-v3.sh \ | |
| # --repo-dir /path/to/your/repo \ | |
| # --base-branch develop \ | |
| # --code-branch pr-code-only \ | |
| # --config-branch pr-config-only \ | |
| # --worktree-dir /tmp/pr-split-worktree | |
| # ============================================================================== | |
| # ====================== 全局善后清理函数(无论如何都执行)====================== | |
| cleanup() { | |
| if [[ -n "${WORKTREE_DIR:-}" && -d "${WORKTREE_DIR:-}" ]]; then | |
| echo -e "\n🧹 清理隔离工作区: $WORKTREE_DIR" | |
| rm -rf "$WORKTREE_DIR" | |
| git -C "$REPO_DIR" worktree prune 2>/dev/null || true | |
| fi | |
| } | |
| # 脚本结束(正常退出、报错、Ctrl+C)都执行清理 | |
| trap cleanup EXIT | |
| # ====================== 参数解析 ====================== | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --repo-dir) REPO_DIR="$2"; shift 2 ;; | |
| --base-branch) BASE_BRANCH="$2"; shift 2 ;; | |
| --code-branch) CODE_BRANCH="$2"; shift 2 ;; | |
| --config-branch) CONFIG_BRANCH="$2"; shift 2 ;; | |
| --worktree-dir) WORKTREE_DIR="$2"; shift 2 ;; | |
| *) | |
| echo "未知参数: $1" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # ====================== 必传参数检查 ====================== | |
| required_vars=(REPO_DIR BASE_BRANCH CODE_BRANCH CONFIG_BRANCH WORKTREE_DIR) | |
| for var in "${required_vars[@]}"; do | |
| if [[ -z "${!var:-}" ]]; then | |
| echo "错误:缺少必须参数 --${var,,}" | |
| exit 1 | |
| fi | |
| done | |
| # ====================== 文件 类型配置 ====================== | |
| CODE_EXTS_REGEX="\.(java|kt)$" | |
| CONFIG_EXTS_REGEX="\.(json|xml|txt|yml|yaml|properties|conf)$" | |
| # ========================================================== | |
| echo "==============================================" | |
| echo "🔒 安全拆分 PR(git worktree 隔离模式)" | |
| echo "原仓库: $REPO_DIR" | |
| echo "基准分支: $BASE_BRANCH" | |
| echo "代码分支: $CODE_BRANCH" | |
| echo "配置分支: $CONFIG_BRANCH" | |
| echo "工作目录: $WORKTREE_DIR" | |
| echo "==============================================" | |
| # 进入原仓库 | |
| cd "$REPO_DIR" | |
| git fetch | |
| ORIGINAL_BRANCH=$(git branch --show-current) | |
| echo -e "✅ 当前开发分支: $ORIGINAL_BRANCH" | |
| # 创建隔离 worktree | |
| echo -e "\n📂 创建隔离工作区..." | |
| rm -rf "$WORKTREE_DIR" | |
| git worktree add "$WORKTREE_DIR" "$BASE_BRANCH" | |
| cd "$WORKTREE_DIR" | |
| # 获取所有 commit(从旧到新) | |
| COMMITS=$(git rev-list --reverse "$BASE_BRANCH..$ORIGINAL_BRANCH") | |
| COMMIT_COUNT=$(echo "$COMMITS" | wc -l | tr -d ' ') | |
| echo -e "🔍 共 $COMMIT_COUNT 个 commit 需要处理" | |
| # -------------------------------------------------------------------- | |
| # 函数:创建过滤分支 | |
| # $1 分支名 | |
| # $2 保留文件正则 | |
| # -------------------------------------------------------------------- | |
| create_filtered_branch() { | |
| local branch_name="$1" | |
| local keep_pattern="$2" | |
| echo -e "\n==============================================" | |
| echo "🚀 构建分支: $branch_name" | |
| echo "==============================================" | |
| git checkout -B "$branch_name" "$BASE_BRANCH" | |
| for commit in $COMMITS; do | |
| echo -e "\n➤ 处理 commit: $commit" | |
| git cherry-pick --no-commit "$commit" | |
| git add -A | |
| # 不属于当前类型的文件全部恢复为基准版本 | |
| git diff --name-only | grep -vE "$keep_pattern" | xargs -r git checkout HEAD -- | |
| # 保留原 commit 的所有信息 | |
| git commit -C "$commit" | |
| done | |
| } | |
| # 开始拆分 | |
| create_filtered_branch "$CODE_BRANCH" "$CODE_EXTS_REGEX" | |
| create_filtered_branch "$CONFIG_BRANCH" "$CONFIG_EXTS_REGEX" | |
| cd "$REPO_DIR" | |
| # ============================================== | |
| # 🧪 自动验证:文件是否完整不丢失 | |
| # ============================================== | |
| echo -e "\n==============================================" | |
| echo "🧪 开始自动验证" | |
| echo "==============================================" | |
| CODE_LIST=$(mktemp) | |
| CONFIG_LIST=$(mktemp) | |
| ALL_LIST=$(mktemp) | |
| git -C "$WORKTREE_DIR" diff --name-only "$BASE_BRANCH" "$CODE_BRANCH" | sort -u > "$CODE_LIST" | |
| git -C "$WORKTREE_DIR" diff --name-only "$BASE_BRANCH" "$CONFIG_BRANCH" | sort -u > "$CONFIG_LIST" | |
| git diff --name-only "$BASE_BRANCH" "$ORIGINAL_BRANCH" | sort -u > "$ALL_LIST" | |
| COMBINED=$(mktemp) | |
| cat "$CODE_LIST" "$CONFIG_LIST" | sort -u | uniq > "$COMBINED" | |
| if diff -q "$COMBINED" "$ALL_LIST" >/dev/null; then | |
| echo -e "\n✅ 校验通过:代码 + 配置 = 原分支全部修改,无丢失!" | |
| else | |
| echo -e "\n❌ 校验失败:存在文件丢失或不一致!" | |
| diff "$COMBINED" "$ALL_LIST" | |
| exit 1 | |
| fi | |
| # 展示统计 | |
| CODE_CNT=$(wc -l < "$CODE_LIST") | |
| CONFIG_CNT=$(wc -l < "$CONFIG_LIST") | |
| ALL_CNT=$(wc -l < "$ALL_LIST") | |
| echo -e "\n📊 修改文件统计:" | |
| echo "代码文件:$CODE_CNT 个" | |
| echo "配置文件:$CONFIG_CNT 个" | |
| echo "原分支总修改:$ALL_CNT 个" | |
| echo "校验结果:$CODE_CNT + $CONFIG_CNT = $ALL_CNT" | |
| rm -f "$CODE_LIST" "$CONFIG_LIST" "$ALL_LIST" "$COMBINED" | |
| # ============================================== | |
| # 🎉 最终提示 | |
| # ============================================== | |
| echo -e "\n==============================================" | |
| echo "🎉 全部完成!" | |
| echo "" | |
| echo "✅ 两个分支已创建在原仓库中:" | |
| echo " - $CODE_BRANCH" | |
| echo " - $CONFIG_BRANCH" | |
| echo "" | |
| echo "✅ 可直接推送远程:" | |
| echo " git push origin $CODE_BRANCH" | |
| echo " git push origin $CONFIG_BRANCH" | |
| echo "" | |
| echo "✅ worktree 已自动清理,环境干净无残留" | |
| echo "==============================================" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment