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

Strange branch name after creating branch from 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

根据您的 git 分支命令的结果,您处于提交的分离头部,并且您只创建了一个没有分离提交的新分支。

要从之前的提交创建分支,您有 3 种方法:

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

git 分支分支名称 sha1-of-commit

  • 或者使用符号引用:

git 分支分支名称 HEAD~5

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

git checkout -b branchname sha1-of-commit 或 HEAD~3

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

  • 任何 Git 存储库中的基本存储单元是 commit。提交中有更小的单元——例如,提交包含文件,有点类似于原子如何容纳质子、中子和电子——但提交本身是你应该使用的容器。 (在这个类比中,我们想做化学,而不是核物理。)

  • 每个提交都有自己唯一的哈希 ID。这些散列 ID 又大又丑,人类很难使用,因此 Git 有时会缩短它们以供显示:例如,您有 548af67(它是更长的东西的缩写), be89a73(又是 40 个字符长的缩写),依此类推。我从你的 git log --all --decorate --oneline --graph 输出中得到了这些。每个 Git 存储库都同意这些哈希 ID 是为 这些特定的 提交保留的,即使该存储库 没有 这些提交.

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

  • 提交本身包含:

    • data: 所有文件的快照。这与之前的提交没有区别。它是每个文件的完整副本。 (这些文件以压缩、冻结、read-only、Git-only格式存储。因为它们read-only,它们可以是shared。例如,如果你的大多数提交都有一个 README 文件,并且在 90 次提交中只有三个 versions of README ,每 30 次提交更改一次,然后 README 的一个内部 Git-format 冻结副本服务于前 30 次提交,另一台服务器为接下来的 30 次提交服务,依此类推。)

    • 元数据: 信息 关于 提交,例如提交人(姓名和电子邮件地址),何时提交(date-and-time 邮票),以及原因(提交日志消息)。在此元数据中,每个提交都可以列出一些先前提交的原始哈希 ID。


    大多数提交只列出一个先前的提交哈希 ID。列出的提交是此提交的 parent,即紧接在此提交之前的提交。一些提交列出了不止一个以前的提交,即有多个 parent。每个 non-empty 存储库中的一次提交是该存储库中的 第一次 提交,因此列出 no parent.

只要 Git 可以访问一个提交,Git 就可以查看该提交的 parent(或 parents),因此可以倒退到前一个提交犯罪。这使 Git 可以访问 parent 提交。所以现在Git可以找到另一个parent——这个parent的parent,即parent的大parent我们刚才的提交——当然那个提交有一个 parent。因此 Git 可以找到 整个历史记录 只需从 最后一个 提交开始并向后工作即可。

分支名称找到特定的提交

但是提交哈希 ID 看起来 是随机的,并且是不可预测的。您和 Git 如何快速轻松地知道哪个提交是 最后一个 提交?这就是 分支名称 的用武之地。像 masterdetached-head-after-gitlab-crush 这样的分支名称存储 one 提交哈希 ID。根据定义,该哈希 ID 是该分支中的 last 提交。

让我们使用大写字母代表实际的提交哈希 ID。我们会 运行 非常快,这是 Git 不使用简单大写字母的原因之一,但它对我们的绘图来说没问题。假设我们的存储库是全新的,并且其中只有三个提交。第一个是提交 A,因为它 第一个,所以它没有 parent:

A

我们将调用第二个提交 B。它会记住第一个提交的哈希 ID 作为它的 parent。所以我们会说 commit B 指向 commit A,然后画成这样:

A <-B

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

A <-B <-C

为了快速找到 C,Git 将其哈希 ID 存储在 name master:

A--B--C   <-- master

(此时我们有点累了,开始偷懒了,将提交与提交之间的连接绘制为线,而不是箭头。请记住,它们仍然是箭头,它们来自 child 并指向 parent,永远不会从 parent 到 child。每个提交的 All 部分将一直冻结,包括从它出来的箭头,所以我们不能返回并稍后添加一个 forward-pointing 箭头:我们提交,它的 parent 有一个或两个 backwards-pointing 箭头,从那时起我们就坚持了。child人知道他们的 parent 是谁,但 parent 永远不知道他们是谁ir children are.)

