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回滚代码
远程仓库版本回退方法