首页 > 技术文章 > Git撤销及回滚操作

birdy-silhouette 2020-11-11 12:58 原文

git工作流

  • 工作区:当前无任何操作时的工作环境
  • 暂存区:git add 将修改的内容放到暂存区
  • 本地库:git commit -m xxx 将暂存区内容提交到本地库

语法格式

  • HEAD^: 上一次提交
  • HEAD~n:前n次提交
  • HEAD ComitHash:目标版本号CommitHash

代码回滚

1、工作区

git checkout a.txt  # 丢弃指定文件的修改
git checkout .   # 丢弃全部修改

2、 缓存区

代码git add到缓存区但并未commit时

git reset HEAD . # 丢弃全部修改
git reset HEAD a.txt	# 丢弃指定文件的修改

这个命令仅改变暂存区,并不改变工作区,这意味着在无任何其他操作的情况下,工作区中的实际文件同该命令运行之前无任何变化

3、本地库

commit到本地分支、但没有git push到远程

# 重置暂存区和工作区
git reset --hard HEAD^

执行后工作区和暂存区的新改动也一起全都消失了

--soft:保留工作目录,并把重置 HEAD 所带来的新的差异放进暂存区

git reset --soft HEAD^

保留工作目录和暂存区中的内容,并把重置 HEAD 所带来的新的差异放进暂存区。

这就是--soft--hard 的区别:--hard 会清空工作目录和暂存区的改动,而 --soft则会保留工作目录的内容,并把因为保留工作目录内容所带来的新的文件差异放进暂存区

reset 不加参数(mixed):保留工作目录,并清空暂存区

reset 如果不加参数,那么默认使用 --mixed 参数。它的行为是:保留工作目录,并且清空暂存区。也就是说,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录。简而言之,就是「把所有差异都混合(mixed)放在工作目录中」。

git reset HEAD^
git reset --mixed HEAD^

工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录

reset三种模式区别

  1. --hard:重置位置的同时,直接将 working Tree工作目录index 暂存区repository 都重置成目标Reset节点的內容,所以效果看起来等同于清空暂存区和工作区。
  2. --soft:重置位置的同时,保留working Tree工作目录index暂存区的内容,只让repository中的内容和 reset 目标节点保持一致,因此原节点和reset节点之间的【差异变更集】会放入index暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和Reset节点之间的所有差异都会放到暂存区中。
  3. --mixed(默认):重置位置的同时,只保留Working Tree工作目录的內容,但会将 Index暂存区Repository 中的內容更改和reset目标节点一致,因此原节点和Reset节点之间的【差异变更集】会放入Working Tree工作目录中。所以效果看起来就是原节点和Reset节点之间的所有差异都会放到工作目录中。

4、提交已经push到远程仓库

方式一:使用revert

#撤销前n次提交并生成一次新的提交
git revert -n HEAD~n 	

#回到某次提交
git revert -n 版本号
然后重新push

注意revert功能是撤销某次提交而不是撤销到某次提交,-n取消自动提交

tips:
(1)这个命令会生成一个新的提交记录,新的提交记录的修改就是把要移除的那次需要移除的提交的修改抵消掉
(2)如果工作区或暂存区还有修改未提交,那么会提示需要先提交或存储起来;
(3)如果在移除的过程中发生了冲突,可以在解决了冲突后,使用 git add添加修改的冲突文件, git revert --continue 来继续操作,如果没有需要提交的,也可以使用 git revert --skip来继续下一步,如果后续还有冲突,也同样如此,直到移除的操作全部完成;当然也可以使用 git revert --abort 来停止移除操作,恢复到执行git revert 之前的状态。

原理: git revert是用于“重做”某一个版本,以达到撤销该版本的修改的目的。比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:

适用场景: 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。

示例

考虑这个例子,我们提交了 6 个版本,其中 3-4 包含了错误的代码需要被回滚掉。 同时希望不影响到后续的 5-6。

* 982d4f6 (HEAD -> master) version 6
* 54cc9dc version 5
* 551c408 version 4, harttle screwed it up again
* 7e345c9 version 3, harttle screwed it up
* f7742cd version 2
* 6c4db3f version 1

这种情况在团队协作的开发中会很常见:可能是流程或认为原因不小心合入了错误的代码, 也可能是合入一段时间后才发现存在问题。 总之已经存在后续提交,使得直接回滚不太现实。

下面的部分就开始介绍具体操作了,同时我们假设远程分支是受保护的(不允许 Force Push)。 思路是从产生一个新的 Commit 撤销之前的错误提交。

使用 git revert 可以撤销指定的提交, 要撤销一串提交可以用 .. 语法。 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。

git revert --no-commit f7742cd..551c408
git commit -a -m 'This reverts commit 7e345c9 and 551c408'

其中 f7742cd 是 version 2,551c408 是 version 4,这样被移除的是 version 3 和 version 4。 注意 revert 命令会对每个撤销的 commit 进行一次提交,--no-commit 后可以最后一起手动提交。

方式二:使用reset

git reset --hard HEAD^
git push 本地分支 远程仓库分支 -f

此时如果用“git push”会报错,因为我们本地库HEAD指向的版本比远程库的要旧,要强制推送

reset(回滚到)和revert(重做)的区别

  • revert是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在;
  • reset是指将HEAD指针指到先前已存在的历史提交,历史记录中不会出现放弃的提交记录。

场景

情况一:修改的文件已被git commit,但想再次修改不再产生新的Commit

# 修改最后一次提交 
$ git add sample.txt
$ git commit --amend -m"说明"

参考文档:
https://www.jianshu.com/p/c2ec5f06cf1a
https://blog.csdn.net/yxlshk/article/details/79944535

推荐阅读