关于 non-interactive-git-rebase
通过 GIT_SEQUENCE_EDITOR 脚本化 Git 交互式 rebase 的技能指南,面向需要批量调整提交历史的开发者。涵盖重排、删除、压缩、拆分和改写提交,并要求先创建备份分支、完成后验证。
主要功能
- 01用预制 todo 文件执行交互式 rebase
- 02支持重排、删除、压缩和拆分提交
- 03以 edit 和 amend 非交互式改写消息
- 04使用 --exec 批量修改提交
- 05要求备份分支并验证结果
适用场景
- 01脚本化调整提交顺序或删除提交
- 02批量修改提交作者或消息
- 03将单个提交拆分为多个提交
- 04在重写历史前后检查代码树
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ facebook/ hermes --skill non-interactive-git-rebase安装后在 Claude Code 中输入/non-interactive-git-rebase确认已加载。
详细介绍
非交互式 Git Rebase
概述
git rebase -i 通常会打开编辑器来编辑 todo 列表。将 GIT_SEQUENCE_EDITOR 设置为一条命令,用预先准备好的文件替换 todo 文件,即可完全通过脚本执行任何交互式 rebase 操作——重排、丢弃、压缩、编辑、修改提交等。
重要:始终创建备份分支
执行任何 rebase 前,都要创建备份分支:
git branch backup-before-rebase HEAD
rebase 成功且验证通过后,不要自动删除备份分支。 除非用户明确要求自动清理,否则删除前务必征求用户同意。保留备份的成本很低,而且用户之后可能会发现问题。
核心技巧
GIT_SEQUENCE_EDITOR 的工作原理
编辑器会将 todo 文件路径作为 $1 接收。任何能覆盖该文件的命令都可以使用:
# Replace the todo with our prepared version
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
Git 会生成默认的 todo(所有行都是 pick),然后调用 cp /tmp/prepared-todo <todo-file>,用你的版本替换它。接着,Git 会执行改写后的 todo。
准备 Todo 文件
提取默认 todo,但不实际执行 rebase——先将其复制出来,再让编辑器执行失败,使 git 中止:
GIT_SEQUENCE_EDITOR='cp $1 /tmp/default-todo && false' git rebase -i <base>
false 会以非零状态退出,因此 git 会中止 rebase。你会得到 todo 文件,但不会执行 rebase。
或者直接通过 git log 构建:
git rev-list --reverse <base>..HEAD | while read hash; do
echo "pick $hash $(git log --oneline -1 $hash | cut -d' ' -f2-)"
done > /tmp/prepared-todo
然后编辑该文件:重排行、更改 pick 为 drop/squash/edit 等。
使用 --exec 批量修改提交
--exec <cmd> 标志会在 todo 中每个 pick 后插入一行 exec <cmd>。结合 GIT_SEQUENCE_EDITOR=:(成功执行但不做任何操作,保留默认 todo 不变),即可在每个提交重放后运行命令,无需编辑器。
更改某个范围内所有提交的作者邮箱:
GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec \
'if [ "$(git log -1 --format=%ae)" != "[email protected]" ]; then git commit --amend --no-edit --author="Name <[email protected]>"; fi'
更改提交消息模式:
GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec \
'git commit --amend -m "$(git log -1 --format=%B | sed s/old/new/)"'
操作
重排提交
重新排列 todo 文件中的行。示例——将提交 abc123 移到最前面:
# Build todo with abc123 first, then everything else in original order
{
echo "pick abc123 The commit to move first"
git rev-list --reverse <base>..HEAD | while read hash; do
full=$(git rev-parse abc123)
[ "$hash" != "$full" ] && echo "pick $hash $(git log --oneline -1 $hash | cut -d' ' -f2-)"
done
} > /tmp/prepared-todo
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
丢弃提交
从 todo 中删除行,或将 pick 改为 drop。
修改提交消息
绝不要在 todo 中使用 reword。 reword 命令会打开交互式编辑器来修改新消息,无法通过 GIT_SEQUENCE_EDITOR 编写脚本实现。应改用 edit,并通过 -m 修改提交:
# Step 1: Prepare todo with 'edit' on commits to reword
# Step 2: At each stop, amend the message non-interactively:
git commit --amend -m "New subject line
New body text."
# Step 3: Continue to the next stop
git rebase --continue
压缩 / 修正提交
将 pick 改为 squash 或 fixup。使用 squash 时,还需要为压缩后打开的提交消息编辑器设置 GIT_SEQUENCE_EDITOR:
# Squash second commit into first, keep first message
# In the todo: pick aaa, fixup bbb
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
拆分提交
这需要在 todo 中使用 edit。rebase 停止后,运行命令进行拆分,然后继续。
# Step 1: Prepare todo with 'edit' on the target commit
sed 's/^pick TARGET_HASH/edit TARGET_HASH/' /tmp/default-todo > /tmp/prepared-todo
GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base>
# Step 2: Rebase stops at the target commit. Split it:
git reset HEAD~1
# Step 3: Selectively stage and commit pieces.
# If the commit has N hunks in a file and you want hunk 3:
printf 'n\nn\ny\n' | git add -p path/to/file
git commit -m "First piece: just hunk 3"
# Stage the rest
git add .
git commit -m "Second piece: remaining changes"
# Step 4: Continue the rebase
git rebase --continue
使用 git add -p 自动选择代码块: printf 中的每个 y/n 都对应一个按顺序出现的代码块。如果代码块之间分隔清晰(位于文件的不同区域),这种方法很可靠。如果代码块相邻,git 可能会将它们合并为一个代码块,此时你可能需要先输入 s(拆分):
# Split first hunk, then accept/reject sub-hunks
printf 's\ny\nn\ny\n' | git add -p path/to/file
替代方案——基于补丁进行拆分(对复杂情况更可靠):
git reset HEAD~1
git diff path/to/file > /tmp/full.patch
git checkout -- path/to/file
# Apply only the lines you want for commit 1
# (manually prepare /tmp/part1.patch from full.patch)
git apply /tmp/part1.patch
git add path/to/file
git commit -m "First piece"
git apply /tmp/full.patch # won't re-apply already-applied hunks
# Or: git checkout <original-hash> -- path/to/file
git add .
git commit -m "Second piece"
验证
任何 rebase 后,都要验证工作树内容与之前完全一致(除非你有意丢弃了提交):
git diff backup-before-rebase HEAD --stat
# Empty output = content preserved correctly
常见错误
| 错误 | 修正方法 |
|---|---|
在 todo 中使用 reword | reword 会打开交互式编辑器。改用 edit + git commit --amend -m |
| 之前执行过 rebase,todo 文件中的哈希值已过时 | 通过 git log 重新生成——每次 rebase 后哈希值都会改变 |
git add -p 遇到意外的代码块 | 上下文重叠时,代码块可能会合并。请改用基于补丁的拆分 |
| 过早删除备份分支 | 删除前务必征求用户同意。他们之后可能会发现问题 |
快速参考
| 操作 | 命令 |
|---|---|
| 提取 todo | GIT_SEQUENCE_EDITOR='cp $1 /tmp/default-todo && false' git rebase -i <base> |
| 重新排序/丢弃 | GIT_SEQUENCE_EDITOR="cp /tmp/prepared-todo" git rebase -i <base> |
| 修改提交说明 | 同上,在 todo 中使用 edit,然后运行 git commit --amend -m "..." 和 --continue |
| 合并(保留说明) | 同上,在 todo 中使用 fixup |
| 合并(编辑说明) | 同上,使用 squash,然后处理说明编辑器 |
| 拆分 | 同上,使用 edit,然后手动拆分并运行 --continue |
| 批量修订 | GIT_SEQUENCE_EDITOR=: git rebase -i <base> --exec '<cmd>' |