撤回修改

本地分支撤销

这块针对的是在本地,如果你刚对工作区的代码进行了修改,或者将部分代码加入了暂存区,或者是提交了一个commit,此时你后悔了,那么如何吃后悔药
1、先回顾一下git中的三个概念
HEAD就是一个指向当前分支的一个指针
index,也就是暂存区,其实就是将会形成下一次提交的文件
工作区,其实就是我们的开发区
刚开始工作区中会有一些文件
然后我们执行git add命令可以将工作区中的文件加入到暂存区中
接着我们执行git commit命令,可以将暂存区中的文件一次性提交到git仓库中,同时创建一个commit object指向一个tree object,tree object指向那些文件对应的blob object。
然后master指针会指向那个commit object,HEAD指针指向master
我们同样可以对文件再次修改,让一个文件变成第二个版本,然后再次git add和git commit,此时暂存里的文件会变成第二个版本,同时也会提交到仓库里创建一个新的commit object
新的commit object会引用上一个commit object
假设我们又修改了第三次,同时提交了,那么此时会有此三个commit object,同时引用第二个commit object,工作区和暂存区也都是第三个版本

reset撤回操作

(1)执行git reset --soft HEAD^,会有下面的影响
1)在仓库中,将master分支指向上一个commit 2)暂存区和工作区还是停留在第三个版本,没有变化
因此这个命令,相当于是撤回了git commit操作,因为git commit操作,就是将暂存区中的第三个版本放入仓库,同时创建一个新的commit object指向第三个版本,master指针也指向最新的commit object
此时就相当于取消了刚执行完的git commit操作
我们可以再次对暂存区加入更多的内容,然后再次执行git commit操作,此时再看一下效果,你会发现跟git commit --amend类似
(2)执行git reset HEAD^,默认等同于git reset --mixed HEAD^
回到三个区域都是第三个版本的状态,执行git reset HEAD^
1)将master指针指向上一个commit 2)将master指针指向的commit对应的版本恢复到暂存区中
此时暂存区中的文件版本会变成第二个版本
这个命令,相当于是撤回了git commit和git add操作
这里我们可以想象一下,如果用git reset HEAD,就可以实现将刚git add到暂存区中的文件版本回退到仓库中的上一个版本,相当于就是回退了git add操作
(3)执行git reset --hard HEAD^
相当于是彻底撤回刚才的git commit,git add,以及工作区中的代码修改
因为这个命令,会将指针指向上一个commit,同时暂存区里的内容变成上一个commit的版本,同时将上一个commit的内容恢复到工作区
其实这个就是之前讲解过的,相当于是回退到历史上某一个版本了

场景梳理

git checkout -- 文件名

  1. 如果刚刚修改了工作区中的内容,但是没放入暂存区和仓库,此时要抛弃这些修改,怎么办?
    1. 这种情况是很常见的,一般见于什么呢?临时修改技术方案。。。。
      比如说,你现在要开发一个工作流模块,然后之前定的技术方案是基于jBPM 4.x去开发,然后呢,你就开始吭哧吭哧吭哧开始写代码,引入了jBPM 4.x的jar包
      写了几天,你的leader,架构师,反悔了,他说,我前两天没设计好,考虑不周到,要不换成用Activit去开发吧。。。
      你做了几天,发现jBPM 4.x的功能不能满足你的需要,临时换框架
      也可能是,做了几天以后,发现其他团队已经有一个现成的基础类库,可以直接基于他们的来封装
      如果说此时你修改的代码比较少的话,那么就用git checkout -- 文件名,就可以将这个文件的内容恢复到上一次提交的状态,完全抹掉你最近的修改
      但是如果你已经修改了很多代码了,此时你需要来个一次性的,强有力的恢复:git reset --hard HEAD
      git reset --hard HEAD 或者是 git checkout -- 文件名
      意思就是保持仓库和暂存区里的文件版本为HEAD最新版本,同时将上一次提交的HEAD版本写回工作区中,覆盖掉这次修改。
  1. 如果修改了工作区中的内容,同时放入了暂存区,但是还没提交,此时要抛弃这些修改,怎么办?
    1. (1)你希望将暂存区里加入进去的内容撤销掉,然后在工作区里面重新修改代码
      git reset HEAD = git reset --mixed HEAD,一样的命令,就是将仓库中HEAD指向的上一个commit,恢复到暂存区,同时恢复到工作区
      (2)你希望将暂存区和工作区里面的内容一次性抹掉:git reset --hard HEAD
  1. 如果修改了工作区中的内容,放入了暂存区,还提交了,怎么办?
    1. 本来今天的代码你都提交了,但是你的leader说,这块代码不要了,此时怎么办?回退版本,直接回退到没做任何代码修改的那个版本
      git reset --hard HEAD^,git reset --hard commit标识符
      git reset --hard HEAD^,分支指针指向上一个commit,同时上一个commit的内容恢复到暂存区,也恢复到工作区
      git reset --hard v1_commit_标识符
      git reset --hard HEAD^^
