我在提交后只使用了check out head
。我认为做这个结账实际上不会做任何事情,但我似乎错了。它让我进入一个独立的头部'州。我忽略了这个说明,并继续做了一些额外的提交。邮件更改为' Head已从...'分离我对此感到有些恼火,我找了一种解决方法。我找到的答案是git checkout master
。我这样做了,现在我的最后几次提交都消失了。这里发生了什么?
答案 0 :(得分:4)
我不清楚你做了什么才能进入"超级头部"州。 (通过除分支名称之外的任何标识符来检出提交的任何内容都会这样做。例如,如果您为git checkout
提供标记名称或远程分支名称,如{ {1}},它会分离。同样,如果你通过SHA-1签出一个特定的提交,你将会处于那种模式。你甚至可以在通过branch-name签出提交时故意这样做,使用origin/master
,虽然我想这不是它。)
以下是我喜欢绘制提交图的方法:
--detach
这表示带有一堆提交的...-D-E-F <-- master
\
G <-- HEAD=branch
分支,以及一个名为master
的分支,其上有一个提交(提交branch
),但不在G
上。此处master
有两个未在master
上提交的提交(提交branch
和E
):分支名称F
&#34;指向&#34;提交master
(已提交F
的SHA-1),提交F
个点以提交F
(通过列出{的原始SHA-1 {1}}作为其父级),将E
提交到E
,依此类推。分支名称E
指向提交D
,branch
指向G
。 G
部分表示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
不是列出其子女。您无法通过提交E
,D
或E
找到它;他们中没有一个甚至是相关的。提交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 bar
和HEAD@{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}
或HEAD
时54ce513
都会发生变化,如果您HEAD
,commit
会变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>
进行要提交的提交