版本回退问题,不同场景下,解决方案是不一样的。总的来说,涉及两个Git命令,即 git revert
和 git reset
,所以要想掌握版本回退,必须理解这两个命令到底做了什么。
1. git revert
与 git reset
的比较
理解这两个命令的区别,参看 git revert
与 git reset
的比较:https://blog.csdn.net/leonliu06/article/details/79747301。
2. 版本回退方案
开头提到版本回退要考虑不同场景,这些场景主要因素有:
- 你想回退到哪里、
- 当前版本与要回退到的版本之间已有多少变化(提交)、
- 本地提交是否已经推送到远程仓库等。
场景很多,但解决方案基本有两种方式:
2.1 git revert方式
//撤消某一个提交如c1(可以跟多个提交)
$ git revert c1
//然后再执行 `git push` 同步到远程仓库,达到回退的效果。
$ git push
这种方式需要注意一个问题,如果要回退到的提交与当前提交之间存在合并提交;
如 c1
是一个合并提交。因为合并提交有两个父提交,所以执行git revert
的时候必须指定回退到哪个版本上。
如当前分支是dev1
,c1
是将 dev2
合并到 dev1
,即命令 git merge dev2
产生的合并提交。
则执行 git revert c1
时,必须给定 -m
选项,选项后面可以是1
或 2
,这里 1
指的是 dev1
,2
指的是 dev2
。
$ git revert c1 -m 1
注意:
- 这样会丢失
dev2
上的所有提交。 - 另外,如果要回退的历史很长,期间可能会有很多合并提交以及伴随的解决冲突的提交,该种方式可能会损害团队其他开发人员的代码,这是另人仇恨的事情。
- 这种方式很难保证团队成员不会在要回退的历史中的提交基础上编写代码。
git revert
更准确的含义是撤消历史中的某次提交,它的历史是前进的,不是回退。
2.2 git reset方式
这种方式要考虑本地提交是否已经推送到远程仓库。
- 本地提交未推送到远程仓库
这种场景很简单,因为所有操作只影响你自己本地的仓库,不需要考虑对团队成员的影响。
所以直接重置到你想要的提交如 c1
就可以了,只需要考虑不同选项--soft
, --mixed
和 --hard
的影响。
$ git reset --hard c1
本地提交已推送到远程仓库
这就是远程仓库版本回退的问题了,这种场景下,可能团队成员都已经与远程仓库进行过了同步,所以如果只单纯地利用git reset
命令,无论如何操作,都不能完全保证远程仓库版本完美地回退到期望的结果。因为即使远程仓库
git reset
重置到了要回退的版本,但团队成员在执行git push
的时候,将会又把删除的提交推送到远程仓库中。即使删除远程仓库分支,但团队成员仓库保存了原来的远程分支引用,所以在执行
git push
的时候,将会又新建出来原来的远程分支。
所以,这种场景下,只能将本地仓库回退到想要的版本后,再按本地版本新建一个远程分支,这个远程分支的版本与本地仓库回退后的版本一致,然后团队成员在拉取这个新的远程分支,以达到远程仓库版本回退的效果。
如果要想使新的远程仓库版本分支名与原来一样,则只能删除远程分支,再新建一个同名分支后,并要求团队成员删除本地仓库分支,重新抓取!
设要回退远程仓库master
分支的版本,当前在master
分支下,这种解决方案的一个操作步骤如下:
// 本地新建old_master分支做备份
$ git branch old_master
// 将备份分支push到远程仓库
$ git push origin old_master:old_master
// 本地仓库回退到某个版本如 bae168
$ git reset –-hard bae168
// 删除远程仓库的master分支
$ git push origin :master
// 重新创建远程仓库master分支
git push origin master
最后,要求所有成员删除本地仓库master
分支,重新抓取避免执行 git push
后,又将远程版本更新。
3. 总结
首先应当尽量避免出现需要回退代码版本的情况,如果不得不回退版本,则建议使用第二种,即 2.2 git reset 方案,这种方案,干脆,利落,明确地知道自己在做什么,且需要鲜明地周知团队我们需要回退版本!当然,如果只是在本地仓库(未同步到远程仓库)反悔,那就是本地仓库自己的事情了。