我们在使用git的过程当中很难避免的一点就是手贱,因为人嘛总有犯错疏忽的时候,有时候一不小心就操作错了。我也经常遇到这种情况,所以这时候对git的了解和掌握就非常重要,即使操作错了,我们也可以通过git还原到我们希望它变成的状态。下面我们来看几个例子,来实际体验一下git的强大。
不小心add错了
这是一个非常非常容易出现的问题,我自己也经常遇到。有时候编译出了二进制文件,明明知道是不应该添加进git管理的。因为添加进来之后会使得整个repo变得非常大,别人clone和pull都非常费劲。而且前面也说过了,一旦commit之后,即使你删除了,这份文件依然还是会保存在git仓库当中。
所以如果我们发现不小心把一个我们的测试文件也一起add进来了,我们commit了之后才发现。这个时候应该怎么办?
我们来实际操作一下,比如我们创建了一个叫做a.test的文件用来测试。结果测试完成之后忘记了删除,直接commit了。这个时候我们要把它删除,应该怎么做呢?
我们要做的就是把它删除,有人会说我们直接rm -rf删除不行吗?我们试试看就知道了。
这样删除了之后你会发现它会提示你,说这个改动没有被commit,因为我们只是删除了操作系统当中的文件,并没有删除git仓库当中已经储存的文件。所以只是这样删除了之后,即使我们再次commit提交,git会记录成一次对这个文件的删除操作。虽然我们看不到这个文件了,但是它仍然在git当中占据空间。
所以要删除只能使用git rm命令来进行,它会将文件从git版本管理以及文件系统当中一起移除。当我们提交之后,从下一个提交开始,这个文件就不会被存储一份了。
这里有一个小问题是为什么会从下一个版本开始?因为我们做的事情只是从git中删除掉文件,而不是撤销add文件的操作。所以git当中会记录两条,一条是记录了添加文件,一条是删除了文件。比如说我们在add file的commit当中提交了文件,在delete file的commit当中删除了文件,在delete以及之后的提交当中,是没有这个文件的记录的,但是在add file这个commit当中这个记录仍然存在。
所以问题来了,如果我就想把这个文件从git仓库当中完全删除,一点记录都不留下呢?其实也简单,我们只需要在commit的时候加上--amend参数即可。--amend表示不提交新的commit而是在当前的commit上修补,这样相当于add file和delete file的commit合并成了一个,那么这个文件的记录也就不存在了。
不过使用--amend需要小心,如果记录已经push过远程,会导致和远程的记录不吻合。这个时候需要使用git push -f来强行push。但是强行push会覆盖远程的commit,可能导致其他人代码的混乱,是一个非常危险的操作,请一定谨慎。
只想撤销,不想删除
除了我们不小心提交了本该要删除的内容,还有一种很常见的情况是我们的文件是很重要的,但是我们不想提交到git。比如我们编译出来的二进制文件,它们都是要用到的,只是不应该被push到git而已。我们在add了之后才发现add错了文件,于是我们想要撤销,有办法吗?
比如这个时候我们已经add了文件,但是还没有commit,我们想要把这个a.test文件从暂存区删除,这样就不会被记录下来了。我们应该怎么做呢?
其实很简单,也是通过git rm命令。因为这个时候还没有commit,也就是说这个文件还没有被提交进git仓库当中,我们只需要把它从暂存区移除就可以了。如果使用git rm命令,它既会从暂存区移除,也会从本地删除文件。我们不想删除本地的文件,这个时候我们只需要加上一个参数--cached,表示我们只想移除已经缓存在git暂存区的内容。
我们发现这个文件回到了被add之前的状态。
但假如我们不小心已经commit了,已经提交进git仓库了之后才发现,这个时候应该怎么办?
这个时候我们需要做的是撤销这个commit,给我们一次重新来过的机会。我们使用的命令是git reset --soft HEAD^,git reset命令非常危险,我们操作的时候需要谨慎。如果不小心用了--hard参数会回滚所有的操作,直接恢复到某一个commit时的状态。比如说我们当前在version3,我们回到了version1,如果使用--hard操作的话,version2和version3的所有改动都会丢失。因此一定谨慎使用--hard,最好使用--mixed或者是--soft,它不会修改本地的文件。关于这两个的区别,我们将会以后在介绍git reset命令的时候详细介绍。
这里的HEAD指的是当前git节点的指针,HEAD^表示的是上一个版本。当然我们也可以用commitid来代替。
我们发现一切都恢复到了illustrate unstage commit这个提交之前的状态,那么我们只需要从缓存当中删除a.test,再次提交即可。
这么操作完了之后你会发现在git log当中illustrate unstage commit这个提交不见了。的确如此,因为它被我们撤销了,同样,reset操作也会导致本地和远程状态不一样。如果要push的话也需要-f强制进行,这也是一个危险的操作,一定要谨慎。
撤销修改
加入我们想要撤销的不是一个文件,而是一次修改呢?就比如我们git add了之后才发现某一个文件的修改错了,我们想要把它恢复到之前的状态,这时候应该怎么操作呢?
我们在第五篇里加上了一行废话,但是等我们git add了之后才发现第五篇里有这么一个无用的改动。这时候应该怎么操作呢?
这个时候我们想要做的是撤销这个文件的修改,如果我们只是要把它从暂存区中移除来恢复到git add之前的状态,我们可以使用git reset,但是这个文件当中的修改依然还是存在。这个时候我们可以用一个命令叫做checkout,这个命令有很多种用途,我们先介绍其中的一种。
我们可以使用git checkout -- filename来回滚某一个文件的改动,注意这个也是一个危险操作,它会直接将文件恢复到之前提交的状态。中间的改动会全部丢失,因此一定要想好了再操作。在git当中有一个原则,只要是提交过的内容几乎都是可以找回的,而没有提交的内容丢失之后就很难找回了,因此对于这种改变没有提交内容的命令,我们一定要小心。
最后我们来看下效果,我们checkout之后,第五篇文章当中的改动真的消失了。不仅是从暂存区消失了,就连文件本身当中的改动也不见了。
到这里我们常见的几种需要撤销改动的场景以及对应的方法就逗介绍完了,对于新手来说,这些命令应该是非常常用的。虽然其中的一些操作说起来危险,但是只要我们想清楚了再操作,三思而后行,是可以避免悲剧发生的。而且操作危险的命令我感觉更加提升我们的能力,因为小心谨慎会逼迫你加深理解。
好了,今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发)
本文使用 mdnice 排版
- END -