Git如何回滚一次错误的合并

栏目: 编程工具 · 发布时间: 5年前

内容简介:原文发表在知乎专栏前端杂货铺, 欢迎关注我的专栏,转载请注明出处今天不说前端,来聊聊git吧。 发现现在的小孩,玩框架一套一套的,等到玩点实质的工程化的东西就不行了。 git 这么好的工具,培训班怎么可以忽视他的重要性呢?很多人对

原文发表在知乎专栏前端杂货铺, 欢迎关注我的专栏,转载请注明出处

今天不说前端,来聊聊git吧。 发现现在的小孩,玩框架一套一套的,等到玩点实质的工程化的东西就不行了。 git 这么好的工具,培训班怎么可以忽视他的重要性呢?

再来聊聊git的工作流程

很多人对 Git究竟是一个怎样的系统 ,还是一知半解。 在这里强烈建议大家先理解git的核心思想和工作原理,有过 subversion 或者 perforce 使用经验的人更是需要摒弃之前所见所学,重新接受这样一个新思想。 我们不再这里赘述其几本原理,我们来介绍一下其简单工作流程。 Git以一个自有的思维框架管理着三个不同的盒子 Commit HistoryINDEXWorking Directory

Commit History
INDEX
Working Directory

理解了这三者的含义后,我们试着来理解一下git的工作流程。 一切的开始,混沌之间,我们要干一件大事,在terminal里面敲打了几下键盘

git init 
复制代码

混沌初开,幻化三界: HEADINDEXWorking Directory 。这就是世界最开始的样子git仓库仿佛就是掌管三界之神。而 Working Directory 就是他分配给你生产和工作的地方,你可以在这里肆意的创造。而为了安全和管理的有序我们需要把我们的添加与修改的文件交给git仓库。Git首先会将修改的文件标记起来放入暂存区、然后git找到暂存区域的文件内容将其永久性的存储为快照到git仓库,此时 HEAD 的指针指向这个最新的快照。

Git如何回滚一次错误的合并

如图,总结下三个步骤

git add
git commit

git 的基本工作流程就是在不断的重复这三个步骤,最终git仓库目录形成了一个快照堆栈,每产生一次新的版本,HEAD就会指向这个版本。

这里我们创建了下面这些文件:

├── README.md
├── v1.js
├── v2.js
└── v3.js

复制代码

形成了下图的提交历史

3aa5dfb v3  (<- HEAD)
        |
5aab391 v2
        |
ff7b88e v1
        |
95d7816 init commit

复制代码

下面我们来看看怎么利用checkout、reset、revert 来操作这个仓库目录

checkout 、reset 还是 revert ?

checkout

版本控制系统背后的思想就是「安全」地储存项目的拷贝,这样你永远不用担心什么时候不可复原地破坏了你的代码库。当你建立了项目历史之后,git checkout 是一种便捷的方式,来将保存的快照「解包」到你的工作目录上去。 git checkout 可以检出提交、也可以检出单个文件甚至还可以检出分支(此处省略)。

git checkout 5aab391
复制代码

检出v2,当前工作目录和 5aab391 完全一致,你可以查看这个版本的文件编辑、运行、测试都不会被保存到git仓库里面。你可以 git checkout master 或者 git checkout - 回到原来的工作状态上来。

git checkout 5aab391 v1.js
复制代码

以检出v2版本对于v1.js的改动,只针对v1.js这个文件检出到 5aab391 版本。所以 它会影响你当前的工作状态,它会把当前状态的v1.js文件内容覆盖为 5aab391 版本。所以除非你清楚你在做什么,最好不要轻易的做这个操作。但这个操作对于舍弃我当前的所有改动很有用:比如当前我在v1.js上面做了一些改动,但我又不想要这些改动了,而我又不想一个个去还原,那么我可以 git checkout HEAD v1.js 或者 git checkout -- v1.js

reset 重置

git checkout 一样, git reset 有很多用法。

git reset <file>
复制代码

从暂存区移除特定文件,但不改变工作目录。它会取消这个文件的缓存,而不覆盖任何更改。

git reset
复制代码

重置暂存区,匹配最近的一次提交,但工作目录不变。它会取消所有文件的暂存,而不会覆盖任何修改,给你了一个重设暂存快照的机会。

git reset --hard
复制代码

加上 --hard 标记后会告诉git要重置缓存区和工作目录的更改,就是说:先将你的暂存区清除掉,然后将你所有未暂存的更改都清除掉,所以在使用前确定你想扔掉所有的本地工作。

git reset <commit>
复制代码

将当前分支的指针HEAD移到 ,将缓存区重设到这个提交,但不改变工作目录。所有 之后的更改会保留在工作目录中,这允许你用更干净、原子性的快照重新提交项目历史。

git reset --hard <commit>
复制代码

将当前分支的指针HEAD移到 ,将缓存区和工作目录都重设到这个提交。它不仅清除了未提交的更改,同时还清除了 之后的所有提交。

可以看出, git reset 通过取消缓存或者取消一系列提交的操作会摒弃一些你当前工作目录上的更改,这样的操作带有一定的危险性。下面我们开始介绍一种相对稳妥的方式 revert

revert 撤销

git revert 被用来撤销一个已经提交的快照。但实现上和reset是完全不同的。通过搞清楚如何撤销这个提交引入的更改,然后在最后加上一个撤销了更改的 新 提交,而不是从项目历史中移除这个提交。

git revert <commit>
复制代码

