git如何重置--soft重置上次提交而不触及索引文件?

时间:2014-04-10 03:07:19

标签: git git-reset git-add

这可能是一个菜鸟问题。

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

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

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

那么git怎么做呢?感谢。

2 个答案:

答案 0 :(得分:3)

最简单的形式是, 1 git reset做了两件事:

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

要了解其工作方式和原因以及工作原理,您需要了解提交的工作方式以及索引的工作方式,至少在相对较高的级别。无论如何,它们紧密相连。

提交,树木和blob

首先, commit 只是一个类型为" commit"的存储库对象,其中包含提交消息和其他一些信息(树,父母,作者,和提交者):

$ git cat-file -p 5f95c9f850b19b368c43ae399cc831b17a26a5ac
tree 972825cf23ba10bc49e81289f628e06ad44044ff
parent 9c8ce7397bac108f83d77dfd96786edb28937511
author Junio C Hamano <gitster@pobox.com> 1392406504 -0800
committer Junio C Hamano <gitster@pobox.com> 1392406504 -0800

Git 1.9.0

Signed-off-by: Junio C Hamano <gitster@pobox.com>

此提交是git源代码的一部分(它是git版本1.9.0的提交)。与所有存储库对象一样,其名称是40-hex-character SHA-1值。

提交的工作目录tree确定,git cat-file -p 972825cf23ba10bc49e81289f628e06ad44044ff是另一个git对象,因此它有另一个SHA-1名称。 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

这些tree条目是构成git源代码的所有文件(和每个blob的子目录;其中有blob个)。每个tree都有一个唯一的SHA-1 ID,基于文件的内容。 x会保留文件列表&#34;模式&#34; (实际上只是它的100644位 - 这些模式都是100755040000)和文件名以及存储库中blob-object的SHA-1名称。 (其他模式,如上面所见的100644,跟踪子树,符号链接和子模块。它只是限制为1007555f95c9f...的blob。 )

每个git存储库对象都是只读的。 ID为tree的提交永远不会更改。它始终具有(单个)972825c... ID 536e555...。 ID为COPYING的文件始终是文件COPYING的特定版本。如果文件已更新,则会有一个新的不同的blob与新的不同的SHA-1进入。

索引

Git&#34; s&#34; index&#34; (也称为&#34;暂存区&#34;有时候&#34;缓存&#34;)是一个记录不完整的文件,实质上代表&#34;下一次提交将会发生什么?#34 ;

与存储库对象不同,索引非常易于编写。制作&#34;下一次提交&#34;有不同的东西,git添加或删除索引中的条目。例如,要更新名为git add COPYING的文件,您可以在编辑后运行COPYING。这将获取文件COPYING的新内容并将它们复制到存储库(它们最终将永远存在), 2 计算SHA-1&#34;真名称&#34 ;为了结果。这个新的SHA-1然后进入索引(​​以及模式和名称blob - 基本上,提交所需的一切)。

提交

