检出旧的提交并保持master分支的头吗?


85

当前要切换到另一个git commit(在同一分支上...实际上是在master分支上!),我正在执行命令

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

现在,每次执行此操作时,git都会告诉我,我现在头顶分离。我该如何进行较早的提交,并且仍然将头保持在同一分支上?


1
执行该命令后,您的HEAD ref更改为该提交。希望HEAD也指向其他地方是没有意义的。
Greg Hewgill

从我从git的消息中了解到,它没有指向任何地方,这是不可取的。
2011年

1
该消息的Git显示了当你检查出具体的承诺一样,说:“头现在ea3d5ed ......”,它告诉你HEAD指向的地方。它只是指向HEAD之外没有其他任何名称的地方(此刻,因为git checkout到另一个提交或分支的名称将HEAD移到新的位置)。
Greg Hewgill

给出的答案是否足以解释这一点,还是我们可以更清楚地了解某些事情?如果无法回答您的问题,我很乐意为您澄清答案。
布莱恩·坎贝尔

如果您是来这里寻找一种在保持HEAD完全不变的情况下签出另一个提交的方法(例如,为了恢复到较早的提交):git revert --no-commit 0766c053..HEAD将执行此操作,0766c053您要检出的提交在哪里。这来自stackoverflow.com/a/21718540/525872
乔·利斯

Answers:



80

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

但是,如果您希望从那时开始进行更多提交,则应创建一个分支。如果您进行的提交没有被分支引用,则它们很容易丢失,最终将被git的垃圾收集器清除,因为没有东西引用它们。您可以通过运行以下命令创建一个新分支:

git checkout -b newbranch ea3d5ed

为了帮助可视化,下面是一些图表,它们说明了在分离的头上进行操作与在分支上进行处理有何不同。

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

美国广播公司
*-*-* <-主<-头

现在,如果我们提交,git将创建一个以C作为父提交的提交(因为这是当前提交,HEAD通过via指向master),并将更新master以指向该新提交。我们所有的提交现在都在master,并通过HEAD指向新的提交master

A B C D
*-*-*-* <-主<-头

现在,让我们检查一下B,给我们一个独立的HEAD

A B C D
*-*-*-* <-主
   ^
    \-头

这里一切正常。我们可以查看所有文件,构建程序,对其进行测试,等等。我们甚至可以创建新的提交。但是,如果这样做,就没有分支,因此我们不能将任何分支指向该新提交。唯一指向它的是HEAD

A B C D
*-*-*-* <-主
    \
     * <-头
     Ë

如果我们以后决定master再次签出,将没有任何内容涉及E。

A B C D
*-*-*-* <-主<-头
    \
     *
     Ë

由于没有任何引用,因此很难找到它,并且git认为没有引用的提交将被放弃(如果您变基,压缩补丁或进行其他有趣的历史操作,则它们很常见;它们通常表示被丢弃的补丁您不再在意的)。在一定时间后,git会将其视为垃圾,在下次运行垃圾收集时将其丢弃。

因此,如果您感觉要进行更多提交,而不是签出裸露的修订版本并得到一个独立的负责人,则应使用git checkout -b branch B创建一个分支并将其签出。现在,您的提交不会丢失,因为它们将包含在一个分支中,您可以轻松地引用它,并在以后合并。

A B C D
*-*-*-* <-主
   ^
    \-分支<-HEAD

如果您忘记执行此操作,并且在分支上创建了提交,则无需担心。您可以使用来创建引用主要修订版的分支git checkout -b branch。如果您已经切换回master分支,并且意识到自己忘记了一个杂项提交,则可以使用来找到它git reflog,它将向您显示HEAD过去几天中提交指向的历史。仍然保留在reflog中的所有内容都不会被垃圾回收,通常,引用在reflog中至少保留30天。


对我来说还不是很清楚,为什么当您结帐旧提交时,脑袋就会松脱。也许问题出在独立的头意味着什么?另一方面,如果检出带有分离头的旧提交只会丢失它,为什么有人会这样做呢?只是为了弄乱并尝试一下?为什么git首先允许它?
吞噬了极乐世界

5
@devoured极乐世界“分离头”意味着您的HEAD引用直接指向提交的SHA-1,而不是指向分支,而分支又指向提交。因为您的头没有引用分支,所以Git不知道在添加新提交时要更新哪个分支。正如我在回答开始时所解释的那样,如果您要返回用于构建或测试代码的旧版本,则可以拥有一个独立的头。您总是可以使用git checkout master或类似符号返回分支。这只是一个问题,如果您在脑袋分离的情况下进行操作。
布莱恩·坎贝尔

@BrianCampbell-在分支(您的头当前所在的位置)上进行提交后,是否将分支合并到B中并将B合并到master中。接下来您该怎么办?
amey1908 2014年

您的解释使我产生了许多其他“点击”。谢谢。我现在可能终于了解了git ...

8

如果您只是想返回到较早的提交而不进行任何更改就可以使用它,则可以执行

git co <previous-commit-id>

执行此命令后,您将位于一个名为“(无分支)”的分支上。

通过以下方式确认

git br

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

git co <the-branch-you-were-on>

“(无分支)”将被自动删除。这样,您无需创建临时分支。


5

Git的HEAD只是一个指示工作目录中内容的指针。如果要检出不是分支头的提交,只需要将HEAD重定向到该提交即可。没有办法解决。您可以在该提交处创建一个临时分支,但是HEAD仍将与master分开。

这就是简短的解释。以下详细信息将有助于理解HEAD和master的不同之处:

通常情况如下:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

就是说:“ C的父是B,B的父是A。分支master指向C,目前我已检出master的内容。另外,当我提交时,将更新master。”

在此隐含一些假设,这些假设对于全面理解提交图是必需的。即,提交仅引用其父级,分支的内容是可以通过跟随父级链接到达的那些提交(并且仅是那些提交)。工作树(未修改)的内容和索引必须与HEAD命名的提交相对应,无论是间接(“符号”)还是直接(“分离”)。

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

C ← refs/heads/master 
↓
B ← HEAD
↓
A

现在,由于您正在寻找旧的东西,因此您已经将分支机构抛在了后面。完全可以,“冷静的头脑”建议会冷静地告诉您(强调我的意思):

您可以环顾四周,进行实验性更改并将其提交,也可以放弃在此状态所做的任何提交,而不会通过执行另一次签帐而影响任何分支

另一方面,在重置分支的同时也会使HEAD到达需要的位置,但效果会大不相同!

C
↓
B ← refs/heads/master ← HEAD
↓
A

提交C将变为垃圾,因为您已经声明不希望它成为master分支的一部分。

简而言之,您要做的就是了解git对“ HEAD”的含义-它在所在的位置,而不是任何给定分支的位置。并且,如果所在的位置与分支所在的位置不同,则别无选择,只能使用分离的HEAD。

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


1

这个问题有点含糊,但是如果您只想更改工作树中的文件,则只需执行以下操作:

git checkout [commit|branch] -- .

然后,您可以进行更改并根据需要创建新的提交。有时候这很有用。


0

我想我理解你的问题。这是我发现要解决的问题。而且它没有GUI解决方案,您只能使用命令来解决它,这真的很简单。

步骤1:为您要返回的旧提交创建标签。

像标签v2.0

步骤2:git checkout v2.0

就是这样,现在您的HEAD指向“ v2.0”提交,但master仍指向最后一次提交。

C:\Program Files\Git\doc\git\html\git-checkout.html 本文件可能对您有帮助

或输入git help <checkout>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.