git reset --soft如何在不接触索引文件的情况下重置最后的提交?

振凯

这可能是一个菜鸟问题。

假设我有一个Git存储库,通过使用git add,它在暂存区中已经有一些文件。然后我做一个git reset --soft @~

我很高兴看到我上次提交的一些文件现在已放入暂存区。

但是如何?我检查.git文件夹。唯一改变的是当前分支的引用。和一个我认为不相关的“ ORIG_HEAD”。最可疑的索引文件根本没有被触及。还有谁能告诉我如何查看内容?

那么git怎么做呢?谢谢。

周二

以其最简单的形式,1 git reset做两件事:

  • 移动当前分支,和/或
  • 撤消索引中的内容

要了解它的工作方式,原因以及工作原理,至少在相对较高的水平上,您需要知道提交的工作方式以及索引的工作方式。无论如何,这些都是紧密联系在一起的。

提交,树和斑点

首先,提交只是类型为“ commit”的存储库对象,该对象具有数据,提交消息和一些其他信息(树,父项,作者和提交者):

$ git cat-file -p 5f95c9f850b19b368c43ae399cc831b17a26a5ac
tree 972825cf23ba10bc49e81289f628e06ad44044ff
parent 9c8ce7397bac108f83d77dfd96786edb28937511
author Junio C Hamano <[email protected]> 1392406504 -0800
committer Junio C Hamano <[email protected]> 1392406504 -0800

Git 1.9.0

Signed-off-by: Junio C Hamano <[email protected]>

该提交是git来源的一部分(这是git版本1.9.0的提交)。与所有存储库对象一样,其名称为40个十六进制字符的SHA-1值。

提交工作目录由决定tree,它是另一个git对象,因此具有另一个SHA-1名称。来自的输出git cat-file -p 972825cf23ba10bc49e81289f628e06ad44044ff太长,无法完全包含,但其开始于:

100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f    .gitattributes
100644 blob b5f9defed37c43b2c6075d7065c8cbae2b1797e1    .gitignore
100644 blob 11057cbcdf4c9f814189bdbf0a17980825da194c    .mailmap
100644 blob 536e55524db72bd2acf175208aef4f3dfc148d42    COPYING
040000 tree 47fca99809b19aeac94aed024d64e6e6d759207d    Documentation
100755 blob 2b97352dd3b113b46bbd53248315ab91f0a9356b    GIT-VERSION-GEN

这些blob条目是构成git源的所有文件(以及每个子目录;每个子目录tree都有blobs)。每个文件blob都有一个基于文件内容的唯一SHA-1 ID。tree保持文件的“模式”(其实只是它的列表x位这些模式都是100644100755)和文件名与资源库中的BLOB对象的SHA-1名一起。(如040000上面所见,其他模式跟踪子树,符号链接和子模块。只有blob限制为100644100755。)

每个git信息库对象都是只读的。ID为的提交5f95c9f...将永远不会更改。它始终tree将ID作为其(单个)972825c...其ID536e555...始终是该文件的特定版本的文件COPYING如果文件已更新,则会输入具有不同的SHA-1的不同的Blob。

索引

Git的“索引”(也称为“暂存区”,有时也称为“缓存”)是一个文档记录较差的文件,从本质上讲,它表示“下一次提交将执行什么操作”。

与存储库对象不同,索引是完全可写的。为了使“下一次提交”有所不同,git会在索引中添加或删除条目。例如,要更新名为的文件COPYING,您将在运行后运行它git add COPYING这将获取文件的新内容,COPYING并将其复制到存储库中(它们最终将在其中永久存在),2为结果计算SHA-1“真名”。然后,这个新的SHA-1进入索引(​​连同模式和名称,COPYING基本上是提交所需要的所有内容)。

做出承诺

因为索引已经准备好了所有东西,所以很容易进行新的提交。所有正确的blobs已经在存储库中。Git只需要将索引变成一些tree对象,将它们写入存储库,获得最新顶层的最终SHA-1tree并编写一个新commit对象。新提交将具有以下属性:

  • tree是根据索引写入的内容
  • parent就是HEAD现在的状态(或多或少-在进行合并提交时有些与多位父母纠缠不清)
  • authorcommitter这些日期是从当前时间采取和你的git的配置user.nameuser.email,或从参数(--author)或者环境变量,如果这些被设置为覆盖东西
  • 该消息是您作为提交消息或作为-m参数进行编辑的内容。

因此git编写了该提交,从而生成了一个新的唯一的SHA-1。然后,它将SHA-1本身写入某处。

分支机构和 HEAD

如您所说,如果您在“分支上master”,则git status意味着该文件.git/HEAD包含文字字符串ref: refs/heads/master这就是git所谓的“间接引用”:只是说“去查找另一个引用,这里是名称”的引用。通常,您在某个分支上,并且HEAD是对该分支的间接引用。

