最近痴迷于git flow,打算对之前大部分人一提起就害怕的代码回退——git reset来进行一个实践,也算是一篇操作总结,想直接看结论的可以拉到文末,如有纰漏,欢迎大佬们指正补充。

一、本地git reset

在代码还没push到代码仓时,我们的代码都是本地操作,这时的git reset都在本地进行。那一般会有几种情况需要reset呢?总结了一下,大概有以下三种:

  • 不该add的文件被add;
  • 某些代码commit后想回退;
  • 某些已经commit的代码完全不想要了

下面就让我们来模拟一下,这三种情况下,代码要怎么进行reset。

在开始之前,给大家送上一些福利图,这对于接下来的场景理解一定会有所帮助。

文件状态转化
在这里插入图片描述
文件存储流程
在这里插入图片描述
git提交文件流程
在这里插入图片描述
git-reset图
在这里插入图片描述

1.1 把不该add的文件add了

我们在码云上新建一个代码仓,pull之后,在本地添加.gitignore文件,然后我们push到remote。这时本地和远程上的提交信息只有一个:
远程
本地
然后,我们在本地新添加一个Java文件:GitDemo.java,如下图:
在这里插入图片描述
然后再添加并add一个新文件:GitRollback.java:
在这里插入图片描述
然后我们commit这两个文件:
在这里插入图片描述
这时,我们发现GitRollback这个类是不需要提交的,那怎么办呢?

我们根据一些网友说的checkout来试试:

git checkout --  GitRollback.java

结果如下图,根本不起作用,:
在这里插入图片描述

git push 时还是默认选择两个文件:
要push的代码根据网友的说法,是因为此时的代码已经被add了,但是checkout这个命令究竟什么时候可以用?我们下文再谈。

继续第一种场景,从开头的git-reset图,我们可以知道,这时我们要回到没有被add的场景,那我们应该选择的是git reset – soft,我们来执行一下:

git reset --soft --GitRollback.java

在这里插入图片描述
没有任何作用,说明该命令不能只回退一个文件,那我们就只能进行版本回退了,在版本回退的同时,我们直接用idea工具来回退。

步骤如下:

1、选择版本:
在这里插入图片描述
2、在idea的工具栏上面,点击git—reset head ,出现如下:
在这里插入图片描述
填入版本号之后,我们发现本地的两个文件都已经恢复到没被add的状态,并且commit记录消失:

在这里插入图片描述

要是我们选择的是soft回退会怎么样?我们重新刚才的场景:

在这里插入图片描述
这次我们选择soft,点击reset后,再次commit时,发现文件还是add状态:
在这里插入图片描述
可以得知:

如果要让文件回退到未被add的状态,只能选择用git reset --mixed

1.2 某些代码commit后想回退;

在第一种情境的基础上,我们先commit&push代码,然后我们在GitRollback.java 上添加一些代码,并commit:
在这里插入图片描述
这时,我们只是想保存代码,而不是回退到未add的状态,那我们执行git reset soft即可:
在这里插入图片描述
那如果选择 mixed,代码会变成没有add的状态吗?我们复现一下:
在这里插入图片描述
发现没有区别,因为我们并没有在上一次commit中去新增文件,所以只是回退代码到未commit的状态,而不是回到没有被add的状态,所以这种场景下,选择soft或者mixed都可以

1.3 某些已经commit的代码完全不想要了

让我们模拟第三种场景,我们在GitRollback.java中,添加了一下代码,并新增了一个类,同时commit:

在这里插入图片描述
但是,如果我们不想要这段代码而且要保存这个类,也就是部分回退,那应该怎么操作呢?

打开命令窗口,输入以下命令:

git reset 1144a121 GitRollback.java

命令中的1144a121是上个版本的版本号。命令格式如下:

git reset 版本号  filename

结果如图:

在这里插入图片描述
我们需要再次在命令窗口执行commit代码,随便输入一个commit message,例如;

git commit -m "just reset local second commit on GitRollback.java"

在这里插入图片描述
这时我们的工作区还没有任何变化,我们在控制台输入:

git checkout GitRollback.java

再看我们的工作区:
在这里插入图片描述
代码已经回退,并且新增的类依旧在。查看git log:

在这里插入图片描述
多了一个commit信息。如果我们不想要commit,又该怎么样操作?我们复现一下。

将代码回退到上一个版本之后,我们使用hard命令来回退试试:

git reset --hard  1144a121 GitRollback.java

在这里插入图片描述
结果如下,并不能回退:

在这里插入图片描述
这时,我们上文提到的checkout命令就派上用场了。我们在命令窗口输入:

git checkout 1144a121 -- GitRollback.java

结果如下:
在这里插入图片描述
本地新增文件没有回退,并且指定文件代码回退到上一个版本,查看git log:
在这里插入图片描述
可以发现,并没有多余的commit信息,所以,对于这种情况,我们推荐使用checkout命令

可能会有读者好奇,每次我都是怎么复现场景的,其实,我是用了git reset hard 来快速复现的,这个命令的应用场景又是怎么样的呢?

1.4 将本地近期commit的版本的代码完全放弃,直接用远程的代码

git reset hard这个命令,就是用于这种场景的。当我们本地修改的代码不多,或者是不小心改动了多次之后,如果不想再从remote clone code,这应该是最好的实现方式。

我们来看看本地和 第一次commit之间的区别:

在这里插入图片描述
第一次commit的时候,本地只有两个Java类,而现在,我们有四个类,那如果我们想不保留这两个类,而且不留下commit信息,如何做呢?

复制第一次commit的版本号,打开idea最上方的git工具栏,git —— reset head,选择hard并输入版本号:

