嘿git,上次提交的内容怎么了?

时间:2014-04-18 19:24:52

标签: git commit git-checkout

我在提交后只使用了check out head。我认为做这个结账实际上不会做任何事情,但我似乎错了。它让我进入一个独立的头部'州。我忽略了这个说明,并继续做了一些额外的提交。邮件更改为' Head已从...'分离我对此感到有些恼火,我找了一种解决方法。我找到的答案是git checkout master。我这样做了,现在我的最后几次提交都消失了。这里发生了什么?

2 个答案:

答案 0 :(得分:4)

我不清楚你做了什么才能进入"超级头部"州。 (通过除分支名称之外的任何标识符来检出提交的任何内容都会这样做。例如,如果您为git checkout提供标记名称或远程分支名称,如{ {1}},它会分离。同样,如果你通过SHA-1签出一个特定的提交,你将会处于那种模式。你甚至可以在通过branch-name签出提交时故意这样做,使用origin/master,虽然我想这不是它。)

以下是我喜欢绘制提交图的方法:

--detach

这表示带有一堆提交的...-D-E-F <-- master \ G <-- HEAD=branch 分支,以及一个名为master的分支,其上有一个提交(提交branch),但不在G上。此处master有两个未在master上提交的提交(提交branchE):分支名称F&#34;指向&#34;提交master(已提交F的SHA-1),提交F个点以提交F(通过列出{的原始SHA-1 {1}}作为其父级),将E提交到E,依此类推。分支名称E指向提交Dbranch指向GG部分表示git的D文件 - 这就是git记录您所在的分支 - 表示您在分支HEAD=上。

在此状态下,如果您进行新的提交,则会像往常一样添加。如果我们标记新提交.git/HEAD,则提交branch的父提交是提交H,并且H更新为指向提交G给予:

branch

当你在这个&#34;分离的HEAD&#34;另一方面,H不包含分支的名称。相反,它包含一个原始SHA-1,用于标识&#34;当前提交&#34;。我们假设您决定查看上面的提交...-D-E-F <-- master \ G-H <-- HEAD=branch

HEAD

E标识提交$ git checkout master^ ,并且不是分支名称,因为它中包含master^个字符,因此这会让您提交E(和我必须使用另一行来提升^,以便我可以用E的箭头绘制:

F

现在,如果你处于这种状态并添加新的提交,它们将以与往常相同的方式添加 - 但是没有要更新的分支,因此git将每个新提交的ID直接写入其{{ 1}}文件。我们现在说我们创建了提交HEAD

        F   <-- master
       /
...-D-E     <-- HEAD
     \
      G-H   <-- branch

提交HEAD已提交I作为其父级, F <-- master / ...-D-E-I <-- HEAD \ G-H <-- branch 现在指向提交I。但是,如果您决定再次查看E

HEAD

您现在如何找到提交I?它唯一的名称branch,在上述结帐后,图片现在看起来像这样:

$ git checkout branch

(注意:如果你在&#34;分离的HEAD&#34;状态下进行了多次提交,那么他们就会提交I。这并不会改变你需要的任何内容在下面做,除了要做一些工作以确保你有#34; last&#34;提交,HEAD。我只使用下面的一个提交 F <-- master / ...-D-E-I \ G-H <-- HEAD=branch 。)

没有标签可以让您找到提交I-J-K。您无法通过提交K找到它:I的父级仍然只有I,而E 不是列出其子女。您无法通过提交EDE找到它;他们中没有一个甚至是相关的。提交F已被放弃&#34;,大约一个月后它将真正消失。

但是,作为Nikhil Gupta noted 仍然是一种查找提交G的方式(直到大约一个月收集):它存储在git称之为&#34; reflog&#34;。 (或者更确切地说,&#34; a&#34; reflog:每个分支都有一个reflog,加上H的一个大的。)事实上,它是reflog本身的保持提交I约一个月。放弃的存储库对象仍然可以通过reflog引用进行命名,但这些reflog条目将过期。一旦提交I的reflog条目到期,如果你没有附加一个更永久的链接,git的垃圾收集过程将真正删除它。

因此,要恢复提交,请使用HEAD查看I reflog。这将显示I之类的内容。您可以向各种git命令提供git reflog或部分SHA-1,HEAD 1 ,包括54ce513 HEAD@{3}: commit: foo the barHEAD@{3}

获得所需的SHA-1(或54ce513之类的名称)之后,您可以附加名称 - 标签或分支名称 -

git log

或:

git show

现在您可以引用名为HEAD@{3}$ git tag restore 54ce513 # or git tag oops HEAD@{3} 的提交$ git branch experiment 54ce513 。如果你把它作为分支名称,你现在有一个普通的普通分支:

I

一旦它有一个方便的名称,你可以用它做任何你想做的事情。或者你可以(大约30天左右)通过reflog-name或raw SHA-1来引用它。例如,您可以将提交restore中的更改复制到分支experiment上的新提交(其中 F <-- master / ...-D-E-I <-- experiment \ G-H <-- HEAD=branch 仍然指向):

I

branch基本上意味着&#34;找出我在该提交中所做的事情,并在当前分支上再次执行#34;)。假设你附加了一个名称来提交HEAD,这会给你:

$ git cherry-pick 54ce513

从提交cherry-pick到提交I的差异与从 F <-- master / ...-D-E-I \ G-H-J <-- HEAD=branch H的差异相同 2 。< / p>


1 字符串J是&#34;相对引用&#34;:&#34;其中E 3更改前&#34;。像I这样的SHA-1是&#34;绝对的&#34;它永远不会改变,并且(与完整的40个字符的SHA-1的其余部分)&#34;真正的名字&#34;提交。由于每次HEAD@{3}HEAD54ce513都会发生变化,如果您HEADcommit会变checkout - 当然git checkout HEAD@{3} 包含HEAD@{4}。如果你HEAD@{0},那总是有效的 - 当然,假设54ce513是实际的SHA-1(我特别提到了这个)。

2 更具体地说,这些提交之间的git checkout 54ce513显示相同的更改,但有一个例外:因为cherry-pick使用git的合并机制,git有时可以告诉你已经有一些变化,并避免重复它们。

答案 1 :(得分:2)

一个分离的头是完全正常的,意味着你的副本直接指向提交而不是分支中的symbolic-ref。您可以在here上看到更多详细信息。

你是如何摆脱困境的?好吧,使用git reflog查看您在git checkout master之前执行的操作,然后使用git merge <sha1>进行要提交的提交