同步Git分支变更的多种姿势
提示
当你在 feat/123 上开发,而队友已经把 feat/456 合并到了 dev,此时最重要的是:先把 dev 的最新变更同步到你的分支,再继续开发或提 交。这样可以避免后面再合并时出现一长串难解的冲突。
为什么要在合并前同步 dev
- 减少冲突:越早同步,差异越小,冲突也越容易处理。
- 保持上下文:能及时看到团队刚合入
dev的内容,避免重复劳动。 - 让提交历史清晰:同步后再提交,记录会直接跟在最新的
dev之后。
Rebase 系列方法
git pull --rebase(推荐做法)
# 1. 先把当前的开发内容提交或暂存
git status
# 2. 拉取 dev 最新变更,并把自己的提交"摘"下来重新放到 dev 之后
git pull origin dev --rebase
# 3. 如果出现冲突,解决后继续
git add <解决冲突的文件>
git rebase --continue
优势:历史保持线性、减少无关提交、在 git log 中更易审计。
git rebase
git fetch origin dev
git rebase origin/dev
# 或者交互式整理
git rebase -i origin/dev
- 直接把当前分支的提交"移动"到最新
dev之后,可顺便调整提交顺序、合并冗余提交; - 会重写提交哈希,适用于未共享或可强制推送的分支;
- 结合
-i可精细控制提交粒度。
处理未提交的修改:git stash
在执行 git rebase 或 git pull --rebase 时,如果工作区有未提交的修改,Git 会提示:
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.
如果不想提交这些修改,可以使用 git stash 暂存:
# 1. 暂存当前未提交的修改
git stash
# 2. 执行 rebase
git rebase origin/dev
# 3. rebase 完成后,恢复之前暂存的修改
git stash pop
git stash 常用操作
# 查看所有暂存的修改
git stash list
# 暂存时添加描述信息
git stash push -m "描述信息"
# 只暂存已暂存的文件(不包括未暂存的文件)
git stash push --staged
# 恢复最近的暂存(保留暂存记录)
git stash apply
# 恢复最近的暂存(删除暂存记录)
git stash pop
# 恢复指定的暂存(stash@{0} 是最近的)
git stash apply stash@{0}
# 删除暂存记录(不恢复)
git stash drop stash@{0}
# 清空所有暂存记录
git stash clear
为什么需要暂存
git rebase 会重写提交历史,需要干净的工作区:
- 避免未保存的修改在 rebase 过程中丢失
- 确保冲突解决时状态清晰
- 保证 rebase 操作的可逆性
注意事项
- 冲突处理:
git stash pop可能产生冲突,如果恢复的修改与 rebase 后的代码冲突,需要手动解决。 - 暂存范围:默认暂存所有修改(包括已暂存和未暂存),使用
--staged可只暂存已暂存的文件。 - 未跟踪文件:默认不暂存未跟踪的文件(新文件),需要时使用
git stash push -u。
Merge 系列方法
git merge(备选方案)
git fetch origin dev
git merge origin/dev
不重写历史,适合希望完整保留拓扑结构的团队。但会额外生成 merge commit,git log 中会看到 dev 的全部历史。
为什么会看到很多历史提交但改动很少
执行 git merge origin/dev 时,Git 会把 dev 的全部提交记录纳入当前分支的历史,哪怕这些提交与当前文件无关。git log 因此显得"很长",但 git diff 只显示真正的文件差异,属于日志层面的噪音。
git merge --squash
git fetch origin dev
git merge --squash origin/dev
git commit -m "sync dev changes"
- 以单个提交带入
dev的全部改动,保持分支历史紧凑; - 不生成 merge commit,也不会记录
dev的提交拓扑; - 执行完必须手动
git commit才会真正落盘。
git merge --ff-only
git fetch origin dev
git merge --ff-only origin/dev
- 只允许 fast-forward,确保历史绝对线性;
- 若存在本地独立提交会被拒绝,需要先 rebase;
- 适合发布分支或对历史有严格要求的场景。
其他方法
git cherry-pick
# 只需要 dev 上的某个具体提交
git fetch origin dev
git cherry-pick <commit-hash>
- 精确提取指定提交,适合紧急修复或仅需部分改动;
- 会在当前分支生成新的提交(哈希不同于原提交);
- 建议在 commit message 里标注来源,方便追溯。
git reset --hard
git fetch origin dev
git reset --hard origin/dev
- 直接把当前分支重置为远端
dev,丢弃本地所有提交与修改; - 适用于纯同步分支或临时分支;
- 执行前需确认没有需要保留的本地改动(可先
git stash)。
如何选择
| 场景 | 推荐命令 | 原因/特点 |
|---|---|---|
| 保持线性历史并完整同步 dev | git pull --rebase origin dev | 无额外提交,记录紧凑,便于审计 |
| 保留真实拓扑结构 | git merge origin/dev | 完整保留 dev 的历史结构 |
| 需要单个同步提交 | git merge --squash origin/dev | 用一个汇总提交代替大量历史提交 |
| 只需要部分提交 | git cherry-pick <hash> | 精确引入特定提交 |
| 需要整理提交/调整顺序 | git rebase origin/dev | 支持交互式编辑、合并或改写提交 |
| 强制要求 fast-forward | git merge --ff-only origin/dev | 没有 merge commit,历史严格线性 |
| 本地分支可直接重置为远端状态 | git reset --hard origin/dev | 丢弃本地修改,最快速地与远端保持一致(需谨慎使用) |