Git 提交署错名字别硬改了!dry-run 预览 + 逐条确认 + 自动备份,一条命令搞定

周五下午,你刚把项目推上公司仓库,同事路过瞄了一眼 git log:
"哎,你这提交怎么都是
xiaoming@qq.com?"
你低头一看,心里咯噔一下——
新电脑没配全局邮箱,过去三个月的提交,作者全是私人账号。
更糟的是:公司 CI 按邮箱统计贡献,你这三个月的代码,系统里查无此人。

遇到这种事,大多数人的第一反应是搜索引擎:
"git 修改历史提交作者"
然后复制回来一段 git filter-branch --env-filter '...',改两个变量,回车——
然后你就开始祈祷:
- 我会不会把别人的提交也一起改了?
- 我要是只想改最近 5 个怎么办?
- 改错了怎么回滚?
- 到底会改到哪些提交,能先看看吗?
这些问题,一条裸命令一个都回答不了。
所以我把这些"祈祷"全部写成了功能,做成了一个脚本:git-change-author.sh。
它能做什么?一张图看懂
先说结论:这是一个纯 Bash、零依赖的脚本,只用 git 自带命令(底层是 git filter-branch --env-filter),不需要装 git-filter-repo,拷过去就能用。
它围绕"改作者"这件事,补齐了一整套安全措施:

| 能力 | 对应参数 | 解决什么问题 |
|---|---|---|
| 精确定位旧作者 | --old-name + --old-email | 姓名和邮箱同时匹配才替换,不误伤别人 |
| 全量覆盖 | --all | 整个仓库统一成新身份 |
| 最近 N 次 | --last N | 只动最近几个提交 |
| 时间范围 | --since / --until | 只改某一段时间内的提交 |
| 指定分支 | --branch <ref> | 默认全部分支,也可只改一个 |
| 同时改提交者 | --also-committer | author 和 committer 一起换 |
| 列出作者 | --list-authors | 先看历史里到底有哪些"人" |
| 预览 | --dry-run | 只告诉你会改哪些,不动仓库 |
| 逐条确认 | --interactive | 每个提交 y/n,随时 q 退出 |
| 自动备份 | 内置 | 改写前清理并生成 refs/original 备份 |
先看人,再动手:--list-authors
很多人改作者失败,是因为旧作者的姓名/邮箱根本没写对——大小写差一个字母,就匹配不上。
所以第一步,先让脚本把历史里出现过的作者全列出来:
./git-change-author.sh --list-authors
它会用 git shortlog 去重汇总,按提交数降序排列:
==================================================================
当前仓库历史中的作者与邮箱(按提交数降序,共 3 位):
==================================================================
提交数 作者 <邮箱>
------------------------------------------------------------------
128 小明 <xiaoming@qq.com>
45 Alice <alice@company.com>
3 ming <ming@localhost>
------------------------------------------------------------------
提示:复制其中一行的『姓名 <邮箱>』可作为 --old-name / --old-email 的参考。
作者太多?加 --limit 10 只看前 10 位,终端里还会问你要不要展开全部。
直接复制,不再手敲。
核心用法:三个场景,三条命令
场景一:把"私人身份"换成"公司身份"(精确匹配)
./git-change-author.sh \
--old-name "小明" --old-email xiaoming@qq.com \
--new-name "小明" --new-email xiaoming@company.com
关键点:姓名 + 邮箱必须同时匹配才会改。同事 Alice 的提交,一个字都不会动。
场景二:只改最近 5 次,并且逐个确认
./git-change-author.sh --all \
--new-name "DevTeam" --new-email dev@company.com \
--last 5 --interactive
场景三:只改 2024 年内、某个分支上的提交
./git-change-author.sh --all \
--new-name "DevTeam" --new-email dev@company.com \
--since 2024-01-01 --until 2024-12-31 --branch master
日期支持 git 能解析的任何格式,"2 weeks ago" 这种也行。
我最喜欢的两个设计
1)--dry-run:先看,不动
任何会"重写历史"的操作,我都建议先预览:
./git-change-author.sh --old-name "小明" --old-email xiaoming@qq.com \
--new-name "小明" --new-email xiaoming@company.com --dry-run
输出会列出每一个将被改写的提交哈希、当前作者和提交说明,最后一行写着:
预览结束,未对仓库做任何修改。
确认列表没问题,再去掉 --dry-run 正式执行。
2)交互式确认:像 git add -p 一样逐个决定
在终端里直接运行(不加 --force)时,脚本默认进入交互模式:

每个候选提交会展示哈希、作者、时间、说明,然后问你:
- y —— 改这一个
- n —— 跳过
- a —— 剩下的全改
- q —— 放弃,退出(什么都不会改)
在管道或 CI 里运行(非 TTY)时不会自动进入交互,避免卡死;想跳过最终确认可以加
--force。
它是怎么工作的?
整个流程,我按"先确认、再动手"的顺序设计:

- 解析参数;在终端里缺什么就问你什么(新姓名、新邮箱、定位方式)
- 校验:是否在 Git 仓库里、
--last是不是正整数、已跟踪文件有没有未提交改动 - 圈定候选提交:先按
--last / --since / --until / --branch圈范围,再按"姓名+邮箱"过滤 - 交互确认(可选):得到最终的允许列表,写入临时文件
- 输出执行计划:旧作者、新作者、范围、候选数、将修改数、备份位置
--dry-run到此为止;否则二次确认- 执行
git filter-branch --env-filter:只对允许列表里的提交替换 author(可选 committer)
有个细节值得说一下:env-filter 里是用 grep -qxF -f 允许列表文件 对 $GIT_COMMIT 做整行精确匹配,所以"最近 N 次""时间范围"这种过滤,都在前面算好,filter-branch 本身只负责"对列表里的提交换人"。
⚠️ 必须知道的三件事
这部分请务必看完。
1. 哈希会变——不只是被改的那几个
Git 的每个提交哈希,都包含父提交的哈希。你改了第 100 个提交,它之后的所有提交,哈希全部连锁变化。
即使你只想改"最近 5 个",脚本也要重写整条链上受影响的部分。

所以:重写历史 = 产生一段全新的历史。
2. 已经推送过的仓库,要 force push,先和团队打招呼
脚本跑完会提示你:
git push --force --tags origin '<分支>'
这会覆盖远程历史。同事本地的分支会和远程"分叉"。多人协作的仓库,请先沟通,让大家在你推送后重新拉取(或基于新历史重建分支)。
3. 后悔药和"彻底清理"
脚本改写前会先清掉旧的 refs/original,改写后 filter-branch 会把原历史留在那里。想回滚:
git reset --hard refs/original/refs/heads/<分支名>
想彻底删除旧对象、回收空间(同时也意味着没有后悔药了):
git reflog expire --expire=now --all && git gc --prune=now
建议顺序:确认新历史没问题 → 再 gc。
几个使用小贴士
- 默认不改 committer。 如果你希望提交者也一并变更,加
--also-committer。 - 工作区要干净。 已跟踪文件有未提交改动时脚本会直接拒绝;未跟踪文件只提示,不阻塞(用
git stash -u可一并暂存)。 - 默认范围是
--all,也就是所有分支和标签都会被重写;只想动一个分支,用--branch。 - 姓名/邮箱里别带单引号。 脚本通过单引号把新身份塞进 env-filter,含
'的名字需要先自行处理。 - 先在副本上演练。 最稳妥的做法:
git clone一份,在副本上跑通--dry-run和正式执行,确认无误再在正式仓库操作。
推荐的"安全五步"
1. git clone 一份副本(或至少确认已有可用备份)
2. ./git-change-author.sh --list-authors # 看清历史里有谁
3. 加上你的参数 + --dry-run # 预览会改哪些
4. 去掉 --dry-run,--interactive 逐条确认 # 正式执行
5. 检查 git log,确认无误 → 再 force push、再 gc
写在最后
改 Git 历史这件事,难的从来不是那条命令,而是**"我真的改对了吗?"**这份不安。
所以这个脚本做的事很朴素:把"预览、确认、备份、回滚"这些本该有的安全措施,一个个补上。
- 想看有哪些作者 →
--list-authors - 想先看看会改什么 →
--dry-run - 想逐个决定 →
--interactive - 想反悔 →
refs/original
工具不该让人提心吊胆,该让人心里有底。
如果这个脚本帮你省下了一个周五下午的焦虑,欢迎点个在看、转给那个总是忘记配邮箱的同事。
你还遇到过哪些"Git 手滑"的瞬间?留言区聊聊 👇
📎 获取脚本:https://github.com/zhengmingliang/code-fragment/blob/main/shell/linux/git/git-change-author.sh
⚠️ 免责声明:重写历史是不可逆性很强的操作,请务必先备份、先
--dry-run,并在团队协作的仓库中提前沟通。
Q.E.D.


