git中有HEAD、Index和working Directory下表是他们的用途
名称 | 用途 |
---|---|
HEAD | 上一次提交的快照,下一次提交的父结点 |
Index | 预期的s下一次提交的快照,可以理解为暂存区 |
Working Directory | 工作目录 |
HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交.
Indox是预期的下一次提交。 也就是指向 Git 的 “暂存区域”,这就是当你运行 git commit 时 Git 看起来的样子。
working directory 会将.git文件夹中的内容解包为实际的文件以便编辑,
git主要是操纵上述三个地方来实现其版本管理的功能的
下面我们来一步一步实现这个过程:
假设我们进入到一个新目录,其中有一个文件。 我们称其为该文件的 v1 版本,将它标记为蓝色。 现在运行 git init,这会创建一个 Git 仓库,其中的 HEAD 引用指向未创建的分支(master 还不存在)。
此时,只有工作目录有内容
现在我们想要提交这个文件,所以用git add
来获取工作目录中的内容,并将其复制到索引中
运行git commit
,它会取得索引中的内容并将它保存为一个永久的快照,然后创建一个指向该快照的提交对象,最后更新 master 来指向本次提交 现在我们来运行一下git status
命令发现没有任何改动,这是因为现在这三个地方的文件是相同的
现在我们想要对文件进行修改然后提交它。 我们将会经历同样的过程;首先在工作目录中修改文件。 我们称其为该文件的 v2 版本,并将它标记为红色。 如果现在运行 git status,我们会看到文件显示在 “Changes not staged for commit,” 下面并被标记为红色,因为该条目在索引与工作目录之间存在不同。
我们运行 git add 来将它暂存到索引中
此时,由于索引和 HEAD 不同,若运行 git status 的话就会看到 “Changes to be committed” 下的该文件变为绿色 ——也就是说,现在预期的下一次提交与上一次提交不同
我们运行 git commit
来完成提交 现在我们来运行一下git status
命令发现没有任何改动,这是因为现在这三个地方的文件是相同的
以上就是git对版本控制的工作流程以及各个流程阶段工作目录,暂存区和本地仓库的状态
切换分支或克隆的过程也类似。 当检出一个分支时,它会修改 HEAD 指向新的分支引用,将 索引 填充为该次提交的快照,然后将 索引 的内容复制到 工作目录 中
知道了git的工作流程,现在我们可以来看一下git是如何重置的.
假设我们再次修改了 file.txt 文件并第三次提交它。 现在的历史看起来是这样的:
reset
做的第一件事是移动 HEAD 的指向. (这与checkout改变 HEAD 自身是不同)
此时本地的状态如下图所示
该命令等同与git reset --soft HEAD~
如图,它本质上是撤销了上一次 git commit 命令。 当你在运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。 当你将它 reset 退回 HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变索引和工作目录。
如果此时你运行git status
命令,你会看到如下输出你现在可以直接使用git commit 将其提交
接下来,reset 会用 HEAD 指向的当前快照的内容来更新索引
git在上一步的基础上,取消了暂存区的所有东西,于是,我们回滚到了上次commit之后.
此时运行git status会看到如下输出: reset 命令默认就是执行git reset --mixed HEAD~ 所以如果你运行git reset commitID
,git会为你清空暂存区并将HEAD指针指向commitID的位置.
如果你使用–hard选项,那么git会为你更新工作目录
必须注意,–hard 标记是 reset 命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。 其他任何形式的 reset 调用都可以轻松撤消,但是 --hard 选项不能,因为它强制覆盖了工作目录中的文件。 在这种特殊情况下,我们的 Git 数据库中的一个提交内还留有该文件的 v3 版本,我们可以通过 reflog 来找回它。但是若该文件还未提交,Git 仍会覆盖它从而导致无法恢复.
在使用reset命令时,你可以通过指定参数来选择你想要回滚的位置