Skip to content

Instantly share code, notes, and snippets.

@cesclong
Last active March 18, 2026 01:56
Show Gist options
  • Select an option

  • Save cesclong/370fa54002b7dcf306309ddd9d09ac08 to your computer and use it in GitHub Desktop.

Select an option

Save cesclong/370fa54002b7dcf306309ddd9d09ac08 to your computer and use it in GitHub Desktop.
split-pr-safe-worktree.sh
#!/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