Skip to content

Instantly share code, notes, and snippets.

@naoyeye
Created August 13, 2025 11:50
Show Gist options
  • Save naoyeye/e367e684aff6019aaa4c58045715051c to your computer and use it in GitHub Desktop.
Save naoyeye/e367e684aff6019aaa4c58045715051c to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage:
$(basename "$0") --base <upstream/ref> [--push] [--remote <origin>] [--branch <current-branch>] <commit> [<commit> ...]
Examples:
# 仅保留单个提交,并推送到 origin 当前分支
$(basename "$0") --base upstream/7.107.0 --push 7b07c159ce16b1d3e04c564fc1a05714e199fca3
# 仅保留多个提交(按给定顺序依次 cherry-pick)
$(basename "$0") --base upstream/7.107.0 abcdef1 2345678 9abcde0
Options:
--base <ref> 必填,作为重置基线的引用(例如 upstream/7.107.0)
--push 可选,完成后以 --force-with-lease 推送到远端
--remote <name> 可选,远端名,默认 origin
--branch <name> 可选,目标本地分支,默认当前分支
-h, --help 显示本帮助
EOF
}
BASE_REF=""
PUSH_AFTER=0
REMOTE_NAME="origin"
TARGET_BRANCH=""
COMMITS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--base)
[[ $# -ge 2 ]] || { echo "--base 需要一个参数"; exit 2; }
BASE_REF="$2"; shift 2 ;;
--push)
PUSH_AFTER=1; shift ;;
--remote)
[[ $# -ge 2 ]] || { echo "--remote 需要一个参数"; exit 2; }
REMOTE_NAME="$2"; shift 2 ;;
--branch)
[[ $# -ge 2 ]] || { echo "--branch 需要一个参数"; exit 2; }
TARGET_BRANCH="$2"; shift 2 ;;
-h|--help)
usage; exit 0 ;;
--)
shift; while [[ $# -gt 0 ]]; do COMMITS+=("$1"); shift; done ;;
*)
COMMITS+=("$1"); shift ;;
esac
done
if [[ -z "$BASE_REF" ]]; then
echo "错误:必须指定 --base <ref>"
usage
exit 2
fi
if [[ ${#COMMITS[@]} -eq 0 ]]; then
echo "错误:至少需要一个待保留的提交 SHA"
usage
exit 2
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "当前目录不是 git 仓库"
exit 2
fi
# 计算目标分支
if [[ -z "$TARGET_BRANCH" ]]; then
TARGET_BRANCH=$(git rev-parse --abbrev-ref HEAD)
fi
# 确保工作区干净
if ! git diff-index --quiet HEAD --; then
echo "工作区存在未提交改动,请先提交或清理后再执行。"
exit 2
fi
echo "[1/5] 获取远端并清理失效引用..."
git fetch --all --prune
echo "[2/5] 切换到目标分支: ${TARGET_BRANCH}"
git switch "$TARGET_BRANCH" >/dev/null 2>&1 || git checkout "$TARGET_BRANCH"
echo "[3/5] 硬重置到基线: ${BASE_REF}"
git reset --hard "$BASE_REF"
echo "[4/5] 依次 cherry-pick 指定提交..."
for c in "${COMMITS[@]}"; do
echo " - cherry-pick $c"
git cherry-pick "$c"
done
echo "[5/5] 完成。当前分支相对基线的提交如下:"
git log --oneline "${BASE_REF}..${TARGET_BRANCH}"
if [[ $PUSH_AFTER -eq 1 ]]; then
echo "推送到远端:${REMOTE_NAME} ${TARGET_BRANCH} (--force-with-lease)"
git push "$REMOTE_NAME" --force-with-lease "$TARGET_BRANCH"
fi
echo "Done."
@naoyeye
Copy link
Author

naoyeye commented Aug 13, 2025

为何 rebase 后混入了他人提交了历史记录

成因

  • 选择了错误的基线 ref:把分支 rebase 到非预期的分支/提交,导致历史包含了不应保留的提交。
  • 交互式 rebase 时编辑了错误的 todo 列表:误把不该保留的提交标记为 pick(或忘记 drop)。
  • 混合了 merge 提交:使用了 git pull(默认 merge)或在解决冲突时生成了额外合并历史。
  • 本地分支分叉点过旧:在较老的基点上叠加新提交,rebase 时把中间层的外部提交一并带上。
  • 多远端/多分支同名的混用:例如 origin/7.107.0upstream/7.107.0 交替当基线,容易选错。

避免办法(最有效的几条)

  • 明确基线:每次 rebase 前先确认目标基线,例如:
    git fetch upstream --prune
    git log --oneline -n 3 upstream/7.107.0
  • 推送前自检:确保仅包含自己的提交
    git log upstream/7.107.0..HEAD --oneline
    # 理想情况:只显示你需要保留的那几条
  • 统一使用 rebase 工作流:设置 git config pull.rebase true,避免无意 merge。
  • 小步提交、小跨度分支:减少长时间未同步导致的复杂 rebase。
  • 交互式 rebase 谨慎核对:git rebase -i <base> 时确认 pick 顺序与数量;不确定就先备份分支:git branch backup/xxx
  • 保留强推安全带:使用 --force-with-lease(而非 --force)避免覆盖别人已推的新提交。

脚本安装(放到 ~/,全项目可用)

  • 将脚本赋权:
    chmod +x ~/git-clean-branch.sh
  • 确保可执行所在目录在 PATH 中(若未在 PATH):
    echo 'export PATH="$HOME:$PATH"' >> ~/.zshrc
    source ~/.zshrc
  • 可选:设置别名
    echo 'alias git-clean-branch="$HOME/git-clean-branch.sh"' >> ~/.zshrc
    source ~/.zshrc

脚本用法(git-clean-branch.sh)

  • 基本功能:将当前分支硬重置到指定基线,只保留你传入的提交(可多个,按顺序 cherry-pick),可选强推到远端。
  • 参数说明:
    • --base <ref>(必填):重置基线,如 upstream/7.107.0
    • --push:完成后使用 --force-with-lease 推送到远端
    • --remote <name>:远端名,默认 origin
    • --branch <name>:目标本地分支,默认当前分支
    • <commit>...:要保留的提交 SHA(1 个或多个)
  • 单提交,推送到 origin 当前分支:
    git-clean-branch.sh --base upstream/7.107.0 --push 7b07c159ce16b1d3e04c564fc1a05714e199fca3
  • 多提交(按给定顺序依次保留),不推送:
    git-clean-branch.sh --base upstream/7.107.0 abcdef1 2345678 9abcde0
  • 指定远端与分支(例如在非当前分支修复):
    git-clean-branch.sh --base upstream/7.107.0 --remote origin --branch 7.107.0 --push 7b07c159ce16b...
  • 快速校验(脚本已输出对比,也可单独执行):
    git log upstream/7.107.0..HEAD --oneline
    # 仅应显示你刚保留的提交

注意

  • 运行前需保证工作区干净(无未提交改动)。
  • cherry-pick 后提交哈希会变化(正常现象)。
  • --push 使用 --force-with-lease,能避免误覆盖他人刚推的更新。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment