Git 分支合并规范:如何优雅地把功能分支合入主分支

多人协作开发中,功能分支合并到主分支时经常出现历史混乱、分叉等问题。本文分享一套标准操作流程,帮你实现个人分支历史纯净主分支线性历史合并粒度可控


先看问题

假设你从主分支 main1 拉了一个功能分支 feat/user 来开发用户管理模块:

复制代码
main1:  a---b---c---x---y (同事陆续提交了 x、y)
            ↑
feat/user:   \---d---e---f---g (你的多个 commit)

开发完了,要合并回去,但问题来了:

  • feat/user 上有好几个 commit,合并后主分支会变得很乱
  • main1 期间被同事提交了不少新代码,不能直接 merge
  • 你只想合并其中几个 commit,不是整个分支

怎么办?


三条合并原则

在动手之前,先记住这三条原则:

  1. 功能分支保持不变 ------ 不把主分支代码合进来,保持个人代码纯净
  2. 主分支一条直线 ------ 合并后历史是线性的,没有分叉
  3. 单 commit 合并 ------ 无论功能分支有多少 commit,合到主分支只体现为一条记录

八步完成合并

下面是完整的操作流程,跟着走就行。

第 1 步:确定要合并的 commit 范围

在功能分支上查看提交记录:

bash 复制代码
git checkout feat/user
git log --oneline

输出类似这样:

复制代码
a1b2c3d 功能4 - 完成数据导出(不需要)
e4f5g6h 功能3 - 完成数据筛选 (结束)
i7j8k9l 功能2 - 完成列表展示
m0n1o2p 功能1 - 完成页面框架 (开始)
q3r4s5t main1 的基 commit(不要)

记下起止 hash :开始 m0n1o2p,结束 e4f5g6h

第 2 步:同步主分支最新代码

bash 复制代码
git checkout main1
git pull origin main1

这一步很关键。同事可能已经往 main1 推了新代码,同步一下避免后续冲突。

第 3 步:创建临时合并分支并记录起点

从 main1 最新代码创建临时分支,在此分支上完成 cherry-pick 和压缩操作,不直接操作 main1

bash 复制代码
git checkout -b merge-for-main1

为什么要建临时分支?因为所有 cherry-pick 和压缩操作都在这里完成,不直接动 main1,安全可控。

创建好之后,立刻记录当前 HEAD 的 hash,后面压缩 commit 时要用到:

bash 复制代码
# 记录起点 hash 到变量
$START_MAIN1 = git rev-parse HEAD
echo $START_MAIN1

这个 hash 就是 main1 当前最新 commit 的位置,后面 reset --soft 会回到这个点。一定要记下来,否则压缩时找不到起点。

第 4 步:cherry-pick 指定范围的 commit

bash 复制代码
git cherry-pick m0n1o2p^..e4f5g6h

语法解释:开始^..结束 表示从"开始"到"结束"(都包含)的所有 commit。

如果遇到冲突,解决后执行:

bash 复制代码
git add .
git cherry-pick --continue --no-edit

注意:cherry-pick 后 commit hash 会变,但代码内容不变。

第 5 步:压缩成一个 commit

用第 3 步记录的起点 hash 做软重置,把所有 cherry-pick 的 commit 暂存到 staging area,然后重新提交:

bash 复制代码
git reset --soft $START_MAIN1
git commit -m "feat: 用户功能模块完整实现"

reset --soft 不会丢代码,只是把 HEAD 指针回到起点,所有变更保留在暂存区。再 commit 一下,多个 commit 就变成了一个,干净利落。

第 6 步:推送临时分支到远程

bash 复制代码
git push origin merge-for-main1

第 7 步:合并到主分支

根据仓库配置,分两种情况:

情况 A:main1 没有保护(可以直接 push)

bash 复制代码
git checkout main1
git merge merge-for-main1 --ff-only
git push origin main1

情况 B:main1 有保护(需要走 合并请求 流程)

在 Git 网页上操作:

  1. 打开仓库 → Pull Requests → New Pull Request
  2. Source 选 merge-for-main1,Target 选 main1
  3. 合并方式选 Rebase and merge (推荐)或 Fast-forward merge
  4. 提交 合并请求,等审查通过

切记:不要选 "Create a merge commit",否则会产生分叉。

第 8 步:清理临时分支

bash 复制代码
git checkout main1
git branch -D merge-for-main1
git push origin --delete merge-for-main1

一张图看懂整个流程

复制代码
初始状态:
main1:            a---b---c---x---y
                      ↑
feat/user:            \---d---e---f---g

创建临时分支:
merge-for-main1:  x---y

cherry-pick d..f:
merge-for-main1:  x---y---d'---e'---f'

压缩成单 commit:
merge-for-main1:  x---y---S

Rebase and merge 后:
main1:            a---b---c---x---y---S (一条直线!)

冲突处理速查表

cherry-pick 过程中遇到冲突不用慌:

bash 复制代码
# 查看哪些文件冲突了
git status

# 手动解决冲突后
git add <冲突文件>

# 继续 cherry-pick
git cherry-pick --continue --no-edit

# 实在搞不定?中止操作
git cherry-pick --abort

合并后各分支状态

分支 最终状态
feat/user 完全不变,保持原有多个 commit,可继续开发
main1 一条直线,新增 1 个 commit
merge-for-main1 已删除(用完即弃)

快速参考命令

把下面这段存下来,下次直接用:

bash 复制代码
# 1. 查看 需要合并的commit hash
git log feat/user --oneline

# 2. 创建合并临时分支
git checkout main1  
git pull origin main1
git checkout -b merge-for-main1
$START_MAIN1 = git rev-parse HEAD
echo $START_MAIN1
git cherry-pick <开始>^..<结束>
git reset --soft $START
git commit -m "feat: xxx"
git push origin merge-for-main1

# 3. 合并到 main1
git merge merge-for-main1

# 4. 清理
git checkout main1 
git branch -D merge-for-main1
git push origin --delete merge-for-main1

写在最后

这套流程的核心思路是:用临时分支做缓冲,cherry-pick 精选代码,压缩成单个 commit 后合入主分支

这样既保证了主分支历史的干净整洁,又保留了功能分支的完整记录,还能精确控制合并范围。团队协作中统一这套规范,代码审查和版本追溯都会轻松很多。