通过git revert来实现线主干代码的回滚。如下命令

  • 对于 merge类型的commit对象,还需要“-m”参数

git revert -m 1  commit-id

  • 对于普通的commtit对象

git revert commit-id

1 问题描述

一个同事不小把自己代码合并 到了master生成C2。但是在该同事还没有回滚之前,又有其他同事合并到master生成了 C3。现在问题是我们想要回滚到C1应该怎么做?

2 解决问题

1、选择git revert还是git reset?

git revert是生成新的commit对象,而git reset是删除commit对象,为了保留记录,使用git revert命令。

2、解决

按时间顺序,依次回滚每一个commit对象,直到自己想要的那个commit对象为准。如下:

  • 回滚C3 ,c3-id是C3对应的那个commit-id

git revert -m  1  c3-id

  • 回滚C2。其中c2-id是C2对应的那个commit-id

git  revert  -m  1  c2-id

3、上面的命令为什么有“-m 1”?

这是因为上面的C2和C3不是普通的commit对象,都是merge生成的commit对象。如下图,如果需要通过git revert回滚M3,那么此时会在M3后面生成一个新的commit节点R,那么这个新节点R是属于M分支(M1->M2->M3->R)还是D分支(D1->D2->D3->R)呢?此时可以通过“-m” 来指定,如果是1,表示的是当前所在的分支,如果是2表示的是另外的分支。

对于上面的问题,由于我们当前分支是master,我们希望revert之后生成的commit也在master这条分支上,所以指定“-m 1”。

这里需要注意的是,如果只是普通的commit 对象,不是merge类型commit对象(由merge产生commit对象),就不需要”-m 1″了

3 问题总结

后续再遇到代码回滚,通过哪些步骤来做呢?这里总结了一些步骤。

1、假设在master上C0后面有C1、C2、C3三次代码提交,此时需要回滚到C0。

2、回滚步骤如下

(1)第一步  切到master代码,使用git log,如下图,获取到每一个commit对象对应的commit-id和commit对象类型(是普通类型还是merge类型)。

commit ee1389bba4bfcaa0ddb850c6e58d1e982fdfcb4d
  Merge: 8397201 7dc873c
  Author: jie01 <jie01@qq.com>
  Date: Thu Oct 12 20:27:33 2017 +0800
    Merge branch 'fweb_1-0-806_BRANCH' into master commit 2f315650dff28e94d654309ed3230c34d32f1000
  Author: shan03 <shan03@qq.com>
  Date: Tue Oct 17 14:33:16 2017 +0800 commit 7dc873cd34b8bba1fab68635ddf8331ab2babc74
  Merge: cbaf7d9 d24f133
  Author: xu01 <xu01@qq.com>
  Date: Thu Oct 12 15:59:13 2017 +0800
    Merge branch 'web_1-0-790_BRANCH' into financeweb_1-0-806_BRANCH

(2) 第二步  按时间倒序,依次执行”git revert”回滚每一个commit对象

  • 对于 merge类型的commit对象,需要“-m”参数

    git revert -m 1  commit-id

  • 对于普通的commtit对象

    git revert commit-id

所以回滚命令如下

  • 回滚C3,merge类型commit对象,需要-m

git revert  -m 1   ee1389bba4bfcaa0ddb850c6e58d1e982fdfcb4d

  • 回滚C2,普通commit对象

git revert  2f315650dff28e94d654309ed3230c34d32f1000

  • 回滚C1,merge类型commit对象,需要-m

git revert -m 1  7dc873cd34b8bba1fab68635ddf8331ab2babc74

后续问题

比如说当M3执行了git revet生成RM3,此时在master上又有其他人提交了M4,我们还需要在dev上进行开发D4和D5,如下图

此时怎么合并D5到master的M4

(1)第一步  master合并到D5

因为master执行了git revert撤销操作,所以此时D1和D2的代码会被删除?

  • 在master上执行git revert撤销 RM3
  • 将master合并到dev的D5

(2)第二步 将dev的合并到master

附1 其他方法-通过覆盖方法来实现回滚主干

步骤如下:

  • 第一步 分别拉两份代码。第一份代码上面基于master新建一个分支F1,作为发布分支;第二份代码可以通过git checkout切到自己想要回滚的那个版本上(切到某个commit对象上)。
  • 第二步 删除分支F1对应的代码,把第二份代码拷贝到F1上面,即使用第二份代码覆盖F1的代码。。
  • 第三步  提交F1代码,并合并到master。

附2 git revert和git reset区别

1、git revert 。只是撤销某一次commit的操作,并没有删除commit对象;并且会生成一个新的commit对象。

假设分支为m1->m2->m3。当我们”git revet m2″生成m4,此时在m4里面只是把m2的操作撤销了而已,并没有撤销m2和m3对象。此时分支为“m1->m2->m3->m4”

