chinawzc/Blog

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 还有什么其他参数呢?

举个例子,当前的操作情况如图:

image

在此举例要 reset 到的目标 commit 为e。当前缓存区和工作目录都有代码,也有已经commit的提交f(为了便于查看效果,当前把 index0.js 的修改commitindex1.js的修改放进缓存区,index2.js 的修改还放在工作区)。我们给 reset 不同的参数,来看看 reset 之后的代码如何:

index0.js 对应已经 commit 过的(有commit信息:f
index1.js 对应已经 add . 过的 (已放进缓存区)
index2.js 对应修改的(处于工作区)

reset --hard:重置工作目录

就是说,未提交的修改会被全部擦掉。例如你在上次 commit 之后又对文件做了一些改动,然后,你执行了 reset 并附上了 --hard 参数(git reset --hard HEAD^),然后你工作目录里的新改动也一起全都消失了,不管它们是否被放进暂存区。如下图:

image

reset --soft:保留工作目录

reset --soft 会在重置 HEAD 时,工作区中的修改还是放进工作区、已经 commit 的和已经在暂存区的都放进暂存区。也可以说是,工作区的还在工作区,commit的只是取消了commit但还是已经add了。如下图:

image

reset --mixed:保留工作目录,并清空暂存区

reset 如果不加参数,那么默认使用 --mixed 参数。它的行为是:工作区的修改、暂存区的内容以及已经commit 的修改,会被放进工作区。简而言之,就是「把所有差异都混合(mixed)放在工作区中」。如下图:

image

reset --merge:保留工作目录,清空暂存区或commit过的

--hard类似,只不过如果在执行reset命令之前你有改动一些文件并且未提交,merge会保留你的这些修改,hard则不会。总之它的行为就是:工作区的修改还在工作区、暂存区的内容以及已经commit 的修改,会被清除。如下图:

image

reset --keep:保留工作区并丢弃一些之前的提交

--hard类似,执行reset之前改动文件如果是分支修改了的,会提示你修改了相同的文件,不能合并。如果不是分支修改的文件,会移除缓存区。git status还是可以看到保持了这些修改。总之它的行为就是:工作区的修改还在工作区、暂存区的内容也被放在了工作区,已经commit 的修改则被清除了。如下图:

image

来个表总结对比下

命令 commit 暂存区 工作区
reset --hard 清除 清除 清除
reset --soft 暂存区 暂存区 工作区
reset --mixed 工作区 工作区 工作区
reset --merge 清除 清除 工作区
reset --keep 清除 工作区 工作区

checkout

如第一张图,当我们 git status 时,git 会提示使用git checkout丢弃修改:

image

git checkout -- filePath

丢弃工作区某个文件的改动

git checkout -- .

丢弃工作区所有文件的改动

如图所示,使用 checkout 会撤销工作区的改动:

image


more

git reset --hard origin/branchName?

将本地分支代码与远程分支同步,和git reset --hard commit_id 效果一样(commit_id为远程分支最新的一次commit_id),是一个快捷操作。hard对代码的影响同上,一样 hard 也可以改为softmixed(省略)

已经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 loggit refloggit reset [commit_id]

不想回滚所有的文件?

git reset [commit_id] [file]

reset 之后又反悔了?

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