使用checkout,reset,revert进行代码回滚

2017-12-15  本文已影响20人  伊凡的一天

git reset和git revert都可以用于撤销已存在的commit,其中git reset可以细化到针对单个文件进行操作。

    首先, 一个Git仓库主要有三个重要的组成:工作目录,缓存区和提交历史,如图所示:

Main components of a git repository

1. git reset

git reset通常用来撤销对缓存区和工作目录的修改。用法如下:

(1)git reset <file>

        从缓存区移除特定的文件,但不改变工作目录。

(2)git reset

        重设缓存区,匹配最近的一次提交,工作目录不变。

(3)git reset --hard

        重设缓存区和工作目录,匹配最近的一次提交。除了取消缓存之外,--hard标记还会清除工作目录中所有未提交的更改,因此使用前确定你想扔掉你所有的本地开发。

(4)git reset <commit>

        将当前分支的末端移动到commit,将缓存区重设到这个commit,但不改变工作目录。

(5)git reset --hard <commit>

        将当前分支的末端移动到commit,并将缓存区和工作目录都重设到这个commit。

git reset命令会将HEAD指向你指定的commit,也就是说,可以直接通过git reset命令来移除你指定的commit之后的所有commit。例如,下面这两条命令让hotfix分支向后回退了两个commit:

git checkout hotfix

git reset HEAD~2

before git reset after git reset

注意,hotfix分支现在最后的两个commit变成了悬挂提交,这意味着下次git进行垃圾回收的时候,这两个commit就会被删除。因此,如果你希望改变此git仓库的提交历史,你可以使用git reset命令。

除了在当前分支上操作,你还可以传入以下参数来更新缓存区和工作区:

--soft 缓存区和工作区都不会改变

--mixed 默认选项。缓存区和你指定的commit同步,工作区不发生改变。

--hard 缓存区和工作区都同步到你指定的commit

这些参数往往针对HEAD来使用。例如你add了某个你不想add的文件,这时可以运行git --mixed reset HEAD,此时暂存区被清空,而你的工作空间并没有改变。

或者你希望将最近的两个commit合并成为一个,此时你可以运行git --mixed reset HEAD~2,这样你的commit记录被改变,缓存区被清空,但工作空间没有改变,因此你可以重新add,再重新commit,这样两个commit记录就变成了一个。

另一方面,你希望完全放弃你没有提交的改动,你可以运行git reset --hard HEAD,这样你的缓存区和工作空间就被强行更新了。

针对已经提交到远程的commit,你希望reset到HEAD~1,此时运行:

(1)git reset HEAD~1 此时查看status,发现有待stage的文件,因为工作空间的文件与HEAD~1的文件不同,因此运行git checkout future2.txt即可。(或者最开始就运行:git reset --hard HEAD~1)

(2)然后执行强制推送:git push -f

git reset可以针对某个文件,此时reset命令会将你指定的文件加入到缓存区中(因此文件从HEAD状态变成了你指定的某个commit的状态)。

git reset git status

可以看到,暂存区产生了一个变更,这是因为文件从HEAD的内容变成了HEAD~2的内容(逻辑上),实际上,工作区域不会发生改变。而产生Changes not staged for commit的原因是此时工作区的文件和HEAD~2的文件不相同。

git commit

将暂存区的内容commit并push之后,此时future2.txt文件内容就回退为了两个commit之前的版本,并且commit log都没有更改。此时查看status:

git status

发现future2.txt依旧待stage,这是因为git reset命令工作区不会发生改变,因此工作区的文件内容与HEAD下的文件内容不一致,此时运行:

git checkout future2.txt

来撤销所有的更改。再次查看status:

git status

并且此时future2的文件内容已经处于最新状态。

git reset总结:(1)git reset在提交级别上,可以撤销已有的commit,并且清除git log,适用于消除还未提交到远程的commit,或者是自己私有的分支,或者并不在意篡改commit log的情况。

(2)git reset针对单个文件时,工作区不会被改变,暂存区一定改变。并且不会清除git log,往往最后需要通过git checkout <file>来同步工作区。 --soft、--mixed 和 --hard 对文件层面的 git reset 毫无作用,因为缓存区中的文件一定会变化,而工作目录中的文件一定不变。


2. git revert

git revert命令可以通过指定commit id来对某个commit进行撤销,此命令会生成一个新的commit来执行撤销动作,不会修改过去已有的commit,这避免了Git丢失项目历史,这一点对你的版本历史和协作的可靠性来说是很重要的。git revert用法如下:

(1)git revert <commit>

        生成了一个新的提交,此提交用于撤销<commit>引入的修改。

例如,下面的命令会撤销HEAD前面的第二个commit:

git checkout hotfix

git revert HEAD~2

before revert after revert

相比 git reset,它不会改变现在的提交历史。因此,git revert 可以用在公共分支上,git reset 应该用在私有分支上。你也可以把 git revert 当作撤销已经提交的更改,而 git reset HEAD 用来撤销没有提交的更改。


3. git checkout

git checkout这个命令有三个作用:检出文件,检出提交和检出分支。

(1)git checkout master

        回到master分支。

(2)git checkout <commit> <file>

        查看文件之前的版本。此时工作目录中下的<file>被替换为<commit>中的<file>,并将它加入缓存区。

(3)git checkout <commit>

        更新工作目录中的所有文件,使得和某个特定提交下的文件一致。此操作会使你处于HEAD分离的状态,因此是只读操作。

git checkout <commit> git checkout <commit> <file>

git checkout命令除了用于常见的分支切换以外,还可以用于代码更改的撤销。例如:

当 future2.txt 存在还没有add的changes时,运行git checkout future2.txt 可以撤销future2.txt的更改

git checkout  HEAD~2 future2.txt 可以将当前的future2.txt恢复为HEAD~2的版本,此时工作区和暂存区都会改变。

总结:如果文件A修改了,你希望撤销这个修改,那么可以使用以下方案:

(1)修改还未加入暂存区(还没有add),直接运行:

         git checkout A

(2)修改已经加入了暂存区,但还没有commit:

         git reset HEAD A(修改暂存区)

         git checkout A(修改工作区)

(3)修改已经commit

         git reset HEAD~1(reset到前一个commit)

         git checkout A

参考:github.com/geeeeeeeeek/git-recipes/wiki/5.2-%E4%BB%A3%E7%A0%81%E5%9B%9E%E6%BB%9A%EF%BC%9AReset%E3%80%81Checkout%E3%80%81Revert-%E7%9A%84%E9%80%89%E6%8B%A9

上一篇下一篇

猜你喜欢

热点阅读