2、git reset。删除某一个commit之后所有commit提交对象;不会产生新的commit对象。

假设m1->m2->m3,当我们git reset m2,此时并没有生成新的commit对象,并且删除了m2和m3两个commit对象,此时分分支为”m1->m2″

撤销提交

Git 的 revert 命令可以用来撤销提交(commit),对于常规的提交来说,revert 命令十分直观易用,相当于做一次被 revert 的提交的「反操作」并形成一个新的 commit,但是当你需要撤销一个合并(merge)的时候,事情就变得稍微复杂了一些。

Merge Commit

在描述 merge commit 之前,先来简短地描述一下常规的 commit。每当你做了一批操作(增加、修改、或删除)之后,你执行 git commit 便会得到一个常规的 Commit。执行 git show 将会输出详细的增删情况。

Merge commit 则不是这样。每当你使用 git merge 合并两个分支,你将会得到一个新的 merge commit。执行 git show 之后,会有类似的输出:

commit 19b7d40d2ebefb4236a8ab630f89e4afca6e9dbe

Merge: b0ef24a cca45f9

......

其中,Merge 这一行代表的是这个合并 parents,它可以用来表明 merge 操作的线索。

举个例子,通常,我们的稳定代码都在 master 分支,而开发过程使用 dev 分支,当开发完成后,再把 dev 分支 merge 进 master 分支:

a -> b -> c -> f -- g -> h (master)
\ /
d -> e (dev)

上图中,g 是 merge commit,其他的都是常规 commit。g 的两个 parent 分别是 f 和 e。

Revert a Merge Commit

当你使用 git revert 撤销一个 merge commit 时,如果除了 commit 号而不加任何其他参数,git 将会提示错误:

git revert g

error: Commit g is a merge but no -m option was given.

fatal: revert failed

在你合并两个分支并试图撤销时,Git 并不知道你到底需要保留哪一个分支上所做的修改。从 Git 的角度来看,master 分支和 dev 在地位上是完全平等的,只是在 workflow 中,master 被人为约定成了「主分支」。

于是 Git 需要你通过 m 或 mainline 参数来指定「主线」。merge commit 的 parents 一定是在两个不同的线索上,因此可以通过 parent 来表示「主线」。m 参数的值可以是 1 或者 2,对应着 parent 在 merge commit 信息中的顺序。

以上面那张图为例,我们查看 commit g 的内容:

git show g
commit g
Merge: f e
那么,$ git revert -m 1 g 将会保留 master 分支上的修改,撤销 dev 分支上的修改。 撤销成功之后,Git 将会生成一个新的 Commit,提交历史就成了这样: a -> b -> c -> f -- g -> h -> G (master)
\ /
d -> e (dev)

其中 G 是撤销 g 生成的 commit。通过 $ git show G 之后,我们会发现 G 是一个常规提交,内容就是撤销 merge 时被丢弃的那条线索的所有 commit 的「反操作」的合集。

Recover a Reverted Merging

上面的提交历史在实践中通常对应着这样的情况:

工程师在 master 分支切出了 dev 分支编写新功能,开发完成后合并 dev 分支到 master 分支并上线。上线之后,发现了 dev 分支引入了严重的 bug,而其他人已经在最新的 master 上切出了新的分支并进行开发,所以不能简单地在 master 分支上通过重置(git reset )来回滚代码,只能选择 revert 那个 merge commit。

但是事情还没有结束。工程师必须切回 dev 分支修复那些 bug,于是提交记录变成了这个样子:

a -> b -> c -> f -- g -> h -> G -> i (master)
\ /
d -> e -> j -> k (dev)

工程师返回 dev 分支通过 j,k 两个 commit 修复了 bug,其他工程师在 master 上有了新的提交 i。现在到了 dev 分支的内容重新上线的时候了。

直觉上来说,还是和之前一样,把 dev 分支合并到 master 分支就好了。于是:

$ git checkout master
$ git merge dev
得到的提交记录变成了这样: a -> b -> c -> f -- g -> h -> G -> i -- m (master)
\ / /
d -> e -> j -> k --------- (dev)

m 是新的 merge commit。需要注意的是,这不能得到我们期望的结果。因为 d 和 e 两个提交曾经被丢弃过,如此合并到 master 的代码,并不会重新包含 d 和 e 两个提交的内容,相当于只有 dev 上的新 commit 被合并了进来,而 dev 分支之前的内容,依然是被 revert 掉了。

所以,如果想恢复整个 dev 所做的修改,应该:

$ git checkout master
$ git revert G
$ git merge dev
于是,提交历史变成了这样: a -> b -> c -> f -- g -> h -> G -> i -> G' -- m (master)
\ / /
d -> e -> j -> k --------------- (dev)

