关于git merge -s ours xxx 的使用
需求背景:开发分支(feat1、feat2、....featn),测试分支(dev),预生产分支(release),上线分支(master)。协作过程是,项目一个大版本上线从release合并到master,上线后反合dev。下一个大版本初始大家都从release分支拉取feat,提测后所有feat都合并到dev,有不规范的操作会从dev直接提交代码,测试完成从dev合并到release。但是在测试的过程中,feat1出现了需求变更,不跟随本次上线了。
解决方案
使用git merge -s ours xxx
这条命令是 Git 合并操作 的一种特殊用法,核心是用当前分支的内容覆盖掉要合并的分支内容
假设你当前在 release 分支,执行 git merge -s ours feat:
- Git 会创建一个新的合并提交(merge commit);
- 这个合并提交的内容和当前
dev分支完全一致,origin/feat分支的所有修改都不会被引入; - Git 的分支历史中会显示
release已经合并过origin/feat,后续如果再执行常规合并,Git 会认为该合并的都合过了,不会再处理这个分支的历史修改。
如上操作之后,如3所说,再次合并feat到release分支,会显示:Already up to date.
但是,我们的需求是feat分支的内容只是当前版本不上线了,但是后续的版本还是会上线的,这并不满足我们的需求。 所以,我们可以,先merge再reset再cherry-pick
- 切换到
release分支,执行git merge -s ours feat=> 得到一个没有内容变更的merge - 在
release分支上,执行git merge origin dev=> 得到一个没有feat分支变更内容的dev分支到release分支的merge,同时我们记下这个commitId - 将
release分支reset到执行git merge -s ours feat之前的那个提交 => 通过git reset xxx --hard - 然后将刚才2记下的commitId通过
cherry-pcik拿过来 => 使用git cherry-pick xxx -m 1(此处的xxx是个mergeId) - 推送到远端
如上所述,即可实现把dev合并到release且不包含feat分支的改动,并且后续也不会影响feat分支往release分支的合并。但是,对release分支进行reset --hard并不符合常规的要求,对于很多公司来讲,release分支应该是受保护的分支,不应该被随便reset。所以,我们可以从release分支上先切出一个新分支,在新分支上进行上述操作,操作完成之后,再把新分支合并到release分支上。
关于其它解决方案的考虑
- 不使用dev合并到release,而是用feat1、feat2...featn逐一合并到release。
- 找到
dev分支上所有非 feat 产生的提交,逐个挑选(cherry-pick)到release分支。 - 在
dev上使用git rebase -i剔除feat的提交。 对于方案1,在实际开发过程中,每个feat分支可能就对应一个开发者,逐一合并可能会涉及到很多人来处理,而且比如在测试过程中有些非常规的操作(在dev上直接提交一些代码),那这部分的提交就会被忽略掉。对于方案2、3,变更比较少的情况下是适用的,但是变更比较多的情况下逐一排查commit也不现实。
思考
一个未解决的问题留给大家:在release分支使用git merge -s ours feat之后,如何在不reset的情况下,可以让feat分支正常合并release呢?