在这里插入图片描述
结果如下:
在这里插入图片描述
本地代码文件变成两个,没有额外添加新的commit记录,但是本地旧的commit记录仍然保持。

再看远程的commmit记录:
在这里插入图片描述

说明本地的git commit 记录没有实时更新。

1.5 本地代码回退多次

我们在远程新建一个文件:helloword.java,然后进行commit:
在这里插入图片描述
之后,我们在本地进行3次commit:
在这里插入图片描述
然后,我们update本地代码,发现本地最近的两次修改都有问题,这时就面临一个git reset的问题。
在这里插入图片描述
其实,git reset 除了指定版本号进行回退外,默认还有一个参数head,代表当前版本,我们只需要在head后加上要回退的次数,即可:

git reset head~2

操作如下:
在这里插入图片描述
我们validate一下,看看内容:
在这里插入图片描述
发现是第二次提交的内容,不是第一次commit的内容,回退后查看工作区:
在这里插入图片描述
发现我们第二次commit的内容还在,这是因为我们在本地commit之后还进行了update,update默认也算一次提交。

我们再次进行update,这次,我们选择rebase,看看git reset head 是否会回退到本地只提交一次时的代码:
在这里插入图片描述
git rebase之后,可以发现本地没有多余的commit信息,我们再执行一次:

git reset head~2

在这里插入图片描述
validate一下:
在这里插入图片描述
发现是第一次commit的内容,reset之后查看工作区:
在这里插入图片描述
代码成功回退到第一次commit的场景。

总结
1、要回退本地的多次commit,用git reset head~num 命令,其中num代表你要回退几次commit;
2、update project时,建议使用git rebase命令,这样update时不会生成多余的commit信息,而且会将本地的commit放到最前面,回退时就可以精准回退。

二、远程git reset

一般情况下,我们不会去回退远程版本到指定版本,因为远程代码在多人协作的情况下,可能更新速度比较快,一旦回退会影响团队其他人的代码,为了演示,这里我们看看这两种场景。

1、因为码云默认分支的设置缘故,不能force push,所以我们新建dev分支并push到remote之后,我们在dev分支上去push一些代码,发现最新一次的push代码有问题,回退到上一个版本

在这里插入图片描述

如上图,我们决定把代码回退到the second commit这个版本。

我们复制码云上的版本号,然后用idea自带的git reset 功能选择mixed,填入版本号,执行:

在这里插入图片描述

结果如图:
在这里插入图片描述

第三次commit的代码已经重新变成未commit状态,查看git log,commit信息还在:
在这里插入图片描述

那远程要怎么和本地保持一致呢?

git 有个不常用的force push 这时就派上用场了,如下图:

在这里插入图片描述
点击后,本地commit信息消失:
在这里插入图片描述
查看远程代码:

在这里插入图片描述
commit信息同步一致,说明版本已经回退。

2、发现某次push的代码里有问题,该如何回退到指定版本

在本地和远程同时commit&push几次代码,现在我们的版本到了v5,相对于the second commit而言,我们多了一个文件和一行代码。

在这里插入图片描述

同样,我们要reset to the second commit,那我们同样复制该版本号。然后,本地回退:

在这里插入图片描述
回退完成后,我们再次选择commit时,会把remote的新建文件也默认勾选了,这会造成一定的困扰:这个文件究竟是我之前修改的,还是其他人修改的呢

在这里插入图片描述

那我们暂时不看commit,直接force push,代码强推上仓库了。
在这里插入图片描述

所以这时说明版本也回退了,本地代码没有被覆盖的问题。

三、如何避免git reset

虽然上面说了种种关于git reset的技巧和方法,但我希望各位读者永远都用不上。作为一个coder,我们应该尽量避免这种可能会造成代码覆盖的高风险事情的发生。

改掉一个bug,最好的方式是重写代码后commit,而不是在本地搞一堆reset之后,还担心把别人的代码给覆盖掉,有时还会分不清哪些代码是自己改的,哪些是别人改的。

这是我一直持有的观点。那我们如何避免呢?我认为有以下两种方式。

1、设立保护分支

现在类似码云、GitHub上都有设置保护分支的功能,本地的代码只能被提交到自己的分支上,如果要合并到保护分支,那就必须先经过commiter的审视,这样子,即使本地代码再怎么玩,只要最后一关没被合入保护分支,那就不会造成代码仓的代码问题。

2、idea自带的rollback和local history功能

作为一个高级开发工具,idea在git上的功能还是比较强大的,当我们本地的代码要commit之前,可以点击下图的rollback功能,看看我们改了哪些东西,哪些需要回退:
在这里插入图片描述
也可以打开local history:
在这里插入图片描述
查看各个文件的history,避免修改有误。

四、总结

  1. 如果要将本地已经add的文件回退到工作区(untracked ),请使用git reset --mixed 功能;

  2. 已经commit的文件代码如果想回退,不管用git reset --mixed还是git reset --soft,都只是将代码回退到add状态,并不会将其回退到untracked ;

  3. 回退commit中的某个文件到某个指定版本,可以用

git checkout 版本号 -- filename
  1. 回退到某个版本并不保留commit记录和代码时,用git reset hard。
  2. 所有的git reset命令都会改写commit log,只是有时local和remote不一致。
  3. git reset remote 只能 force push到非保护分支,会造成remote repository的commit log和代码消失问题。
  4. 避免git reset,最好是设置保护分支,然后合理利用好idea自带的rollback和local history功能。

参考链接

git 让单个文件回退到指定版本

Git语法之Checkout使用

What’s the difference between git reset --mixed, --soft, and --hard?

ocal-branching-on-the-cheap

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