Git 版本管理时,往往需要撤销某些操作。
本文介绍几种最主要的情况,给出详细的解释。更多的命令可以参考《常用 Git 命令清单》一文。
一、撤销提交
一种常见的场景是,提交代码以后,你突然意识到这个提交有问题,应该撤销掉,这时执行下面的命令就可以了。
$ git revert HEAD
上面命令的原理是,在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化。它不会改变过去的历史,所以是首选方式,没有任何丢失代码的风险。
git revert
命令只能抵消上一个提交,如果想抵消多个提交,必须在命令行依次指定这些提交。比如,抵消前两个提交,要像下面这样写。
$ git revert [倒数第一个提交] [倒数第二个提交]
git revert
命令还有两个参数。
--no-edit
:执行时不打开默认编辑器,直接使用 Git 自动生成的提交信息。--no-commit
:只抵消暂存区和工作区的文件变化,不产生新的提交。
二、丢弃提交
如果希望以前的提交在历史中彻底消失,而不是被抵消掉,可以使用git reset
命令,丢弃掉某个提交之后的所有提交。
$ git reset [last good SHA]
git reset
的原理是,让最新提交的指针回到以前某个时点,该时点之后的提交都从历史中消失。
默认情况下,git reset
不改变工作区的文件(但会改变暂存区),--hard
参数可以让工作区里面的文件也回到以前的状态。
$ git reset --hard [last good SHA]
执行git reset
命令之后,如果想找回那些丢弃掉的提交,可以使用git reflog
命令,具体做法参考这里。不过,这种做法有时效性,时间长了可能找不回来。
三、替换上一次提交
提交以后,发现提交信息写错了,这时可以使用git commit
命令的--amend
参数,可以修改上一次的提交信息。
$ git commit --amend -m "Fixes bug #42"
它的原理是产生一个新的提交对象,替换掉上一次提交产生的提交对象。
这时如果暂存区有发生变化的文件,会一起提交到仓库。所以,--amend
不仅可以修改提交信息,还可以整个把上一次提交替换掉。
四、撤销工作区的文件修改
如果工作区的某个文件被改乱了,但还没有提交,可以用git checkout
命令找回本次修改之前的文件。
$ git checkout -- [filename]
它的原理是先找暂存区,如果该文件有暂存的版本,则恢复该版本,否则恢复上一次提交的版本。
注意,工作区的文件变化一旦被撤销,就无法找回了。
五、从暂存区撤销文件
如果不小心把一个文件添加到暂存区,可以用下面的命令撤销。
$ git rm --cached [filename]
上面的命令不影响已经提交的内容。
六、撤销当前分支的变化
你在当前分支上做了几次提交,突然发现放错了分支,这几个提交本应该放到另一个分支。
# 新建一个 feature 分支,指向当前最新的提交 # 注意,这时依然停留在当前分支 $ git branch feature # 切换到这几次提交之前的状态 $ git reset --hard [当前分支此前的最后一次提交] # 切换到 feature 分支 $ git checkout feature
上面的操作等于是撤销当前分支的变化,将这些变化放到一个新建的分支。
(完)
强子 说:
git reflog
git reset --hard ******
Git后悔药
2019年12月24日 18:39 | # | 引用
别样风采 说:
写的很好
2019年12月24日 20:46 | # | 引用
李不白 说:
没有一定的逻辑能力,用不起来这玩意儿
2019年12月24日 20:54 | # | 引用
Payne 说:
最常用的 git reset --hard
最喜欢的后悔药 git reflog
2019年12月24日 22:53 | # | 引用
冯罡 说:
简洁明了
2019年12月25日 09:14 | # | 引用
小学生之手 说:
老阮总是能普及一些很欠缺的常识点。 爱死你了。
2019年12月25日 10:28 | # | 引用
cxx 说:
我之前也写了篇关于撤销git操作的博客,今天又学到新的「git revert」~感谢阮老师!
不知道能不能把我那篇博客放上来呢嘻嘻
2019年12月25日 16:08 | # | 引用
小财神的奋斗 说:
谢谢普及,我以前原来都是反复折腾。
2019年12月26日 07:48 | # | 引用
石樱灯笼 说:
常年在个人分支上使用rebase来处理nighty commit。
不过我感觉rebase一般人都驾驭不了,不论国内还是国外都为rebase互相喷来喷去。
2019年12月26日 14:00 | # | 引用
米斯特先生 说:
git reset --hard
用的最多
其他的嘛
哈哈
2019年12月26日 17:44 | # | 引用
沈扬东 说:
看了会儿命令,发现都是平时在 vscode 和 github desktop 上用的可视化操作了。推荐用点点点的方式,直观不出错~
2019年12月27日 10:11 | # | 引用
liquid 说:
冒昧的问下,阮兄是否是翻译的这篇文章 https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/ ?
2019年12月27日 12:11 | # | 引用
liquid 说:
和 “How to undo (almost) anything with Git “ 这篇文章很相似,如果是翻译的话,注明下会比较好。
2019年12月27日 12:30 | # | 引用
潘昊宇 说:
我一个小程序员就用过一次撤销,一次一不小心回退后找不到了,后来发现,只要是提交到git的东西,不管在不在分支里,都可以找回,就觉得很NB
2019年12月29日 10:34 | # | 引用
AlexMaxes 说:
想知道如何撤回中间某一次提交 网上找了很多都不一样... 感谢!
2019年12月30日 10:46 | # | 引用
路人丙 说:
假如你想撤回倒数第三次提交,你可以这么做:
$ git rebase -i HEAD~4
在出现的编辑窗口中,将倒数第三条 commit 删除,然後保存。
就这么简单。
2019年12月31日 23:01 | # | 引用
路人丙 说:
如果你执行过 prune 和 gc 操作,可能就找不回。
2020年1月 1日 07:57 | # | 引用
浪里白条 说:
一口气把您以前的文章都看了一遍,文笔真好,真切务实,充满了人文关怀。
2020年1月10日 16:43 | # | 引用
西蒙 说:
大神,你好,第五点好像不对哈,我找了下资料,如果要取消暂存,是使用git reset HEAD filename,而git rm --cached filename 是从git索引中删除文件,即不做版本控制
2020年3月16日 16:16 | # | 引用
nina 说:
是的,看到这点我也是一懵
2020年3月26日 11:32 | # | 引用
tanjuntao 说:
对的,这一点我也发现了,第五点阮老师应该是写错了。一般 git rm --catch 是用来将 index 里面的文件 untrack 掉的。
2021年1月15日 19:29 | # | 引用
小吴YYDS 说:
restore 命令了解一下,不仅可以方便撤销工作区、暂存区文件,还可以直接撤销工作区和暂存区文件,比 checkout 和 reset 来的更直白更舒服一些。
2022年5月 6日 23:40 | # | 引用