Git -- 撤销
Opened this issue · 0 comments
reset
一般我们在最新的 commit
写错时,可以用 reset --hard
来把 commit
撤销
git reset --hard HEAD^
reset
这个指令虽然可以用来撤销 commit
,但它的实质行为并不是撤销,而是移动 HEAD。而 reset --hard HEAD^
之所以起到了撤销 commit
的效果,是因为它把 HEAD
移动到了当前 commit
上,从而起到了「撤销」的效果。所以reset
的本质实际上是移动HEAD
。那么 reset
后面跟着的那个 --hard
是什么意思呢?除了--hard
还有什么其他参数呢?
举个例子,当前的操作情况如图:
在此举例要 reset
到的目标 commit
为e。当前缓存区和工作目录都有代码,也有已经commit
的提交f(为了便于查看效果,当前把 index0.js
的修改commit
,index1.js
的修改放进缓存区,index2.js
的修改还放在工作区)。我们给 reset
不同的参数,来看看 reset
之后的代码如何:
index0.js
对应已经commit
过的(有commit
信息:f
)
index1.js
对应已经add .
过的 (已放进缓存区)
index2.js
对应修改的(处于工作区)
reset --hard
:重置工作目录
就是说,未提交的修改会被全部擦掉。例如你在上次 commit
之后又对文件做了一些改动,然后,你执行了 reset
并附上了 --hard
参数(git reset --hard HEAD^
),然后你工作目录里的新改动也一起全都消失了,不管它们是否被放进暂存区。如下图:
reset --soft
:保留工作目录
reset --soft
会在重置 HEAD 时,工作区中的修改还是放进工作区、已经 commit
的和已经在暂存区的都放进暂存区。也可以说是,工作区的还在工作区,commit
的只是取消了commit
但还是已经add
了。如下图:
reset --mixed
:保留工作目录,并清空暂存区
reset
如果不加参数,那么默认使用 --mixed
参数。它的行为是:工作区的修改、暂存区的内容以及已经commit
的修改,都会被放进工作区。简而言之,就是「把所有差异都混合(mixed
)放在工作区中」。如下图:
reset --merge
:保留工作目录,清空暂存区或commit过的
和--hard
类似,只不过如果在执行reset
命令之前你有改动一些文件并且未提交,merge
会保留你的这些修改,hard
则不会。总之它的行为就是:工作区的修改还在工作区、暂存区的内容以及已经commit
的修改,都会被清除。如下图:
reset --keep
:保留工作区并丢弃一些之前的提交
和--hard
类似,执行reset
之前改动文件如果是分支修改了的,会提示你修改了相同的文件,不能合并。如果不是分支修改的文件,会移除缓存区。git status
还是可以看到保持了这些修改。总之它的行为就是:工作区的修改还在工作区、暂存区的内容也被放在了工作区,已经commit
的修改则被清除了。如下图:
来个表总结对比下
命令 | commit | 暂存区 | 工作区 |
---|---|---|---|
reset --hard |
清除 | 清除 | 清除 |
reset --soft |
暂存区 | 暂存区 | 工作区 |
reset --mixed |
工作区 | 工作区 | 工作区 |
reset --merge |
清除 | 清除 | 工作区 |
reset --keep |
清除 | 工作区 | 工作区 |
checkout
如第一张图,当我们 git status
时,git
会提示使用git checkout
丢弃修改:
git checkout -- filePath
丢弃工作区某个文件的改动
git checkout -- .
丢弃工作区所有文件的改动
如图所示,使用 checkout
会撤销工作区的改动:
more
git reset --hard origin/branchName
?
将本地分支代码与远程分支同步,和
git reset --hard commit_id
效果一样(commit_id
为远程分支最新的一次commit_id
),是一个快捷操作。hard
对代码的影响同上,一样hard
也可以改为soft
、mixed(省略)
已经push
到远程仓库的commit
不允许reset
?
git reset
是会丢弃掉commit
的,如果commit
已经被push
到远程仓库上了,也就意味着其他开发人员就可能基于这个commit
形成了新的commit
,这时你去reset
,就会造成其他开发人员的提交历史莫名其妙的丢失,或者其他灾难性的后果。当然了,没有其他开发人员基于此的话是可以的,比如说自己的分支,此时必须使用强制推送覆盖远程分支,否则无法推送到远程分支,reset
之后需要执行git push origin branch --force
来同步远程。
常用命令?
reset
到上一次:git reset HEAD^
,reset
到上上次:git reset HEAD^^
...,也可以:git reset HEAD~2
配合git log
、git reflog
:git reset [commit_id]
不想回滚所有的文件?
git reset [commit_id] [file]
reset
之后又反悔了?
git reflog
得到reset
的commit_id
,再reset
回到未来
回滚之前务必先提交自己的代码?
因为你如果使用
--hard
的,同时你又没有提交的话,真的回不去了=_=
但是,reset --hard
时真的没有commit
怎么办?
git reset
时共有三种情况:1.之前的修改进行了commit
提交(可以回去,同上);2.之前的修改未进行commit
提交,但是进行了git add
操作;3.之前的修改未进行commit
提交,也未进行git add
操作
第二种情况,我们可以用git fsck
命令,我们执行git fsck --lost-found
,然后找到目录'.git/lost-found'
,经过一顿复杂的查找(一个一个文件的查看)可以找到之前的add
但未commit
的提交
第三种情况,git 命令是暂时无解了,此时我们就需要感谢IDE提供了一个强大的插件:Local History
(vscode)。文件修改后修就会在这里生成新的记录(要保存),我们一个个对比选择好要还原的版本,再右键 ->Restore
就好了
后两种情况操作如此复杂,所以reset
之前最好要commit
一下
push远程了,并且小伙伴已经基于此开发了,又需要reset?
git
操作:。。。
简单暴力点,不复杂的话还是辛苦手动重新改吧
reset --hard
之后我们想应用某一个被 reset
掉的commit
?
此时我们可以使用
git cherry-pick
命令,git cherry-pick commit_id
,会将commit_id
应用到分支上
Note
reset
有风险,使用需谨慎,记得commit
参考链接:
Git 实战手册
git reset回滚代码
远程仓库版本回退方法