生成一个撤消了 引入的修改的新提交,然后应用到当前分支。

Git如何回滚一次错误的合并

例如:

81f734d commit after bug
        |
3a395af bug
        |
3aa5dfb v3  (<- HEAD)
        |
5aab391 v2
        |
ff7b88e v1
        |
95d7816 init commit

复制代码

我们在 3a395af 引入了一个bug,我们明确是由于 3a395af 造成的bug的时候,以其我们通过新的提交来fix这个bug,不如 git revert , 让他来帮你剔除这个bug。

git revert 3a395af
复制代码

得到结果

cfb71fc Revert "bug"
        |
81f734d commit after bug
        |
3a395af bug
        |
3aa5dfb v3  (<- HEAD)
        |
5aab391 v2
        |
ff7b88e v1
        |
95d7816 init commit

复制代码

这个时候bug的改动被撤销了,产生了一个新的commit,但是 commit after bug 没有被清初。

所以相较于 resetrevert 不会改变项目历史,对那些已经发布到共享仓库的提交来说这是一个安全的操作。其次 git revert 可以将提交历史中的任何一个提交撤销、而 reset 会把历史上某个提交及之后所有的提交都移除掉,这太野蛮了。

另外 revert 的设计,还有一个考量,那就是撤销一个公共仓库的提交。至于为什么不能用 reset ,你们可以自己思考一下。 下面我们就用一个麻烦事(回滚一个错误的合并),来讲解这个操作。

合并操作

相对于常规的 commit ,当使用 git merge <branch> 合并两个分支的时候,你会得到一个新的 merge commit . 当我们 git show <commit> 的时候会出现类似信息:

commit 6dd0e2b9398ca8cd12bfd1faa1531d86dc41021a
Merge: d24d3b4 11a7112
Author: 前端杂货铺 
...............
复制代码

Merge: d24d3b4 11a7112 这行表明了两个分支在合并时,所处的parent的版本线索。

比如在上述项目中我们开出了一个dev分支并做了一些操作,现在分支的样子变成了这样:

init -> v1 -> v2 -> v3  (master)
           \      
            d1 -> d2  (dev)
复制代码

当我们在dev开发的差不多了

#git:(dev)
git checkout master 
#git:(master)
git merge dev
复制代码

这个时候形成了一个Merge Commit faulty merge

init -> v1 -> v2 -> v3 -- faulty merge  (master)
           \            /
            d1  -->  d2  (dev)
复制代码

此时 faulty merge 有两个parent 分别是v3 和 d2。

回滚错误的合并

这个merge之后还继续在dev开发,另一波人也在从别的分支往master合并代码。变成这样:

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 (master)
        \  \            /                     /
         \  d1  -->  d2  --> d3 --> d4  (dev)/
          \                                 / 
           c1  -->  c2 -------------------c3 (other)
复制代码

这个时候你发现, 妈也上次那个merge 好像给共享分支master引入了一个bug。这个bug导致团队其他同学跑不通测试,或者这是一个线上的bug,如果不及时修复老板要骂街了。

这个时候第一想到的肯定是回滚代码,但怎么回滚呢。用 reset ?不现实,因为太流氓不说,还会把别人的代码也干掉,所以只能用 revert 。而 revert 它最初被设计出来就是干这个活的。

怎么操作呢?首先想到的是上面所说的 git revert <commit> ,但是貌似不太行。

git revert faulty merge
error: Commit faulty merge is a merge but no -m option was given.
fatal: revert failed
复制代码

这是因为试图撤销两个分支的合并的时候Git不知道要保留哪一个分支上的修改。所以我们需要告诉git我们保留那个分支 m 或者 mainline .

git revert -m 1 faulty merge
复制代码

-m 后面带的参数值 可以是1或者2,对应着parent的顺序.上面列子:1代表 v3 ,2代表 d2 所以该操作会保留master分支的修改,而撤销dev分支合并过来的修改。

提交历史变为

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 (master)
          \            /                     
           d1  -->  d2  --> d3 --> d4  (dev)
复制代码

此处 rev3 是一个常规commit,其内容包含了之前在 faulty merge 撤销掉的dev合并过来的commit的【反操作】的合集。

到这个时候还没完,我们要记住,因为我们抛弃过之前dev合并过来的commit,下次dev再往master合并,之前抛弃过的其实是不包含在里面的。那怎么办呢?

恢复之前的回滚

很简单我们把之前master那个带有【反操作】的commit给撤销掉不就好了?

git checkout master
git revert rev3
git merge dev
复制代码

此时提交历史变成了

init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 -> rev3` -> final merge (master)
          \            /                                               /
           d1  -->  d2  --> d3 --> d4  --------------------------------(dev)
复制代码

以上所述就是小编给大家介绍的《Git如何回滚一次错误的合并》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

设计沟通十器

设计沟通十器

Daniel M. Brown / 樊旺斌 / 机械工业出版社 / 2008-12 / 49.00元

本书提供了网站设计时所需的可交付文档资料包括:概念模型,站点地图,可用性报告等,这些文档资料是设计人员和客户进行交流的主要工具。本书深入讨论了文档推介和风险规避技巧,向你展示了如何将文档资料按要求制作成有效的交流工具。 本书内容全面,结构清晰,讲解详细。可作为网站设计人员的参考用书。 关于网站设计的多数讨论好像都着眼于流程的创建,然而,要想把概念变为现实,需要一整套强大的可交付文档资料......一起来看看 《设计沟通十器》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具