赞
踩
之前在实习时因为版本回退问题踩过坑,现在开始填坑,顺便系统了解了解Git的回退机制。
前沿知识:
Git在管理修改时在本地可以分为三个区域:
1. 工作区(working tree)
2. 暂存区(index)
3. 版本库(tree-ish)
其中 工作区就是我们日常编辑代码的区域,git记录工作区的所有修改。
我们使用了 git add <file>就将工作区的修改提交到了 暂存区中。
我们使用了 git commit就将暂存区中的内容全部提交到了 版本库中,此时版本库中增加了一个最新的版本,HEAD指向该版本。 git push将当前版本库中对应分支的最新版本提交到远端。
step 1. 首先我们在GitHub上创建一个项目Test,用于测试使用,并将该项目clone到本地。
这样我们就相当于是在本地直接获得了一个git管理的项目,并且已经和远端建立好了联系。如果我们直接使用
git init
也是可以在本地创建一个git管理的项目的,只是还没有和远端建立联系。
step 2. 我们尝试做一些commit,我是创建了文件info.txt。我一共做了3次commit,commit的内容可以使用
git log
来查看:
当然,使用
git log --pretty=oneline
也是可以的,可以获得简洁版的commit log:
可以看出我一共commit了4次:
(1)Initial commit(这一次是在GitHub上创建repo的时候自动提交的)。
(2)add student.
(3)add AnHui.
(4)add age.
前面一大串黄色的字符串是commit id. 我们需要使用commit id来指定回退到哪个版本。
其中:
(1)commit message为Initial commit,此时还没有创建任何文件。
(2)commit message为add student时,info.txt的内容是:
I am a student.
(3)commit message为add AnHui时,info.txt的内容是:
- I am a student.
- I am from AnHui.
(4)commit message为add age时,info.txt的内容是:
- I am a student.
- I am from AnHui.
- I am 21 years old.
版本记录如下图所示:
step 3. 我们当前的版本就是HEAD指向的版本,上一个版本就是HEAD^,上上个版本就是HEAD^^,上100个版本就是HEAD~100。OK,我们现在使用:
git reset --hard HEAD^
来回退到上一个版本:
查看cat info.txt,果然回退成功,最后一次commit的修改(增加了I am 21 years old)被撤销了:
版本记录如下图所示:
如果这时我又想回到之前的add age的commit版本也是可以的,前提是你得记得它的commit id。因为这时我们使用git log查看的时候发现add age的commit已经没有了,取而代之的是HEAD指向add AnHui的commit!!
我在命令行窗口中向上翻,查到add age的commit id,键入命令
git reset --hard f913a5
即可。
注意:commit id不需要全部输入,只需要输入前几个字符即可。
果然又穿梭成功了:
注:如果无法在命令行窗口找到add age的commit id,使用
git reflog
可以查看每一次命令的记录:
前面黄色字符串就是每次命令操作之后的commit id的前几位。
我们接着上面的内容来,我们对info.txt进行修改,添加一行
I love programming.
此时git status:
此时修改已经产生,我们分3种情况讨论如何将刚刚的修改撤销掉。
情况1:未git add
这是最简单的情况,此时修改仅保存在工作区,还没有进入暂存区。
使用
git checkout -- info.txt
将info.txt在工作区的修改全部撤销到最近的一次git add或者git commit(如果最近没有git add的话)。
果然撤销了修改。
情况2:已经git add,但未git commit
此时修改已经被提交到了暂存区,但是还没有进入版本库中。你可以:
2. 同上,使用git checkout -- info.txt对工作区中的修改撤销。
情况3:已经git commit
此时修改已经从暂存区被提交到了版本库中。这个时候可以使用
git reset --hard HEAD^
来强行回退到上一个版本,此时工作区的内容也回退到之前的版本。
以上3种情况的撤销修改也可以直接使用某些可视化工具来进行。
这里我力荐VS Code,我简单介绍一下其git操作的几个小功能,更多功能欢迎探索:
一、可直接查看工作区和暂存区的修改
二、直接查看对应修改文件与原状态diff
三、撤销工作区修改
直接点击Discard Changes即可将文件回复到原始状态:
四、撤销暂存区修改
需要先Unstage Changes,将该文件对应修改的状态由STAGED CHANGES转换为CHANGES,相当于撤销了InstanceSolver.py修改的git add:
后面要想将文件回复到原始状态,操作就和 三、撤销工作区修改 一样了~
五、直接撤销上一次commit
操作如下图所示:
注意这里的undo last commit只是撤销你最近一次commit的所有修改,将这些修改转移到暂存区(即STAGED CHANGES)中
关于命令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(<path>)回复到与版本库HEAD指向的commit相同的状态。也就是将暂存区中git add的info.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`.
也就是会将:
关于参数`<mode>`(仅介绍`soft`和`hard`):
--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>`之后的改变全部都会丢弃。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。