因为索引具有这样的所有准备,所以很容易进行新的提交。所有正确的tree已经存储在存储库中。 Git只需要将索引转换为一些tree个对象,将它们写入存储库,获取最新顶级commit的最终SHA-1,然后编写一个新的{{1}对象。新提交将具有以下属性:

  • tree是根据索引编写的内容
  • parent现在是HEAD中的任何内容(或多或少 - 在进行合并提交时,有些多个父母正在摆弄)
  • authorcommitter这些日期取自当前时间和您的git配置user.nameuser.email,或来自参数(--author)或环境变量(如果设置为覆盖事物
  • 该消息是您在提交消息中编辑的任何内容,或者作为-m参数提供。

所以git写了commit,它产生一个新的,唯一的SHA-1。然后它将SHA-1本身写在某个地方。

分支和HEAD

如果您在分支master&#34;&#34;正如git status所说,这意味着文件.git/HEAD包含文字字符串{{1 }}。这就是git所谓的&#34;间接引用&#34;:一个引用,只是说&#34;去找另一个引用,这里是名称。&#34;通常你在某个分支上,而ref: refs/heads/master是对该分支的间接引用。

分支本身可以以多种不同的方式存储,但最简单的是HEAD中的另一个文件,在本例中是文件.git。如果该文件存在并且您已阅读它,则它将包含类似.git/refs/heads/master的SHA-1。这是当前的提交,也就是git知道你在&#34;上发生了哪些提交,就像5f95c9f850b19b368c43ae399cc831b17a26a5ac git知道你在分支上的方式一样ref: refs/heads/master

要进行 new 提交,git会按上述方式写入提交,从而生成一个新的唯一SHA-1。然后,由于你在分支master上,git只是将新的commit-ID写入master,现在你重新进行新的提交,这是分支的提示{ {1}}。

你也可以拥有一个&#34;分离的HEAD&#34;,尽管听起来像法国大革命中的某些内容 - 只是意味着.git/refs/heads/master 不是是间接引用。相反,master包含原始SHA-1。在这种情况下,为了进行新的提交,git以与以前相同的方式进行提交,但不是更新HEAD,而是将新的commit-ID直接写入HEAD

git reset

因此,考虑到所有这一点,让我们具体看看.git/refs/heads/master做了什么。

如果你进行HEAD重置,git会完全不受影响。这意味着它只更新当前分支。

要更新当前分支,git执行与进行新提交时相同的操作:它查找哪个分支git reset指向,并将新SHA-1写入该引用。如果--soft指向HEAD,则只需将新的SHA-1写入HEAD

git写的SHA-1是你在命令行上提供的那个:

master

您可以通过.git/refs/heads/master(对于git reset --soft @~ # @~ means @~1, which means HEAD~1, aka HEAD^ - 相对参考来查看SHA-1的内容,您必须在 git rev-parse之前执行此操作当然更改HEAD

reset

如果您告诉HEAD使用$ git rev-parse @~ 9c8ce7397bac108f83d77dfd96786edb28937511 也会更新索引。它放入索引的内容来自它将写入分支的提交SHA-1:

git reset

在这里,通过告诉它将--mixed更改为$ git reset --mixed HEAD -- COPYING ,您可以重置以将分支移动到以前没有距离的位置,因此分支根本不会更新;但是HEAD说&#34;从目标修订版HEAD中提取文件-- COPYING的SHA-1,并将该SHA-1放入文件{{1}的索引中}&#34。所以这意味着下一次提交赢得会对文件COPYING进行更改,因为我们已将旧的SHA-1放回索引中。

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

由于您使用了git commit,因此您禁止对索引进行更改,因此git add唯一可以做的就是更改分支提示文件的内容git commit


1 --soft过去只有这三种操作模式。它现在有git reset.git/refs/heads/master加上git reset,它们比简单案例更有效。它有点像关于西班牙宗教裁判所的Monty Python短剧:&#34;我们的三种模式是软,混合,硬和合并。 ... 四个!我们的四个模式是软的,混合的,硬的,合并的,并且保持......&#34;

2 存储库中的对象&#34;永远存在&#34;有一个非常大的例外:未引用的对象,--merge显示为--keep的对象,是垃圾收集的候选对象。未引用的blob,提交等都是完全正常的。他们坐在占用磁盘空间周围(通常很少:对象被压缩存储),这样你就可以恢复一些东西,这样一来,如果git认为它是一个好主意,可以立即收集和丢弃它们起来。

对象被引用&#34; (因此永远存在)当一些外部标签 - 分支名称,标签,--patch或其他任何东西 - 直接或间接指向它们时。分支名称指向该分支上的最尖端提交。那个提交指向它的树,它指向任何子树和blob,所以所有这些都永远存在;并且该提交指向其父提交,因此这些父母将永远保留。每个父提交依次指向自己的父母,这些也永远保留。

当您将分支标签移离它时,提交变为 un -referenced:

git fsck

此处dangling(我们当前的分支)指向HEADA <- B <- C <-- HEAD=master 指向masterC指向C。但是如果我们:

B

我们将B指向A,指向$ git reset --hard HEAD^ 。提交master现在未引用:它已被放弃,最终将被垃圾收集,以及它的树和任何子树和blob。类似的事件发生在例如B上,它执行软重置和新提交,使新的提交A指向C,并且git commit --amend指向D

B

master操作复制然后放弃整个提交序列,生成大量用于垃圾收集的候选对象。这就是为什么悬空物体是正常的。

答案 1 :(得分:1)

索引文件是纯元数据,显示已上演的内容及其文件系统统计信息。当您执行git checkout时,索引将从已提交的树中加载 - 它包含所有这些文件的SHA以及写入工作树的文件系统大小/时间。

当您逐步添加内容时,该内容将被放入repo并将其SHA写入索引文件。

git reset --soft没有触及任何内容,它只会更改HEAD指向的内容。所以git status所要做的就是告诉你索引与HEAD提交或工作树的不同之处。