从分离的头创建分支后,奇怪的分支名称

时间:2020-01-22 11:51:17

标签: git git-branch git-detached-head

我的Git头部脱落有问题。 我在上一次提交时进行了结帐。 之后,我们进行了提交。 因此,在我为此提交创建分支之后。

git checkout -b detached-head-after-gitlab-crush

因此,在那之后,我进行了更改并提交了更改。

但是现在,当我写git branch时:

* (detached from a71c5ea)
  detached-head-after-gitlab-crush
  master

所以我想了解什么是当前分支以及如何创建。

如何更改最后一个分支,也不能将此当前分支推到原点。

git log --all --decorate --oneline --graph

显示以下结果:

* 548af67 (HEAD) Images were changed, and issue with Printing Gate entry records
* be89a73 (origin/detached-head-after-gitlab-crush, detached-head-after-gitlab-c
* 6979cba Files before solving HEAD detached problem
* fb89a62 Rules added, made some changes which I don't remember
| *   d4183f3 (origin/master, origin/HEAD, master) Merged files
| |\
|/ /
| *   3c3cadc Merge branch 'master' of http://gitlab.sdu.edu.kz/sdu/portal

2 个答案:

答案 0 :(得分:2)

让我们先快速回顾一下您已经知道的事情:

  • 任何Git存储库中的基本存储单位是 commit 。提交内部有一些较小的单元,例如,提交包含文件,有点类似于原子如何容纳质子,中子和电子的方式,但是提交本身就是您应该使用的容器。 (以此类推,我们想做化学,而不是核物理。)

  • 每个提交都有自己的唯一哈希ID。这些哈希ID大而丑陋,人类难以使用,因此Git有时会缩短它们以供显示:例如,您有548af67(对于更长的ID来说是简称),{{1} }(又是40个字符长的缩写),依此类推。这些是从您的be89a73输出中获得的。每个地方的每个Git存储库都同意这些哈希ID是为这些特定的提交保留的,即使该存储库没有 这些提交。

    您可以始终使用原始哈希ID来引用您自己存储库中的任何提交。

  • 提交本身包含:

    • 数据:所有文件的快照。这与先前的提交没有区别。它是每个文件的完整副本。 (这些文件以压缩,冻结,只读,仅Git的格式存储。由于它们是 只读文件,因此可以共享。例如,如果您的大多数提交都有一个git log --all --decorate --oneline --graph文件,在README中只有3个版本在90次提交中,每30次提交更改一次,然后冻结了一种内部Git格式README的副本用于前30次提交,其他服务器则用于接下来的30次提交,依此类推。)

    • 元数据:关于提交的信息 ,例如提交者(姓名和电子邮件地址),时间(日期和时间戳)以及为什么(提交日志消息)。在此元数据中,每个提交都可以列出一些先前提交的原始哈希ID。


    大多数提交仅列出一个先前的提交哈希ID。列出的提交是该提交的 parent ,即紧接在此提交之前的提交。一些提交列出了一个以上的提交,即具有多个父提交。每个非空存储库中的一个提交就是该存储库中的 first 提交,因此列出了 no 父级。

每当Git有权访问一个提交时,Git都可以查看该提交的父对象(或多个父对象),因此可以回溯到上一个提交。这使Git可以访问父提交。因此,现在Git可以找到另一个父级(该父级的父级,即我们刚才提交的祖父母),当然,该提交也有一个父级。因此,Git只需从 last 提交开始并向后进行操作,就可以找到整个历史

分支名称找到特定的提交

但是提交哈希ID look 是随机的,并且是不可预测的。您和Git如何快速,轻松地知道哪个 last 提交?这是分支名称的来源。分支名称(例如READMEmaster)存储一个提交哈希ID。根据定义,该哈希ID是该分支中的 last 提交。

让我们使用大写字母代替实际的提交哈希ID。我们很快就会用完,这是Git不使用简单的大写字母的原因之一,但是对于我们的绘图来说可以。假设我们的存储库确实是新的,并且其中只有三个提交。第一个是提交detached-head-after-gitlab-crush,并且因为它是第一个,所以它没有父项:

A

我们将第二次提交称为A 。它记住第一次提交的哈希ID作为其父对象。因此,我们将说提交B 指向提交B,并像这样绘制:

A

当然,提交A <-B 包含提交C的哈希ID,因此B向后指向C

B

要快速查找A <-B <-C ,Git将其哈希ID存储在名称 C中:

master

(在这一点上,我们有点累了,变得懒惰,并从commit提交到线,而不是箭头来绘制连接。只记得它们仍然是箭头,它们是从孩子身上出来并指向每次提交的 All 部分都被冻结,包括从其中出来的箭头,因此我们不能返回并添加前向指向后来的箭头:我们作出承诺,它有一个或两个向后指向父母的箭头,从那以后我们就一直坚持下去,孩子知道他们的父母是谁,但父母却永远不知道他们的孩子是谁。 )

现在我们有了这个,让我们在此图片中添加另一个分支名称。与其将A--B--C <-- master 拼写为crash而不是将其命名为crush

develop

现在让我们向集合中添加新的提交。我们使用Git中的常规过程来做到这一点。无论Git带有什么哈希ID,我们都将其称为新的提交A--B--C <-- master, develop 。新的提交D将指向现有的提交D,因为我们将通过检出提交C开始进行 make D的工作。因此,一旦完成C,它就会像这样:

D

A--B--C \ D 上下指向DC指向C,依此类推。

这是B进入的地方

我们现在有问题。我们有两个分支名称。 哪个应该记得新的提交HEAD

要告诉Git哪一个,我们将用大写字母将特殊名称D附加到两个现有分支名称之一中。假设在进行新的提交HEAD之前有这种安排:

D

然后我们将得到此信息:

A--B--C   <-- master (HEAD), develop

但是,如果那不是我们想要的,我们应该首先A--B--C <-- develop \ D <-- master (HEAD) 。然后,我们将得到:

git checkout develop

当我们进行新的提交A--B--C <-- master, develop (HEAD) 时,我们将得到:

D

无论哪种方式,我们都会得到相同的 commits 集。区别在于,当Git 进行新提交时,它将新提交的哈希ID of 写入附有名称A--B--C <-- master \ D <-- develop (HEAD) 的分支名称。 。然后,该分支名称会自动指向新的提交。

实际上,新提交的 parent 是之前指向的任何提交HEAD的分支名称。根据定义,这就是我们已经完成的 commit 。我们使用了HEADgit checkout master,但是无论哪种方式,我们都选择了现有的提交git checkout develop

C未附加到分支名称时,发生分离的HEAD

现在我们有了:

HEAD

我们可以继续进行更多的新提交:

A--B--C   <-- master
       \
        D   <-- develop (HEAD)

例如。但是,如果愿意,我们可以摘下头。 Git有一种模式,我们可以使A--B--C <-- master \ D--E--F <-- develop (HEAD) 直接指向 任何现有的提交。举例来说,由于某种原因,我们想使HEAD指向直接提交HEAD

E

我们现在可以进行 new 提交(我们将其称为A--B--C <-- master \ D--E <-- HEAD \ F <-- develop ),这将指向现有的提交G。 Git会将新提交的哈希ID(无论可能是多少)写入分离的HEAD中,从而为我们提供:

E

此模式本质上没有错误,但是以后会使事情变得更难。假设我们要再次查看提交A--B--C <-- master \ D--E--G <-- HEAD \ F <-- develop 。我们可以运行C。这会将名称git checkout master再次附加到名称HEAD

master

您如何找到提交A--B--C <-- master (HEAD) \ D--E--G \ F <-- develop ?我们可以轻松找到G:这是我们当前的提交,名称CHEAD都可以找到它。我们可以从master中找到B,方法是返回上一个。我们无法从C中找到D,但是我们可以从名称 C中找到F。从develop,我们可以退回到F,再从那里回到E。但是我们不能前进前进。 Git的所有箭头都指向后方。找不到提交D的简便方法。

解决方案是在我们从G切换到之前添加一个新的分支名称。这就是您先前创建名称G时所做的。如果我们知道detached-head-after-gitlab-crush的哈希ID(例如,如果仍在屏幕上),我们可以用另一种方法做同样的事情:

G

可以解决问题:

git branch save-it <hash-of-G>

现在我们可以使用提交A--B--C <-- master (HEAD) \ D--E--G <-- save-it \ F <-- develop 一段时间,甚至可以进行一个新的提交C,使H更改为指向master

H

返回到A--B--C--H <-- master (HEAD) \ D--E--G <-- save-it \ F <-- develop 所需要做的就是G,它将git checkout save-it附加到名称HEAD(仍然指向save-it)上:

G

您需要做的就是找出为什么让HEAD脱离的原因

尽管Git中的分离式HEAD模式从根本上没有什么错,但很难使用。您必须手动创建和/或更新分支名称才能记住您的提交。

只要您告诉Git,Git便会进入此HEAD分离模式:

A--B--C--H   <-- master
       \
        D--E--G   <-- save-it (HEAD)
            \
             F  <-- develop
例如,

说“我想使用git checkout --detach master 标识的提交,但是我想在分离的HEAD模式下使用它。”

每当您要求

Git还将 分离HEAD时(通过新的Git 2.23和更高版本的master签出(或切换到)原始哈希ID或不是分支名称的任何名称。其中包括远程跟踪名称(如git switch)和标签名称(如origin/master(如果已创建标签)。

某些命令(特别是v1.2)会在运行时临时分离HEAD。如果他们不能完成,从而使您处于重新部署的中间,他们将停止并处于分离的HEAD模式。然后,您必须选择是完成变基还是以git rebase完全终止变基。 (如果您不想执行任何一项操作,则可能会有些卡住:您确实必须执行其中一项。)

所以:找出为什么您继续进入这种分离的HEAD模式。你在做什么导致了它?您可以使用git rebase --abortgit branch(或在Git 2.23及更高版本中,git checkout -b中用git switch -c代表提交)创建提交的新分支名称,以在以下情况下解决问题:您处于独立的HEAD模式,或者您不需要记住自己现在的位置-如果您故意查看历史记录,例如,可以并且可能使用c,只需使用git loggit checkout将HEAD重新附加到现有分支名称即可。但是,除了那些要做想要分离的HEAD(使用带标记的提交或查看历史提交)的特殊情况,或者像在基础上工作的情况下,您处于分离的HEAD模式直到完成操作后,您可能不想在分离的HEAD模式下工作。所以,不要那样做!

答案 1 :(得分:1)

从git branch命令的结果来看,您处于提交的分离头中,并且您仅创建了一个没有分离的提交的新分支。

要从上一次提交创建分支,您可以使用3种方法:

  • 您可以通过哈希创建分支:

git branch分支名称sha1-of-commit

  • 或使用符号引用:

git branch分支名称HEAD〜5

  • 要在创建分支时签出分支,请使用

git checkout -b分支名称sha1-of-commit或HEAD〜3