其中 G' 是这次 revert 操作生成的 commit,把之前撤销合并时丢弃的代码恢复了回来,然后再 merge dev 分支,把解 bug 写的新代码合并到 master 分支。

现在,工程师可以放心地上线没有 bug 的新功能了。

git 还原、恢复、回退的更多相关文章

  1. git(创建,提交,回退)

    创建版本库 2334次阅读 什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都能跟踪,以 ...

  2. Git(时光机-版本回退)

    现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control system. ...

  3. Git版本恢复命令reset(转载)

    本博文转载自:http://www.tech126.com/git-reset/: 如果看不懂的话,请在git下练习,如果练习后任然有不懂的,可以留言也可以发送邮件到luoquantao@126.co ...

  4. Git 之 恢复修改的文件

    对于恢复修改的文件,就是将文件从仓库中拉到本地工作区,即 仓库区 ----> 暂存区 ----> 工作区. 对于修改的文件有两种情况: 只是修改了文件,没有任何 git 操作 修改了文件, ...

  5. Git命令之回退篇 git revert git reset

    Git command之回退篇 欲练回退 必先了解:HEAD.index.WorkingCopy HEAD: 当前所在的分支版本顶端的别名,也就是最新的一次commit. git commit 之后与 ...

  6. Git学习--版本回退

    现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt文件如下: Git is a distributed version control system. ...

  7. Git 版本恢复命令reset

    reset命令有3中方式: git reset -mixed: 此为默认方式,不带任何参数的git reset, 使用这种方式,项目会回退到某个版本,只保留源码,回退commit和index的信息. ...

  8. git 如何恢复只是提交到本地的文件(或者commit)

    今天早上傻逼了,把四天的代码commit到了本地,然后fetch一下,然后就全没了,不过git还是挺强大的 参考:http://blog.163.com/jiams_wang/blog/static/ ...

  9. Git回版本回退

    这里我们使用命令行的方式对已经提交的版本进行强行回退操作~~~ 一.将git的安装目录bin放到path路径中, 如下图所示: 二.进入cmd界面,依次输入下面内容即可(git 远程仓库 回退到指定版 ...

  10. idea中git远程版本回退

    idea中git远程版本回退 2017年10月15日 15:25:36 gomeplus 阅读数:19313 工作中遇到git远程仓库需要回退到历史版本的问题,根据网上的搜索结果结合自己的实践,整理了 ...

随机推荐

  1. PCIeのType0与Type1型配置请求与BAR(基地址寄存器)

    PCIe中存在两种配置空间Type0&type1,TYPE0对应非桥设备(Endpoint),Type1对应桥设备(Root和Switch端口中的P2P桥)因为Root每个端口总都含有一个P2 ...

  2. webshell查杀

    大部分Webshell查杀工具都是基于关键字特征的,通常他们会维护一个关键字列表,以此遍历指定扩展名的文件来进行扫描,所以可能最先想到的是各种字符串变形,下面总结了一些小的方法,各种不足之前还请看官拍 ...

  3. js验证小数或者整数

    利用正则表达式校验是否为小数或者整数,废话不多说直接上demo(此正则表达式无法校验负数和数字为00开头的数字). PS:(如果有不对之处,请批评指教) <!DOCTYPE html> & ...

  4. <img> 标签的 src 属性

    src属性 加载的时候就会请求 1.servlet生成一个图片 2.你直接输入servlet的连接看一下,就是一个图片,和我们自己发布到服务器的一样. 3.页面加载时,会访问这个servelt连接,自 ...

  5. 用HTTP核心模块配置一个静态Web服务器

    静态Web服务器的主要功能由ngx_http_core_module模块(HTTP框架的主要成员)实现与core模块类似,可以根据相关模块(如ngx_http_gzip_filter_module.n ...

  6. python打包命令

    打包成exe方法 (1)切换到该文件夹 (2)pyinstaller -F py文件 (py文件要英文才行) -F 生成单个可执行文件 -w 去掉控制台窗口 -p 自定义需要加载的类路径 -i 可执行 ...

  7. RK3288之kernel目录结构以及功能

    :~/RK3288/kernel$ ls android include MAINTAINERS security arch init Makefile sound backported-featur ...

  8. VMware虚拟机NAT模式无法上外网

    VMware虚拟机NAT模式无法上外网排错思路 1,确保三种模式只有一种在连接 2,确保ip配置正确 配置的子网跟DHCP必须是同一网段 3,确保网关配置正确 网关不管怎么配,一定不要配192.168 ...

  9. 10年前文章_UC3A/B 开发环境设置

    大部分设置和 Z32U 交叉编译环境的配置 类似 Windows 环境 步骤二: 安装 toolchain 和mkII lite V2 的驱动 安装运行 avr32-gnu-toolchain-2.0 ...

  10. string初始化

    #include <iostream> using namespace std; int main(int argc, const char * argv[]) { //通过const c ...