为什么你的 PR 总是多出一堆奇怪的 commit?90% 的人都踩过这个 Git 坑

为什么你的 PR 总是多出一堆奇怪的 commit?90% 的人都踩过这个 Git 坑

在日常开发中,功能分支开发周期较长时,主干分支往往已经有了新的提交。这时需要将主干的最新代码同步到自己的功能分支,常见的方式有两种:mergerebase。本文通过实际场景对比两种方式的区别与使用方法。


背景:为什么需要同步主干?

假设你从主干 main 切出了功能分支 feature/my-work,开发过程中主干有了新的提交,此时你的分支历史如下:

makefile 复制代码
main:    A ── B ── C(新提交)

feature: A ── B ── D(你的提交)

如果不同步主干,提 PR 时可能产生冲突,或者遗漏主干的重要修改。


git pull 是什么?

git pullgit fetch + 合并操作的快捷命令,默认使用 merge 方式:

bash 复制代码
git pull origin main
# 等价于
git fetch origin
git merge origin/main

也可以指定使用 rebase 方式:

bash 复制代码
git pull --rebase origin main
# 等价于
git fetch origin
git rebase origin/main

两者的区别与下文 merge / rebase 章节一致。如果希望 git pull 始终使用 rebase,可以设置全局配置:

bash 复制代码
git config --global pull.rebase true

💡 建议 :对于个人功能分支,推荐全局开启 pull.rebase true,保持提交历史线性整洁。


方式一:merge

原理

将主干的最新提交合并进你的分支,并生成一个新的 MergeCommit 记录这次合并。

makefile 复制代码
main:    A ── B ── C
                    \
feature: A ── B ── D ── M(MergeCommit)

操作步骤

第一步:拉取远程最新代码

bash 复制代码
git fetch origin

第二步:切换到自己的功能分支

bash 复制代码
git checkout feature/my-work

第三步:合并主干

bash 复制代码
git merge origin/main

处理冲突(如有)

bash 复制代码
# 手动解决冲突后
git add .
git merge --continue

# 放弃本次合并
git merge --abort

第四步:推送到远程

bash 复制代码
git push origin feature/my-work

提 PR 后的 commit 记录

sql 复制代码
D  你的提交
M  Merge branch 'origin/main' into feature/my-work

方式二:rebase(推荐)

原理

将你的提交「移植」到主干最新提交的后面,历史记录保持线性,不会产生多余的 MergeCommit

vbnet 复制代码
main:    A ── B ── C
                    \
feature: A ── B ── C ── D'(你的提交被接在 C 后面)

操作步骤

第一步:拉取远程最新代码

bash 复制代码
git fetch origin

第二步:切换到自己的功能分支

bash 复制代码
git checkout feature/my-work

第三步:变基到主干

bash 复制代码
git rebase origin/main

处理冲突(如有)

rebase 会逐个回放你的每一个 commit,每个 commit 都可能产生冲突,需要逐一处理:

bash 复制代码
# 手动解决冲突后
git add .
git rebase --continue

# 放弃本次 rebase
git rebase --abort

第四步:force push 到远程

由于 rebase 改写了本地提交历史,必须使用 force push 同步到远程:

bash 复制代码
git push origin feature/my-work --force-with-lease

⚠️ 为什么用 --force-with-lease 而不是 --force --force 会无条件覆盖远程分支,存在覆盖他人提交的风险。 --force-with-lease 更安全------如果远程分支有你本地没有的新提交,会拒绝推送并提示,避免意外覆盖他人代码。

提 PR 后的 commit 记录

vbnet 复制代码
D'  你的提交(仅此一条,干净清晰)

两种方式对比

merge rebase
历史记录 非线性,含 MergeCommit 线性,简洁清晰
PR commit 数量 自己的 commit + MergeCommit 只有自己的 commit
推送方式 正常 git push git push --force-with-lease
冲突处理次数 只处理一次 每个 commit 都可能处理一次
适合场景 多人协作的公共分支 个人功能分支同步主干

常见问题

Q:rebase 时提示 hint: use --reapply-cherry-picks to include skipped commits

这是正常现象。rebase 检测到你分支上的某个 commit 内容已经存在于目标分支(例如之前被 cherry-pick 过),会自动跳过该 commit,避免重复提交。

可以关闭这条提示:

bash 复制代码
git config --global advice.skippedCherryPicks false

Q:rebase 之后提 PR,为什么还是有重复的 commit?

rebase 只改写了本地历史,如果没有执行 force push,远程分支仍然是旧的历史。PR 提的是远程分支,所以重复的 commit 还是会被带进去。

务必在 rebase 完成后执行:

bash 复制代码
git push origin feature/my-work --force-with-lease

Q:git pull 默认是 merge,能改成 rebase 吗?

可以,设置全局配置:

bash 复制代码
# git pull 默认使用 rebase
git config --global pull.rebase true

设置后,git pull 等价于 git pull --rebase,不再产生多余的 MergeCommit。


总结

  • 个人功能分支 同步主干,推荐使用 rebase,PR 历史干净,只包含自己真正新增的 commit。
  • 公共分支 之间的合并,推荐使用 merge,保留完整历史,不需要 force push,更安全。
  • 无论哪种方式,同步前都应先执行 git fetch origin 获取远程最新状态。
相关推荐
LinXunFeng2 小时前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
乘风gg6 小时前
为什么AI 时代来临,大部分人吃不到红利
前端·ai编程·claude
恋猫de小郭6 小时前
Android 限制侧载新进展,谷歌联合国内厂商推验证计划
android·前端·flutter
IT_陈寒6 小时前
Redis内存爆了,原来我漏掉了这个致命配置
前端·人工智能·后端
恋猫de小郭6 小时前
解读 Android 17 全新内存限制,有没有“豁免”后门?
android·前端·flutter
Hyyy7 小时前
理解LLM的基本工作原理:预训练、微调、推理的区别
前端
Gatlin8 小时前
前端逆向与反逆向:一场猫鼠游戏的底层逻辑与实战
前端
Pedantic8 小时前
本地通知(Local Notifications)学习笔记
前端
森蓝情丶9 小时前
我给 AI 搭了个法庭:一个前端仔的 LangGraph 实战全记录
前端·后端
爱勇宝9 小时前
干了近 8 年,一夜之间被裁:AI 时代,程序员最该害怕的不是 AI
前端·后端·程序员