分支本身可以用几种不同的方式存储,但是最简单的是另一个文件.git,在本例中为file .git/refs/heads/master如果该文件存在并且您已阅读,则该文件将包含SHA-1之类的5f95c9f850b19b368c43ae399cc831b17a26a5ac这就是当前的提交,也是git知道您“在”哪个提交的方式,就像ref: refs/heads/mastergit知道您在branch上的方式一样master

为了进行新的提交,git如上所述编写了提交,这产生了一个新的唯一SHA-1。然后,由于您在branch上master,因此git只需将新的commit-ID写入.git/refs/heads/master,现在您就在新的commit上,这是branch的技巧master

你也可以有一个“分离的头”,其中,尽管听起来像来自法国大革命,只是表示事情HEAD不是间接引用。而是HEAD包含原始SHA-1。在这种情况下,要进行新的提交,git会像以前一样进行提交,但不是更新.git/refs/heads/master,而是将新的提交ID直接写入HEAD

git重置

因此,考虑到所有这些,让我们具体看一下是什么git reset

如果您进行--soft重置,则git会使索引完全保持不变。这意味着它仅更新当前分支。

为了更新当前分支,git做与进行新提交时相同的操作:它查找HEAD间接指向哪个分支,并将新的SHA-1写入该引用中。如果HEAD指向master,则只需将一个新的SHA-1写入.git/refs/heads/master

git编写的SHA-1是您在命令行上提供的SHA-1:

git reset --soft @~   # @~ means @~1, which means HEAD~1, aka HEAD^

您可以通过运行查看SHA-1的内容git rev-parse(对于HEAD-relative ref,当然必须更改之前执行此操作):resetHEAD

$ git rev-parse @~
9c8ce7397bac108f83d77dfd96786edb28937511

如果您告诉git reset您使用--mixed,它还会更新索引。它放入索引的东西来自它将写入分支的SHA-1提交:

$ git reset --mixed HEAD -- COPYING

在这里,通过告诉它将更改HEADHEAD,您将得到重置,以使分支与原来的位置完全没有距离,因此该分支毕竟不会得到更新。但是-- COPYING说“COPYING从目标修订版中提取文件的SHA-1 HEAD,然后将SHA-1放入文件的索引中” COPYING因此,这意味着下一次提交将不会更改文件COPYING,因为我们已经将旧的SHA-1放回了索引中。

如果您告诉git reset您使用--hard,它还会更新工作目录(它已经在更新分支和索引)。通过从存储库中获取实际文件内容(从唯一的blob SHA-1查找它们)并覆盖工作目录版本来实现此目的。如果您尚未git add-ed和git commit-ed这些工作目录版本,则意味着更改已消失。(如果这样做了 git add,则它们在存储库中,但是如果您没有这样做,则git commit它们有资格进行垃圾回收-请参见脚注。)

既然您使用过--soft,就抑制了对索引的更改,因此唯一git reset可以做的就是更改分支提示文件的内容.git/refs/heads/master


1git reset曾经只有这三种操作模式。现在它具有--merge--keep,加上的功能--patch比简单的情况还要多。这有点像关于西班牙宗教裁判所的Monty Python片段:“我们的三种模式分别是软,混合,硬和合并。...四种!我们的四种模式是软,混合,硬,合并和保留...”

2存储库中的对象“永远存在”,但有一个非常大的例外:未引用的对象(git fsck显示为dangling)是垃圾回收的候选对象。未引用的Blob,提交等都是完全正常的。它们位于占用的磁盘空间周围(通常很少:对象是压缩存储的),以便您可以恢复事物,以便稍后在git认为清理是个好主意时可以一次将它们收集和丢弃。

当某个外部标签(分支名称,标签等)HEAD直接或间接指向对象时,这些对象将被“引用”(并因此永远存在)分支名称指向该分支上最尖端的提交。该提交指向其树,该树指向任何子树和blob,因此所有这些树将永远存在。并且该提交指向其父提交,因此这些父始终存在。每个父母的承诺依次指向其自己的父母,而这些父母也将永远存在。

当您将分支标签从其移开时,提交将变为引用:

A <- B <- C   <-- HEAD=master

这里master(我们的当前分支)点CCBBA但是,如果我们:

$ git reset --hard HEAD^

我们master指出了B,指出了A提交C现在是未引用的:它已被废弃,最终将连同其树,所有子树和Blob一起被垃圾回收。发生类似的事件,例如git commit --amend使用进行软复位和新提交,进行D指向的新提交B,并master指向D

A - B - D   <-- HEAD=master
      \
        C   [abandoned]

rebase操作将复制并放弃整个提交序列,从而生成大量用于垃圾回收的候选对象。这就是为什么悬挂物体正常的原因。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章