总结
上面说的,同样都是针对本地的commit,一旦你已经push到了远程仓库,就不能这样了,否则会弄乱你的提交历史

远程和本地同时撤销

notion image

场景

1、在出现冲突的时候
如果两个分支merge出现了冲突,临时打算取消注意,不想merge了,那么此时可以执行git merge --abort命令即可
2、如果仅仅是在本地完成了merge后想要撤回
很简单,直接执行git reset --hard HEAD^,将分支挪回merge之前的那个commit即可,就相当于没执行过merge
3、如果是在推送了merge后的commit到远程仓库之后想要撤回
我给大家说一个非常经典的实际的案例,工作中之前经常遇到的
比如说,我们之前讲过,按照GitFlow工作流来走,最后是在release分支进行QA测试,测试通过之后,会合并到master分支,准备进行预发布测试和上线
但是。。。。。
不靠谱的队友,不靠谱的pm
不靠谱的队友,你是跟其他团队协作一起上线的,此时你的仓库的代码都合并到了master,确认没问题了,服务A,依赖于其他团队开发的服务B,在不同的仓库里的两个项目;但是。。。你要一起上线的其他团队的同学,服务B,突然发现在QA环境,临时测试出来了一个大bug,此时pm沟通了一下,评估了一下风险,决定,今天取消上线,下周再上
尴尬了
你的代码都合并到master分支了。。。但是说不上线了,此时你是不能将不准备上线的代码留在master分支里的
如果你有其他需求在最近一两天也要上线,也会合并到master分支,然后如果那个需求上线,master分支会把你的那个不准备上线的功能的代码,给带上线
所以这种情况,是不能接受的
常见于大公司,上百人协作的那种大项目,比如某巨头公司的广告系统,最核心的系统,牵扯到上百人,数百人一起在搞,架构升级
如果碰到这样的情况,你是需要将本次之心的merge给撤回的,无论是本地还是远程,都要撤回
但是如果push到了远程仓库以后,你是不能再本地reset一下,再push的,因为那样会搞乱你的提交历史的
不上线了。。。撤回release/v1.2.0到master分支的合并
需要在本地执行一个命令:git revert -m 1 HEAD
git revert实现的效果,不是说指针往回挪动,而是说创建一个新的commit,对应的代码版本跟合并之前的那个commit是一模一样的
  • m 1的意思就是,恢复到当前这个分支合并之前的那个状态,此时会创建一个新的commit,这个新的commit对应的版本跟合并之前的commit是一模一样的
这就完美撤回了merge操作,同时此时如果执行git push origin master,会让远程仓库的master分支也回退到merge之前的状态,但是也多了一个新的commit出来
但是此时有一个问题,就是如果再次执行git merge feature/001,你会发现,git不让你merge了,因为之前已经merge过了,很尴尬
这个时候,过了几天了,然后呢,已经准备好可以上线了,你需要重新将release分支的代码合并到master分支上来,此时该怎么办呢?
而且此时一般是说feature分支需要继续修改代码,比如又加入了一些代码,但是如果这个时候你胡乱再次合并,会发现master分支仅仅merge进去了feature分支后来修改的那一点代码而已。。。。
此时,要做的事情是,继续对feature分支进行修改,完成最终版本的代码
然后,再次执行git revert HEAD,将master分支再次进行revert,回退到之前merge过后的那个状态,这个状态是包含了feature分支merge进来的部分代码的,然后再次执行git merge feature/001,将feature分支最新的修改也merge进来
ok了,完美解决,再次git push origin master,推送到远程分支去
4、结合gitlab实战一下分支合并的撤回操作
梳理一下
(1)如果是要撤回已经push到远程仓库的merge操作
(2)在本地执行git revert -m 1 HEAD,再执行git push origin master,此时本地和远程的提交历史都会多一个commit出来,该commit的内容和合并之前的master指向的那个commit是一样的,同时master此时指向最新的那个commit
(3)但是后面,如果要再次将release分支和master分支进行合并,此时是需要特殊处理的,再次在本地执行:git revert HEAD,git push origin master。就是再次将master还原到指向一个新的commit,该commit的内容与上一次merge后的那个commit一样,包含merge的内容
(4)最后再次将release分支与master分支进行合并,此时可以保证release分支所有的内容,都是合并到master分支的
5、接着上一讲,如果在本地做了commit,同时还推送到了远程仓库,此时要撤回,怎么办?
简单了,比撤回merge简单多了,直接git revert HEAD,不就回到了上一个commit的状态,然后再次git push origin master,完美同步撤回远程仓库的commit操作了