为什么有些提交属于没有分支?

时间:2018-04-24 21:09:31

标签: git apache github branch commit

我遇到过一些提交不属于Git存储库中的任何分支。例如,以下提交被标记为Apache Commons CSV的发布,但它不属于任何分支:

https://github.com/apache/commons-csv/commit/0fbd1af5e3bd70454d5e398493a5c983aead2b67

其父提交属于master。

https://github.com/apache/commons-csv/commit/7688fbc3f9f5acf73d3c5018dd83310f7580d02e

你有可能帮我理解这个吗?

2 个答案:

答案 0 :(得分:6)

这种情况在Git中是正常的,它使用与大多数传统版本控制系统(VCS)截然不同的分支。事实上,这里隐藏着一个相当深刻的哲学问题:见What exactly do we mean by "branch"?

分支名称标识提示提交

在大多数VCS中,分支的名称很重要,甚至可能是关于分支的最重要的事情。在Git中不是这样:在Git中,分支名称的价值非常小(无论如何都是Git本身)。对Git来说,重要的是提交。提交是永久性的,大部分是永久性的和不可变的:一旦制定,就不能改变提交。但是每个提交的真实名称是一个可怕的,不可思议的,不可发音的,不可能记住的数字和字母串,例如fe0a9eaf31dd0c349ae4308498c33a5c3794b293。这些对人类不利,因此Git允许我们使用名称代替这些原始哈希ID。

每次提交的另一个重要事项是,任何一个提交都存储真实名称 - 另一个提交的哈希ID,我们称之为提交的前身。我们说这个提交指向其父级。 1 如果我们采用一串不可发声的哈希ID并将它们放在&#34中;大多数祖父母 - "到了#34;大多数孩子"顺序,我们得到类似的东西:

... <-26e4... <-8b02... <-fe0a...

这些提交中最像子项的获取分支名称,然后名称指向 last 提交:

... <-26e4... <-8b02... <-fe0a...   <--master

Git使用最后一个(或 tip )提交来查找其父级,然后使用父级在整个存储库中查找祖父级,依此类推。但是因为哈希ID看起来是随机的 - 并且故意几乎不可能预测 - 即使Git本身想要一个名称,它可以通过它找到链中的 last 提交。这个哈希ID特别重要,因为Git使用该提交来查找其余的提交。这给了我们这样的图片:

          o--o   <-- branch1
         /
...--o--o
         \
          o--o--o   <-- branch2

(我只是停止绘制箭头的内部向后方向,并用每个提交的圆点替换哈希ID)。

中间行的提交有点令人费解,但是他们在哪个分支? Git的答案是他们在两个分支上。 Git提交不属于属于首次提交的分支的提交,而是属于每个分支 - 井,每个分支名称 - 返回到它。

要向某个分支添加新提交,您git checkout该分支会照常工作,git add,并运行git commit。这会写出一个新的提交,指向当前提交作为其父提交:

               o   (new!)
              /
          o--o   <-- branch1 (HEAD)
         /
...--o--o
         \
          o--o--o   <-- branch2

然后,无论将提交的哈希ID分配给 new 提交,Git都会将哈希ID 写入分支名称。要知道要更新的哪个名称,Git会将您的HEAD附加到其中一个分支名称。一旦安全地存储了新提交的哈希,我们就可以将更新的图片绘制为:

          o--o--o   <-- branch1 (HEAD)
         /
...--o--o
         \
          o--o--o   <-- branch2

这是分支增长的正常方式之一。

1 孩子记得父母,而不是相反。由于提交是不可变的,因此这是必要的。就像人类父母和孩子一样,父母在孩子被创建时存在,但是在父母被创建时孩子还不存在。由于提交只能记住过去,父母不能回忆起他们的孩子。

标签还标识提交

标记名称(如分支名称)只是直接指向提交。但是,与分支名称不同,Git不会自动更改标记名称,使其指向任何其他提交。事实上,你通常不应该这样做 - 不是它会打破你自己的Git,但它可能会打破其他人对你的Git存储库的期望。一旦他们有了tag-name-to-hash-ID映射,他们可能会认为他们从那时起就拥有正确的哈希ID,因为标签不像那样像分支名一样移动。因此,如果我们标记一些提交:

          o--o--o   <-- branch1
         /
...--o--o
         \
          o--o--o   <-- branch2 (HEAD)
                ^
                |
             tag:v1.2

然后添加另一个提交:

          o--o--o   <-- branch1
         /
...--o--o
         \
          o--o--o--o   <-- branch2 (HEAD)
                ^
                |
             tag:v1.2

标签仍然存在。

可以随时删除名称

如果我们认为branch2不是一个好主意,我们可以git checkout branch1然后删除名称 branch2。如果没有名称branch2,我们刚刚添加的最终提交将无法再找到:

          o--o--o   <-- branch1
         /
...--o--o
         \
          o--o--o--o   ???
                ^
                |
             tag:v1.2

标记名称v1.2仍然存在,它使标记的提交可查找。那个标记的提交在 no 分支上(在这个图中,它的父或祖父都没有,尽管它的曾祖父母仍然在branch1)。

名称保护提交

我在上面提到过,提交主要是永久性的。最后一次提交,不再有名称,现在不受保护。 Git有一个名为垃圾收集器的设备,它可以作为一种死神来移除剩余的,不需要的东西。这个Grim Collector git gc在整个Git数据库中搜索所有提交,同时还使用所有名称来查找所有提交。提交可以通过某个名称找到 - 任何名称,包括标签名称 - 都标记为保留。提交(和其他Git对象)可以通过这种方式查找,无法访问来自命名提交,被收集和销毁。

这个过程让Git可以自由地生成对象,并且只决定在最后一分钟使用它们。它还允许您随时移动分支名称。只要提交受到名称的保护,它们就会存在。一旦有 no 名称,它们就可用于垃圾收集。这就是你(和Git)摆脱不必要的提交的方式。像git stash这样的命令可以通过创建不在分支上的提交来工作,但受refs/stash名称(或其 reflog )的保护,我不会在这里进入)。丢下一个藏匿的名字;最终git gc将其删除。

标记保护标记的提交以及任何早期(父)提交,就像分支名称一样。如果删除标记,则现在未命名的提交将容易受到git gc的攻击。但在那之前,即使它根本没有任何分支,也可以愉快地留下来。

答案 1 :(得分:1)

由于此提交也是一个标记,您可以在此处看到:

enter image description here可以有多种选择:

  1. 它在一个给定的分支上,分支被删除
  2. 它是在分支上提交的,reset已在分支上完成
  3. 1最有可能发生 内容是在功能分支中开发的,最后一次提交被赋予了一个标签,分支被删除了

    以下是一个示例场景:

    • 我创建了一个功能分支
    • 承诺的内容
    • 标记此提交
    • 删除分支而不将其合并回任何其他分支
    • 列出标签及其在列表中

    enter image description here enter image description here enter image description here