美文网首页
Revert的奇妙技巧

Revert的奇妙技巧

作者: 丿灬尘埃 | 来源:发表于2021-01-06 11:35 被阅读0次

    21年了,不来篇文章,都对不起新的一年。
    正常流程,先来个3连问。【请读完这个文章,绝对有你想要的】
    首先来了解下revert是干嘛用的?revert的用法都有神马? revert之后的代码如何再次使用?

    1. 了解revert

    git revert 撤销 某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销
    作为一次最新的提交
    新增一個 Commit 來反轉(或說取消)另一個 Commit 的內容,原本的 Commit 依舊還是會保留在历史记录中。虽然会因此而增加 Commit 數,但通常比较適用於已经推出去的 Commit,或是不允許使用 Reset 或 Rebase 之修改历史记录的指令的場合。
    会记录曾经的提交历史

    2.用法

    2.1 简单的回滚策略

    • git revert HEAD 撤销前一次 commit
    • git revert HEAD^ 撤销前前一次 commit
    • git revert commit (比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff)撤销指定的版本,撤销也会作为一次提交进行保存。
      git revert是提交一个新的版本,将需要revert的版本的内容再反向修改回去,
      版本会递增,不影响之前提交的内容【可以理解为撤销某次合并后,重新生成了一个版本(commit hash)】

    2.2 revert 其他用法

    git revert [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>…
    git revert --continue
    git revert --quit
    git revert --abort
    

    说明:放弃一个或多个提交,并生成一个或多个新的提交来记录这些放弃操作。

    git revert [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>…
     1. --edit or --no-edit是否弹出commit message窗口
     2.  -n    是 --no-commit的缩写
     3. -m parent-number 存在merge是,指定父系分支号。
    

    举个栗子:

    案例一:
    主线: A -> B -> C -> D
    目前HEAD 指向 D,如果想回滚到C的状态
    git revert D
    说明: 直接回滚即可,此时会生成一个新的commit 号,那么此时索引是:A->B->C->D->CV1

    案例二:
    如果是单个主线可以这么解决,如果多个分支协同开发呢
    主线:【借用下网图吧】


    image.png

    场景:

    1. dev1 提交了C1,C2, C3 ,C4 然后merge了master 生成了m5
    2. dev2 提交了C3,[应该是B3,dev2分支那个线],然后和合并了master 生成了m6
      问题: 此时dev1 上有bug ,需要回滚怎么做呢
      方案1:
      直接在master上进行reset,然后 -f 提交完美解决。
      这种方案可以,不过不建议用,首先master上一般不会让你-f, 而且master也尽可能的保持最新,最干净的代码

    方案二
    本节主要的git revert 来拯救世界了【用好了可以造世界,用不好可要受罚的哦】

    step1 : git revert commitHash
    这里呢,可以加一个参数 -m
    -m 后面还需要加一个整数,且只能指定1或者2,否则会报错

    commit 893247dca92dae929cf6255ad5985df25dbcfdac is a merge but no -m option was given.
    fatal: revert failed
    

    -m 标记指出 “mainline” 需要被保留下来的父结点。 为什么要写1或者2 呢,不是直接反向执行下这个commit进行的修改吗,commit加一行revert就减一行吗?为什么还要选1还是2模式?

    回答:【谷歌是个好东西,大佬的解析很明白,这段说明原文:https://www.cnblogs.com/bellkosmos/p/11409904.html
    git并不是基于diff进行管理的(有这样的版本管理系统),git的每个commit都是一个当前版本的快照,简单说每个commit都是一个完整的仓库版本,所以当你需要revert某个commit的时候,GIT需要知道你到底是希望revert哪个commit与这个commit间的改动
    不过其实并没有那么复杂,你要revert一个commit,就是revert掉这个commit和它上个commit间的改动,所以大部分时候,你直接revert就好了,不用指定-m参数

    不过当你要revert的的commit的上面有两个commit节点的时候,问题就来了

    A -> B -> 
                E -> F
    C -> D ->
    

    比如这里的E节点,它是AC两个分支合并的节点,这里假设是你在A分支使用命令merge C,那么E就有两个上游节点了,当你在新的分支F(其实就是之前的A分支)revert E 时,你就需要加上-m参数了,当你指定1时,就是revert 掉 B到E的改动,当你指定2时,你也可以revert 掉 D到E的改动,其实大部分时候我们都是选1就好了~

    3. revert 掉的代码,如果想继续用怎么办呢

    来个主线任务

    image.png

    step1: 按照前文这个log, revert了m5,
    stop2: dev2 的c3 merge 到了master生成了新的m6

    当你认为是bug回滚完之后, 某位产品大大说,那个不是bug, 是我们的新方案,你有没有看需求文档,
    你仔细看了下需求文档,一排脑门,对啊,这不是bug,那么少年想既然没问题,那么我在合并一次master就可以完美上线了,然后你开开心心的进行merge 代码了

    git checkout dev1  # 查看分支,很完美,代码都还在,很是庆幸
    
    git merge dev1 # 继续十分完美,没有冲突,但是在看一眼代码,dev1代码的改动被master给冲掉了,dev1代码没了
    

    哦,no,怎么办?
    百度吧,百度的方案其实类似我第二个模块方案所介绍的,回到master 上进行再次revert,

    1. 回到gitlab 上页面revert,兄弟你此时可是在master上操作啊,小心腿打折啊。
      那么如果我在 master 新开分支 test_2021 , 在当时那个rever的分支上进行revert 然后在和test_2021 进行merge 是否可以呢?
      如果你幸运是没是的,不过你很不幸,在那次revert之后有过新的提交
      那么你会很惊喜的收到如下报错
     Sorry, we cannot revert this merge request automatically. This merge request may already have been
    reverted, or a more recent commit may have updated some of its content.
    
    1. 命令执行,命令行执行有什么区别吗?

    说明,如果你无法在master的当前HEAD指向下进行revert 到当时的c4点,那么你是无法在git上在此使用dev1代码的,甚至你把dev1的代码随便往任意一个基于master新建的分支,都是无法把dev1的改动弄过去的。因为git认为你已经提交过了。

    现在来介绍一个中重要的方式
    step1:
    git checkout revert-commitHash
    切换分支到当时revert出来的新版本上【可以先回到master,然后 git pull 就可以down下所有的branch 了】

    step2:
    假如你这个项目叫做 fileProject [项目文件夹],cp出一个新的副本

    cp -r  fileProject  fileProjectV1
    cd fileProjectV1
    git checkout -b feature_revert
    

    此时fileProjectV1 项目只是fileProject 一个副本,代码,git仓库信息都一致

    step3:
    第一个小重点来了
    将fileProjectV1 文件夹中,曾经涉及dev1代码改动的文件删掉,当然如果文件很少,如果很多,不想分辨了,
    像楼主一样,找到文件所在的文件夹,把文件夹彻底干掉,[主要不是改那个删那个]

    cd fileProjectV1
    rm -rf  fileirectory
    

    删完世界都干净了,有没有

    step4:
    打开fileProject,切换分支到 dev1及revert 玩,还想要继续用的

    cd fileProject
    git checkout dev1
    

    step5:
    注意了啊,第二个重点来了
    将你在dev1 分支上,当时修改的文件夹或者理解为在 step3 删除了那些文件,就把那些文件复制下来
    复制,复制完cp到fileProjectV1中
    不要cp git 相关文件
    不要cp git 相关文件
    不要cp git 相关文件

    cp  fileProject_fileirectory  fileProjectV1_fileirectory #这是伪命令,相信你可以懂
    

    step6:
    此时fileProjectV1 项目是不是就有 dev1 代码了,且分支还是feature_revert
    进行代码梳理提交
    所在项目:fileProjectV1

    git  diff
    git add
    git commit
    git push 
    

    step7
    所在项目:fileProjectV1

    git checkout master
    git pull
    git checkout feature_revert
    git merge master 
    #不出意外有冲突,那么解决冲突
    git  add
    git commit
    git push 
    

    完美收官!

    no!!! 这个办法有个后遗症,master原有代码可能会丢失,那么问题来了?
    到底怎么解决呢??

    终极奥义 - R 闪 马氏三角杀: cherry-pick

    来上主线图


    image.png

    因为此时你想要dev1 的代码,但是这个分支又不能用了
    上操作完事了

    step1: 回master新开分支

    git checkout master
    git checkout -b dev_revert
    

    step2: 查看log, 查找你想要的代码

    git log # 根据commit msg 找到commitHash 
    git cherry-pick  commitHash
    

    step3:如果遇到冲突,自己解决冲突,然后执行git add, git push即可
    cherry-pick 是自动提交的 如果不想自动提交,加参数-n
    git cherry-pick commitHash -n

    ==================华丽分割线=====================
    如果你想要D2哪里的这个dev分支代码怎么操作呢?
    直接 cherry-pick 是错误的,因为那是2个分支的交点,
    如果原始提交是一个合并节点,来自于两个分支的合并,那么 Cherry pick 默认将失败,因为它不知道应该采用哪个分支的代码变动
    问题又来了,每日三省吾身,这个问题怎么解决??到底怎么解决呢?
    继续看招 ------- eqr闪
    git cherry-pick -m 1 <commitHash>
    熟悉不,和上面的方式一样-m含义也一样
    -m配置项告诉 Git,应该采用哪个分支的变动。它的参数parent-number是一个从1开始的整数,代表原始提交的父分支编号。
    上面命令表示,Cherry pick 采用提交commitHash来自编号1的父分支的变动。
    一般来说,1号父分支是作为变动来源的分支(the branch being merged from),
    2号父分支是接受变动的分支(the branch being merged into).

    来下具体的操作,干说不操作假把式
    看个分支树节点信息

    [Merge branch 'master' into  dev1]   
    对应的commitHash : qwer1234
    

    如果你要dev1 的这个节点全部代码
    git cherry-pick -m 2 qwer1234
    那么执行完如果有冲突,会终止合并的, 你要先解决冲突,
    然后 git add之后 ,
    执行git cherry-pick --continue
    最后git push
    记住这个方式 也是自动提交的【即不需要git commit,你可以add 完,git status 看下,这是个小秘密哦】
    当然,如果你想放弃此次cherry-pick,那么狠遗憾的告诉你,
    执行这个就可以了git cherry-pick --abort.

    传送门cherry-pick :http://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html

    4.加餐:revert 和reset 区别

    1. git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
    2. 在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。
    3. git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。

    相关文章

      网友评论

          本文标题:Revert的奇妙技巧

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