Git 回滚 checkout、reset、revert
首先看一下 Git 的工作流程 
checkout 是检出的意思,作用是将某次 commit 的状态检出到工作区,它的过程是先将 HEAD 指向某个分支的最后一次 commit,然后从 commit 恢复 index,最后从 index 恢复工作区。 通常用于切换分支,创建新分支,即HEAD 从一个分支切换到另一个分支,或创建并切换到新分支。
(1)放弃工作区中的全部修改 命令:git checkout .
(2)放弃工作区中某个文件的修改 命令:git checkout filename 可以通过 git status 命令查看工作区的所有修改
什么是工作区的修改? 比如文件 A1.txt ,在A1.txt 中添加了几行修改,没有执行 git add . 或者 git add XXX/A1.txt 则 A1.txt 的修改还是在工作区中,执行如上两条命令均可丢弃对 A1.txt 的修改。
(3)强制放弃 暂存区(index) 和工作区的改动 命令:git checkout -f
如果已经执行了 git add . 或者 git add XXX/A1.txt ,则 A1.txt 的修改已经进入到了暂存区也就是 index,此时如果想丢弃 A1.txt 的修改,则需要执行 git checkout -f
checkout 只能撤销修改,对于添加的文件则需要执行 git clean -df ,将新添加的文件删除
下面是关于 reset 和 revert 使用 在开发过程中或者上线后,发现新开发的内容有问题,来不及修改或者想舍弃不要了,避免不了需要回退到以前某一次提交的版本,或者撤销某一次提交的修改。
reset 和 revert 都可以用来回滚代码,但是他们的功能却又不同。 reset 用来 “回退” 版本 revert 是用来 “还原” 某次的提交
说了但是还是不明白?举例如下,假如某个分支有三次修改,提交历史如下 82rb8w9 第三次修改 6dh37c6 第二次修改 29s62u4 第一次修改
如果发现 第二次和第三次修改有错误,需要回滚到第一次修改,就是要舍弃第二次和第三次的所有修改,直接将版本 “回退” 到第一次修改,此时使用 reset。
如果发现第二次修改有错误,但是还要保留第三次的修改,其实就是要把第二次的修改 “还原” 了,其他的修改则保留下来,此时使用 revert。
在了解 reset 和 revert 如何使用前,首先看下 Git 关于 分支和 commit 的 tree 结构图 图1 
A0、A1、A2、A3 为分支 A 的四次 commit,当前 HEAD 指针指向的是最新的 A3 commit B0、B1 是分支 B 的两次 commit
每次 commit 都会生成一个 40位的标识码,可以通过 命令: git log 查看 图2 
reset 会重置到某个提交的 commit 看上图,最新的三次提交我分别是提交了 A1.txt、A2.txt、A3.txt 三个文件 假设本来在 图1 A0 位置处 当提交 A1.txt 后 HEAD 指针指向了 A1 当提交 A2.txt 后 HEAD 指针指向了 A2 当提交 A3.txt 后 HEAD 指针指向了 A3
现在不想要 A2 和 A3 的提交了,想回退到 A1, 获取到 A1 的 commitID : b3a0d5532808c0d6c40eabe1c8a5a33e3636a96f 执行命令:git reset --hard b3a0d5532808c0d6c40eabe1c8a5a33e3636a96f
然后执行push 命令: git push origin -f 注意:一定要执行 push  reset 命令后可跟三个不同命令 git reset --mixed commit (默认) git reset --soft git reset --hard
注1: --mixed:暂存区会更新到指定的commit。但是工作目录不受影响。默认参数 注2:–soft:不会改变暂存区或工作目录。(即原本的更新内容还在工作目录和暂存区,但是 commit id 回到之前的。可以重新 commit) 注3:–hard:暂存区和工作目录都会更新到指定的 commit。(即指定commit之后的更新会全部消失)
在看此时的 Git 分支结构图  此时 HEAD 指针指向的是 A1,而 A1 之后的所有提交 A2、A3 则被舍弃了,就像历史回退一样,从来没有发生过 A2、A3
如果当前分支只有自己在使用,一般是不会出大问题,因为出问题了也是自己哭 如果当前分支多人在使用,且A1 之后有其他人的提交,则此操作将丢弃所有人的提交,理论上是不允许的。
此时再使用 git log 查看日志  会发现 b3a0d5532808c0d6c40eabe1c8a5a33e3636a96f 就成了最新的提交,且 A2、A3 的提交已经看不到了
如果此时发现 A2 或者 A3 提交的部分内容还得找回来怎么办,git log 已经没有他们的记录了。 莫慌,git 还提供了另一个命令 git reflog ,执行看结果  从图中可以看到从下往上依次记录了 A3 1999abd 的提交 A2 4325ab4 的提交 A1 b3a0d55 的提交 并且最上边一条记录 b3a0d55 (HEAD -> A, origin/A) HEAD@{0}: reset: moving to b3a0d5532808c0d6c40eabe1c8a5a33e3636a96f 就是记录了 使用 reset 命令将 HEAD 指针移动到 A1 commit 的操作
所以我们的一切操作 Git 都是会有记录的,现在我发现刚才 reset 到 A1 的操作不行,我还得回到 A3 的提交怎么办呢?
在 git reflog 命令下 复制 A3 的 commit 1999abd 执行命令: git reset --hard 1999abd 然后执行:git push origin -f  此时又重新回到了 A3 的提交。
上边操作完成后又回到了 A3 的提交,此时执行 git log  可以看到 A1、A2、A3 的提交又能看到了
此时发现 A2 的提交有错误,A3 的修改需要保留 使用 revert 还原某次提交 复制 A2 的 commitID 4325ab4a53e8f60608e1275653a6677dcd0bd553 首先执行: git revert -n 4325ab4a53e8f60608e1275653a6677dcd0bd553 然后执行: git commit -m "还原 A2 的添加" 最后执行: git push
再次查看 git log  此时 A2 的提交被还原了,且又产生了一条新的提交。
切换到分支 B,将分支A合并到分支B 执行命令:git merge --squash --no-commit A git log 查看日志  然后B分支,修改 添加 B1.txt,B3.txt 提交并 push 到远端 git log 查看日志  此时发现 分支 A 合并到 分支 B 的修改是错误的应该撤销这次合并。 复制 分支 A 合并到 分支 B 的 commitID c3f0d54ec171a84a617f576451c28a41c94f21d0 执行命令:git revert -n c3f0d54ec171a84a617f576451c28a41c94f21d0 然后执行: git commit -m "撤销分支 A 合并到 分支 B 的提交" 最后执行: git push 大功告成
|