现在我们有了这个,让我们为这张图片添加另一个分支名称。与其将 crash 拼写为 crush,不如将其命名为 develop:

A--B--C   <-- master, develop

现在让我们向我们的集合添加一个新的提交。我们使用 Git 中的常规流程来做到这一点。我们将调用新的提交 D,不管哈希 ID Git 是什么。新提交 D 将指向现有提交 C 因为我们将通过检查提交 C 开始 make D .所以一旦 D 被制作出来,它就会看起来像这样:

A--B--C
       \
        D

D 将 up-and-left 指向 CC 指向 B,依此类推。

这就是 HEAD 发挥作用的地方

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

为了告诉 Git 哪一个,我们将 附加 特殊名称 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,我们将得到:

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

无论哪种方式,我们都会得到相同的一组 commits。不同之处在于,当 Git 进行 新提交时,它将新提交的 的哈希 ID 写入 branch-name ] 名字 HEAD 是 attached-to。该分支名称然后自动指向新的提交。

事实上,新提交的parent是之前提交HEAD的分支名称pointed-to。根据定义,这就是我们提出的 commit。我们使用 git checkout mastergit checkout develop,但无论哪种方式,我们都选择了现有提交 C.

HEAD 没有附加到分支名称

时,会出现 分离的 HEAD

现在我们有:

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

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

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

例如。但如果我们愿意,我们可以把我们的头拿下来。 Git 有一种模式,我们可以使 HEAD 指向 直接 到任何现有的提交。比方说,出于某种原因,我们想让 HEAD 直接指向提交 E:

A--B--C   <-- master
       \
        D--E   <-- HEAD
            \
             F  <-- develop

我们现在可以进行 提交——我们称之为 G——这将指向现有的提交 E。 Git 会将新提交的哈希 ID(无论它是什么)写入分离的 HEAD,给我们:

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?我们可以轻松找到 C:这是我们当前的提交,名称 HEADmaster 都可以找到它。我们可以通过返回一个从 C 中找到 B。我们无法从C中找到D,但我们可以从namedevelop中找到F。从 F,我们可以回到 E,然后从那里回到 D。但是我们不能前进。 Git 的所有箭头都指向后方。不再有简单的方法来查找提交 G.

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

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):

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

你需要做的是找出为什么你的 HEAD 总是分离

虽然 Git 中的分离 HEAD 模式从根本上没有错,但很难使用。您必须手动创建 and/or 更新分支名称以记住您的提交。

Git 将在您告诉它时进入此分离的 HEAD 模式:

git checkout --detach master

例如说"I want to use the commit identified by master, but I want to do that in detached HEAD mode"。

Git 将 分离 HEAD 每当你要求它检出(或切换到新的 Git 2.23 和更高版本 git switch) 通过原始哈希 ID 或任何 而不是 分支名称的名称提交。这包括 remote-tracking 名称 ,如 origin/master,以及标签名称,如 v1.2,如果你创建了标签。

一些命令,特别是 git rebase,将 暂时 在它们 运行 时分离 HEAD。如果他们不能完成,那么哟处于变基的中间,他们将停止并让您处于这种分离的 HEAD 模式。然后你必须选择是完成 rebase,还是用 git rebase --abort 完全终止它。 (如果你不想做其中任何一个,你就会有点卡住了:你真的必须做其中一个。)

所以:找出为什么你总是进入这种分离的 HEAD 模式。你在做什么导致它?您可以使用 git branchgit checkout -b(或在 Git 2.23 及更高版本中,git switch -cc 代表创建)为提交创建新的分支名称当你处于分离的 HEAD 模式时修复问题,或者如果你不需要记住你现在在哪里 - 如果你正在故意查看历史提交,你可以并且可能已经找到使用 git log 例如——只需使用 git checkoutgit switch 来 re-attach 你的 HEAD 到一个现有的分支名称。但除了那些你 do 想要分离的 HEAD(使用标记的提交或查看历史提交)的特殊情况,或者像 working-on-rebase 你处于分离状态的情况HEAD 模式直到你完成,你可能不想要 在分离的 HEAD 模式下工作。所以,不要那样做!