Git进阶(十九):git revert 导致 merge 代码丢失问题修复
文章目录
- 一、前言
- 二、问题分析
- 三、解决方案
- 3.1 方案一:官方推荐方法
- 3.2 方案二:reset 方法
- 3.3 应用git cherry-pick commit-version
- 3.3.1 cherry-pick 应用示例
- 3.4 git reset 和git revert的区别
- 四、拓展阅读
一、前言
项目开发阶段使用分支是feature_xxx,将feature_xxx分支merge
到master之后,这时发现刚代码有bug,需要回版本回退,点击revert
按钮。
将本地的原分支feature_xxx代码修改后提交到远程分支,发起 Merge 到 Master 的申请会发现,feature_xxx分支和 master 分支的差异 commit 只有 feature_xxx 分支 revert 后的提交记录,之前的代码变动都没有了。
二、问题分析
对于master而言,已经合并过feature_xxx,master包含feature_xxx的commitid
,这些提交不会参与diff
,所以不会显示出两个分支的不同。
revert
操作实际是只是进行了一次逆向 commit
,将 merge
的代码进行回滚,但是 commit
的记录还存在。也就是说,feature_xxx 上面存在的待提交代码,其实已经是 master 的过去代码,属于已提交过的状态,所以不会显示 different。
三、解决方案
3.1 方案一:官方推荐方法
该核心思想就是:对 revert
的那次提交记录再次 revert
。这个操作能够将本要提交的代码,放置到最新的HEAD
,其commitid
要比master高,所以会重新diff
。
首先,切换到 master 分支,并基于 master 分支拉出一个分支 revert_tmp。作为 master 的副本,revert_tmp 的作用就是保存 revert 的提交记录;
git checkout master
git checkout -b revert_tmp
其次,在 master 分支上找到 revert 的那条提交记录的版本号,回滚至之前的版本(版本号可以通过“git log
”命令,或者从网页端查看);
git log # 查询<版本号>,格式,如:f2c3b544166eec612ea6814d6cd19aeef46824f8
git revert <版本号>
然后,切换到 feature_xxx 分支上,将 revert_tmp 这个分支 merge 到 feature_xxx 分支上。
git checkout feature_xxx
git merge revert_tmp
git push -f
最后,在 feature_xxx 重新提交对 master 的 merge 申请,会发现 revert 之前的代码都回来了。
3.2 方案二:reset 方法
与 revert
不同,采用 git reset
将 head 向后移动到上一次 merge
前的 commit 版本,会丢弃所有的 merge commit
记录(revert
不会丢弃,是逆向 commit
),所以,再次合并不会出现记录不显示或者冲突的问题。
git reset HEAD^ # 回退所有内容到上一个版本
git reset HEAD^^ # 回退所有内容到上上个版本
注意⚠️:谨慎使用 git reset –hard
参数,它会删除回退点之前的所有信息!
HEAD 说明:
3.3 应用git cherry-pick commit-version
cherry-pick
可以把其它分支上的commit
一个个抽离出来,合并到指定分支上。用法如下:
$ git cherry-pick <commitHash>
针对以上问题,首先找到revert
前的commitid,应用git cherry-pick commit-version
把commit
提交一个个找回。
如果在cherry-pick
过程中出现了冲突 ,可以使用git cherry-pick --continue
跳过此版本;如果想中途退出cherry-pcik
,可以使用git cherry-pick --abort
命令,执行完cherry-pick
后可以使用git cherry-pick --quit
命令。
3.3.1 cherry-pick 应用示例
举例来说,代码仓库有master和feature两个分支。
现在将提交f应用到master分支。
# 切换到 master 分支
$ git checkout master# Cherry pick 操作
$ git cherry-pick f
上面的操作完成以后,代码库就变成了下面的样子。
从上面可以看到,master分支的末尾增加了一个提交f。
git cherry-pick
命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
$ git cherry-pick feature
上面代码表示将feature分支的最近一次提交,转移到当前分支。
Cherry pick 支持一次转移多个提交。
$ git cherry-pick <HashA> <HashB>
上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。
如果想要转移一系列的连续提交,可以使用下面的简便语法。
$ git cherry-pick A..B
上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。
注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。
$ git cherry-pick A^..B
git cherry-pick
命令的常用配置项如下。
-e
,--edit
打开外部编辑器,编辑提交信息。
-n
,--no-commit
只更新工作区和暂存区,不产生新的提交。
-x
在提交信息的末尾追加一行(cherry picked from commit …),方便以后查到这个提交是如何产生的。
-s
,--signoff
在提交信息的末尾追加一行操作者的签名,表示是谁进行了这个操作。
-m parent-number
,--mainline parent-number
如果原始提交是一个合并节点,来自于两个分支的合并,那么 Cherry pick 默认将失败,因为它不知道应该采用哪个分支的代码变动。
-m
配置项告诉 Git,应该采用哪个分支的变动。它的参数parent-number是一个从1开始的整数,代表原始提交的父分支编号。
$ git cherry-pick -m 1 <commitHash>
上面命令表示,Cherry pick
采用提交commitHash来自编号1的父分支的变动。
一般来说,1号父分支是接受变动的分支(the branch being merged into),2号父分支是作为变动来源的分支(the branch being merged from)。
3.4 git reset 和git revert的区别
git reset
是回退到某个版本之前,在这个版本后提交的代码都没有了,git revert
是单纯的撤销某次操作,不影响后续的提交记录(但是如果是要撤销merge
操作,请慎用此命令,因为有可能出现无法再次merge
的情况,如果是撤销常规的commit
提交,可以使用)。
四、拓展阅读
- 《Git进阶(二):git revert 用法》
- 《Git进阶(十二):git merge 用法详解》
- 《Git进阶(十八):git rebase详解》