Git填坑系列之(1)版本回退

Git填坑系列之(1)版本回退

1.1 版本回退

之前在实习时因为版本回退问题踩过坑,现在开始填坑,顺便系统了解了解Git的回退机制。

  1. 首先我们在GitHub上创建一个项目Test,用于测试使用:Git填坑系列之(1)版本回退

  2. 将项目clone到本地:Git填坑系列之(1)版本回退

    这样我们就相当于是在本地直接获得了一个git管理的项目,并且已经和远端建立好了联系。如果我们直接使用git init也是可以在本地创建一个git管理的项目的,只是还没有和远端建立联系。

  3. 我们尝试做一些commit,我是创建了文件info.txt。我一共做了3次commit(注意:我的commit均没有push),commit的内容可以使用git log来查看:Git填坑系列之(1)版本回退

    当然,使用git log --pretty=oneline也是可以的:Git填坑系列之(1)版本回退

    可以看出我一共commit了4次:(1)Initial commit(这一次是在GitHub上创建repo的时候自动提交的)。(2)add student.(3)add AnHui.(4)add age.前面一大串黄色的字符串是commit id. 我们需要使用commit id来指定回退到哪个版本。

    其中(1)commit message为add student时,info.txt的内容是:

    I am a student.
    

    (2)commit message为add AnHui时,info.txt的内容是:

    I am a student.
    I am from AnHui.
    

    (3)commit message为add age时,info.txt的内容是:

    I am a student.
    I am from AnHui.
    I am 21 years old.
    
  4. 我们当前的版本就是HEAD指向的版本,上一个版本就是HEAD^,上上个版本就是HEAD^^,上100个版本就是HEAD~100。OK,我们现在使用git reset --hard HEAD^来回退到上一个版本:Git填坑系列之(1)版本回退

    查看cat info.txt,果然回退成功:Git填坑系列之(1)版本回退

    如果这时我又想回到之前的add age的commit版本也是可以的,前提是你得记得它的commit id。因为这时我们使用git log查看的时候发现add age的commit已经没有了,取而代之的是HEAD指向add AnHui的commit!Git填坑系列之(1)版本回退

    我在命令行窗口中向上翻,查到add age的commit id,键入命令git reset --hard f913a5即可。注意:commit id不需要全部输入,只需要输入前几个就可以。Git填坑系列之(1)版本回退

    果然回退成功了:Git填坑系列之(1)版本回退

    注:如果无法在命令行窗口找到add age的commit id,使用git reflog可以查看每一次命令的记录:Git填坑系列之(1)版本回退

    前面黄色字符串就是每次命令操作之后的commit id的前几位。

1.2 撤销修改

前沿知识:Git在管理修改时在本地可以分为工作区(working tree)暂存区(index)版本库(tree-ish)三个区域,其中工作区就是我们日常编辑代码的区域,git记录工作区的所有修改。我们使用了git add <file>就将工作区的修改提交到了暂存区中。我们使用git commit就将暂存区中的内容全部提交到了版本库中,此时版本库中增加了一个最新的版本,HEAD指向该版本。git push将当前版本库中对应分支的最新版本提交到远端。

我们接着上面的内容来,我们对info.txt进行修改,添加一行I love programming.此时git statusGit填坑系列之(1)版本回退

此时修改已经产生,我们分3种情况讨论如何将刚刚的修改回退掉。

  1. git add

    这是最简单的情况,此时修改仅保存在工作区,还没有进入暂存区。使用git checkout -- info.txtinfo.txt在工作区的修改全部撤销到最近的一次git add或者git commit(如果最近没有git add的话):Git填坑系列之(1)版本回退

    果然撤销了修改。

  2. 已经git add,但未git commit

    此时修改已经被提交到了暂存区,但是还没有进入版本库中。你可以:

    1. 使用git reset HEAD info.txt先把info.txt在暂存区的修改撤销掉,回到工作区:Git填坑系列之(1)版本回退
    2. 同上,使用git checkout -- info.txt对工作区中的修改撤销。
  3. 已经git commit

    此时修改已经从暂存区被提交到了版本库中。这个时候可以使用git reset --hard HEAD^来强行回退到上一个版本,此时工作区的内容也回退到之前的版本。

关于命令git reset**

看了上面,我们可以看出,git reset不仅可以回退版本,也可以把暂存区中的修改回退到工作区中。那个命令git reset HEAD info.txt也是很神奇的(Pro Git书中原话The command is a bit strange, but it works.)。

Git官方文档中reset命令的格式为:

git reset [-q] [<tree-ish>] [--] <paths>git reset (--patch | -p) [<tree-ish>] [--] [<paths>]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

原来index就是指暂存区!而tree-ish可以理解成就是版本库了。

第1条命令:This form resets the index entries for all <paths> to their state at <tree-ish>. (It does not affect the working tree or the current branch.)

git reset HEAD info.txt就是第1条命令,意思是将暂存区中的info.txt()回复到与版本库HEAD指向的commit相同的状态。也就是将暂存区中git addinfo.txt给撤销掉了,因为info.txt的这个修改还并没有添加到版本库中,所以版本库中HEAD指向的commit info.txt还是之前的状态。

我们再来说说第3条命令:This form resets the current branch head to <commit> and possibly updates the index (resetting it to the tree of <commit>) and the working tree depending on <mode>. If <mode> is omitted, defaults to --mixed.

也就是会将当前分支的HEAD指向commit,并且会使暂存区重置到<commit>,工作区也可能会改变,不过这取决于参数<mode>.

关于参数<mode>(仅介绍softhard):

  • --soft:Does not touch the index file or the working tree at all (but resets the head to <commit>, just like all modes do). This leaves all your changed files “Changes to be committed”, as git status would put it.

    仅仅重置HEAD指向<commit>,而并不会改变暂存区和工作区的内容。

  • --hard:Resets the index and working tree. Any changes to tracked files in the working tree since <commit>are discarded.

    将HEAD指向<commit>,会重置暂存区和工作区,<commit>之后的改变全部都会丢弃。

1.3 参考资料

[1] Git Documentation - reset

[2] 廖雪峰git教程 - 版本回退/撤销修改

[3] Pro Git Book - Undoing things

[4] Pro Git Book - Reset Demystified

[5] **** git-reset用法总结