Created
August 13, 2025 11:50
-
-
Save naoyeye/e367e684aff6019aaa4c58045715051c to your computer and use it in GitHub Desktop.
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
| #!/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." | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
为何 rebase 后混入了他人提交了历史记录
成因
git pull(默认 merge)或在解决冲突时生成了额外合并历史。origin/7.107.0与upstream/7.107.0交替当基线,容易选错。避免办法(最有效的几条)
git log upstream/7.107.0..HEAD --oneline # 理想情况:只显示你需要保留的那几条git config pull.rebase true,避免无意 merge。git rebase -i <base>时确认 pick 顺序与数量;不确定就先备份分支:git branch backup/xxx。--force-with-lease(而非--force)避免覆盖别人已推的新提交。脚本安装(放到 ~/,全项目可用)
chmod +x ~/git-clean-branch.sh脚本用法(git-clean-branch.sh)
--base <ref>(必填):重置基线,如upstream/7.107.0--push:完成后使用--force-with-lease推送到远端--remote <name>:远端名,默认origin--branch <name>:目标本地分支,默认当前分支<commit>...:要保留的提交 SHA(1 个或多个)git log upstream/7.107.0..HEAD --oneline # 仅应显示你刚保留的提交注意
--push使用--force-with-lease,能避免误覆盖他人刚推的更新。