签出旧的提交并保持主分支的头部?

dev*_*ium 85 git version-control

目前切换到另一个git提交(在同一个分支上......实际上,在主分支上!),我正在执行命令

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a
Run Code Online (Sandbox Code Playgroud)

现在,每次我这样做,git告诉我,我现在有一个超然的头.我如何进行较早的提交仍然保持在同一分支上?

Nic*_*eri 191

大多数时候,当我这样做时,我会结账到临时分支:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a
Run Code Online (Sandbox Code Playgroud)

然后,在我完成后,我只删除分支


Bri*_*ell 78

这取决于您在签出提交时要执行的操作.如果您所做的只是检查它以便您可以构建或测试该修订版,那么使用分离的头部没有任何问题.只需记住在进行任何提交之前检查一个实际的分支(git checkout master例如),这样就不会创建任何分支中未包含的提交.

但是,如果您希望从该点开始提交更多提交,则应创建一个分支.如果你做了一个没有被分支引用的提交,它们很容易丢失,最终会被git的垃圾收集器清理掉,因为没有任何东西引用它们.您可以通过运行以下命令来创建新分支:

git checkout -b newbranch ea3d5ed
Run Code Online (Sandbox Code Playgroud)

为了帮助可视化,这里有一些图表,展示了在分离头上工作与在分支上工作的不同之处.

让我们从3个提交开始master,A,B和C. master是当前分支,所以HEAD指向master,指向提交C.

A  B  C
*--*--* <-- master <-- HEAD

现在,如果我们提交,git将创建一个C作为父项的提交(因为这是当前提交,从HEADvia 指向master),并将更新master为指向新提交.我们所有的提交现在都在master,并HEAD指向新的提交master.

A  B  C  D
*--*--*--* <-- master <-- HEAD

现在让我们看看B,给我们一个超然的HEAD.

A  B  C  D
*--*--*--* <-- master
   ^
    \-- HEAD

这里的一切都很好; 我们可以查看所有文件,构建我们的程序,测试它等等.我们甚至可以创建新的提交; 但是如果我们这样做,那么我们就没有分支,所以我们不能指出任何分支在新的提交.唯一指向它的是HEAD:

A  B  C  D
*--*--*--* <-- master
    \
     * <-- HEAD
     E

如果我们以后决定master再次退房,那么将无法提及E.

A  B  C  D
*--*--*--* <-- master <-- HEAD
    \
     *
     E

因为没有任何东西可以引用它,所以很难找到,并且git认为没有被引用的提交被放弃(如果你重新加入,或者压缩补丁,或者做其他有趣的历史操作,它们会发生很常见;它们通常代表废弃的补丁你不再关心的).经过一段时间后,git会认为它是垃圾,下次垃圾收集运行时会被丢弃.

因此,如果您觉得要进行更多提交,则应该使用git checkout -b branch B创建分支并检查它,而不是检查一个简单的修订版并获得一个独立的头部.现在你的提交不会丢失,因为它们将被包含在一个分支中,你可以很容易地引用它,并在以后合并.

A  B  C  D
*--*--*--* <-- master
   ^
    \-- branch <-- HEAD

如果您忘记这样做,并在分支机构上创建提交,则无需担心.您可以创建一个引用头部修订的分支git checkout -b branch.如果您已经切换回master分支,并意识到您忘记了一个迷路提交,您可以使用它来查找它git reflog,它将显示HEAD过去几天提交的指向的历史记录.仍然存在于reflog中的任何内容都不会被垃圾回收,并且通常引用会在reflog中保留至少30天.

  • @devoured elysium"detached head"意味着你有一个`HEAD`引用直接指向提交的SHA-1,而不是指向一个分支,而分支又指向提交.因为你的头部没有引用分支,所以当你添加新的提交时,Git不知道要更新的分支.正如我在回答的开头所解释的那样,如果你要回到旧版本来构建或测试代码,那么拥有一个独立的头部是完全没问题的.你可以随时使用`git checkout master`等回到分支机构.如果你有一个超然的头,你只会犯一个问题. (5认同)

Zac*_* Xu 7

如果您只想返回之前的提交以使用它而不进行任何更改,您可以这样做

git co <previous-commit-id>
Run Code Online (Sandbox Code Playgroud)

在此命令之后,您将在一个名为"(无分支)"的分支上.

通过确认

git br
Run Code Online (Sandbox Code Playgroud)

使用此前提交的代码后,您可以切换到您所在的分支

git co <the-branch-you-were-on>
Run Code Online (Sandbox Code Playgroud)

"(无分支)"将自动删除.这样您就不需要创建临时分支.


Jos*_*Lee 5

Git的HEAD只是一个指针,指出工作目录中的内容.如果要查看不是分支头的提交,只需将HEAD重定向到指向该提交即可.没有办法绕过它.您可以在该提交中创建一个临时分支,但HEAD将被引导远离master.

这是简短的解释.下面的详细内容有望帮助理解HEAD和master如何不同:

通常,事情看起来像这样:

C ? refs/heads/master ? HEAD 
?
B
?
A
Run Code Online (Sandbox Code Playgroud)

也就是说:"C的父级是B,B的父级是A.分支大师指向C,我目前已经检查了master的内容.另外,当我提交时,主人应该更新."

在此中隐含了一些假设,这些假设对于彻底理解提交图是必需的.也就是说,提交只引用它们的父项,而分支的内容是那些可以通过跟随父链接到达的提交(并且只有那些提交).工作树和索引的(未修改的)内容必须对应于HEAD命名的提交,间接("符号")或直接("分离").

因此,如果要签出旧提交,则必须更新HEAD以指向所需的提交.git-checkout这样做:

C ? refs/heads/master 
?
B ? HEAD
?
A
Run Code Online (Sandbox Code Playgroud)

现在,你已经离开了你的分支,因为你正在寻找旧的东西.这是完全可以的,因为"独立的头"建议平静地告诉你(强调我的):

您可以环顾四周,进行实验性更改并提交它们,并且您可以放弃在此状态进行的任何提交,而不会通过执行另一次检出来影响任何分支.

另一方面,虽然重置你的分支也会在需要的地方获得HEAD,但它会产生非常不同的效果!

C
?
B ? refs/heads/master ? HEAD
?
A
Run Code Online (Sandbox Code Playgroud)

提交C将变为垃圾,因为您已声明您不希望它再次成为主分支的一部分.

简而言之,你所要做的就是通过"HEAD"理解git的意思 - 它就在所在的位置,而不是任何给定分支的位置.如果所在的地方与分支的位置不同,则别无选择,只能使用分离的HEAD.

(也许还会查看GitHub,gitk或gitweb以浏览提交历史记录,如果您的HEAD出轨仍然让您烦恼.)