美文网首页
Git-少年,你想学回滚吗?想撤销文件修改吗?

Git-少年,你想学回滚吗?想撤销文件修改吗?

作者: 段浅浅儿 | 来源:发表于2018-07-06 12:11 被阅读3次

    写在前面

    林俊杰有首歌《可惜没如果》,道尽后悔的遗憾,但是万幸,在 Git 中你可以拥有如果,用 resetcheckoutrevert 可以用来撤销当年那些错误的决定。

    带着 Git 三大区的概念去阅读

    来,看下面这张图复习复习 Git 三大区的概念,这个概念即将贯穿今天这篇文章,理解很重要,不太理解的小伙伴可以先去我的另一篇博客 《 Git三大特色之Stage(暂存区)》学习下先,等你哦。

    git 数据流程图示意图

    吃第一颗栗子,理解一下什么叫做回滚

    有一个Android 开发工程师,他叫小明,他正在 feature 分支上开发,他的 feature 分支历史如下图所示,一切都很正常,按照计划进行,这时他接到一个需求变更通知,说“用户积分模块,这个版本不要了”,这真是一个悲伤的消息,小明要赶紧改回来。

    image

    如果小明不会回滚历史,需要人肉操作去找有关 “用户积分模块” 的代码,然后一个个注或删除释掉,这个更悲伤了,我猜小明可能会哭。

    image image

    如果小明是个老手,他会先新建个分支做备份,然后再回滚,那也只需要两条命令:

    image

    以上完美解决此时的需求变更,小明很开心。

    image

    拿起你的栗子,来认识 reset 的三种模式

    上面的例子中,我们使用的命令是 git reset --hard <commit-id>,其实 reset 有三种模式:

    • --hard
    • --mixed(默认模式)
    • --soft

    三个参数对应的是不同的作用范围,这个范围和开篇说的 Git 三大区息息相关,因为这个三个模式就是因为有三个分区才存在的。

    image

    实际使用中,你可能会遇到各种千奇百怪需求的的撤销操作,理解好这三种模式的区别,使用起来倍爽。下面列两三个你肯定会遇到的场景,然后用 reset 的不同模式。可以轻松解决问题。

    小明写代码ing;
    同事说“小明,你快拉取一下仓库的最新代码,blabla...” ;
    小明开始“git pull” ;
    git pull failed ,原因是暂存区有未提交的修改,这些修改可能会被覆盖;
    小明确定不会被覆盖,不想创建一个新提交,还想保存修改内容 ;
    请问:该怎么办?

    //将暂存区的内容与提交历史同步
    git reset
    
    //然后再拉取                               
    git pull xxx  
    
    

    小明刚创建了一个 commit;
    然后发现那个 commit 中有个小错误;
    但是为了维护美好的分支历史,不想因为这个小修改再创建一个提交;
    请问:该怎么办?

    //将此提交撤销,但是不修改暂存区和工作区的内容
     git reset --soft HEAD^  
    
    //修改好小问题
    
    //重新提交                      
    git add .
    git commit -m 'xxx'
    
    

    其实,这是使用 --soft的解决方案,还有一种更方便的方法,不用撤销也能完成,git commit --amend追加提交:

    
    //修改好小问题
    
    //重新提交                      
    git add .
    git commit --amend`
    
    

    上面的例子,希望给帮你更加清楚的理解 reset 三种的模式的区别,在使用 reset --hard的时候,要小心一点,因为这个操作会彻底地重置工作区,放弃修改,使用之前,要十分确定未保存的修改要放弃,不然重置后有可能找不回来了哈。

    下面给小伙伴们介绍另外一种撤销方式 revert,是一个神奇的命令。

    revert 是一种抵消式撤销

    revert,翻译是“反转”,一个比较难理解的词,快来解开非凡想象力的封印。

    想象一下,提交中添加了一个A.txt, revert 创建了一个删除 A.txt 的新提交,两个提交同时存在,于是,A.txt 等于从来没有存在过,等于撤销了原来提交。

    这像一个互为正负的关系 1+(-1)= 0

    第一个例子,小明如果用 revert “撤销积分模块”,就要这样用了

    //将 head^^^到head 范围内的提交反转
    git revert head^^^..head
    
    
    image

    git log 看一下操作后的分支历史,不仅没少,反而多了,增加几个反转提交。

    image

    这是 revert 第一个例子中场景的使用表现,也许你会疑问:

    有了 reset,为什么还要有 revert ?

    这是因为,在下图情况下,分支顶端还有要保存的提交,reset 无法使用,只能用 revert 将中间的提交反转。

    image

    如果不想要新增的反转提交污染分支历史,你也可以选择发射你的黑魔法 rebase -i 将中间的提交施加 drop 操作,Git 真的无所不能,只要你会。

    reset VS revert ,Ready? GO!

    由于 resetrevert 的作用都是撤销提交,所以很多人很自然而然地将两者对比。我就是那很多人之一,而且我还特别无聊的,画了对比图,如下:

    image

    可以很明显的看出两者对分支历史改变区别:

    • reset 回退了历史
    • revert 创建反转提交,增加了历史

    从数据安全上角度,revertreset安全,因为它的操作可以回溯,反转了还可以倒回来。reset比较彻底,是直接丢弃了,不过可以考虑想第一个例子中创建一个备份分支来保证安全。

    从分支历史的长期维护角度,reset的历史比较干净,revert的反转提交没多大意义,毕竟很少有需求让你滚来滚去的。

    在被撤销提交,不再分支顶端的场景上,reset 无法使用,revert可以做到,。

    以上对两者的区别讨论结束,没有总结,看你个人喜好和需求场景使用。

    撤销的不是提交,而是文件

    上面给大家介绍的都是关于 commit history 提交历史的撤销,但是有时候会出现撤销单个文件修改的场景,例如,我就经常写了很多代码之后发现某个想法不可行,那个时候已经写了很多代码了,Command+Z都撤不完了,手动改太麻烦还容易出错,怎么办?

    这个时候就需要撤销文件的修改了,下面就给大家介绍一下,如何进行文件撤销。

    checkout 的第二命令格

    checkout 这个命令,我们对它太熟悉了,切换分支,检出提交,检出 Tag 标签等等各种对 HEAD 指针花式操作,但是看下,git-checkout 官网文档的定义:

    image

    是不是很惊讶,还可以被用来还原工作区的文件!

    其实这个命令就近在我们眼前,运行 git status,会看到工作区和暂存区的修改,如果工作区有修改存在,Git 就会提示,可以通过 git add <file> 来加入暂存区以备下次提交,如果放弃修改,就执行 git checkout -- <file>

    git status中的 checkout

    所以,要是哪次想撤回修改,恢复到上次 add 时内容 ,除了手动 Command+ZCtrl+Z ,还可以是使用 git checkout -- <file>,改动较多时,简直一步到位!

    reset 的新操作

    接着上面,工作区1.txt的修改我想下次提交,于是执行了 git add . 加入了暂存区,这个时候运行 git status,又发现 reset 命令的新操作 git reset HEAD <file>

    git status 中的 reset

    看注释,这个操作可以将某个文件的修改从暂存区拿出去。在错误 add 不想提交的文件时,这个命令还是很有用的。

    写在后面

    好了,以上是本博客的全部内容,希望能帮助大家理解和学会提交历史的撤销和文件文修改的撤销。

    下篇博客会讲一个特别治愈的命令 reflog,和 reset 配合可以被用来恢复数据,包括已经撤销的,敬请期待!

    see you next blog!


    欢迎关注博主的微信公众号,快快加入哦,期待与你一起成长!

    相关文章

      网友评论

          本文标题:Git-少年,你想学回滚吗?想撤销文件修改吗?

          本文链接:https://www.haomeiwen.com/subject/tgvvuftx.html