apache 为什么有些提交不属于任何分支?

zpgglvta  于 2022-11-16  发布在  Apache
关注(0)|答案(2)|浏览(195)

我遇到过一些提交不属于Git仓库中的任何分支。例如,以下提交被标记为Apache Commons CSV的release,但它不属于任何分支:
https://github.com/apache/commons-csv/commit/0fbd1af5e3bd70454d5e398493a5c983aead2b67
其父提交属于master。
https://github.com/apache/commons-csv/commit/7688fbc3f9f5acf73d3c5018dd83310f7580d02e
你能帮我理解一下吗?

pdkcd3nj

pdkcd3nj1#

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

分支名称标识提示提交

在大多数VC中,分支的 name 是很重要的,甚至可能是关于分支的最重要的东西。分支 names 在Git中几乎没有价值(对Git本身来说)。对Git来说,重要的是 commits。Commits是永久的--好吧,大部分是永久的--并且不可改变:一旦提交,任何提交都不能被改变。但是每个提交的真实名称都是一串可怕的、笨拙的、不发音的、不可能记住的数字和字母,比如fe0a9eaf31dd0c349ae4308498c33a5c3794b293。这些对人类来说是不好的,所以Git让我们用名字来代替这些原始的哈希ID。
然而,关于每个提交的另一个重要的事情是,任何一个提交都存储了另一个提交的真实名称--哈希ID,我们称之为提交的 * 父 * 或 * 前趋 *。我们说这个 * 子 * 提交 * 指向 * 它的父。1如果我们取一串无法发音的哈希ID,并将它们按照“most grandparent-y”到“most child-y”的顺序排列,我们会得到如下结果:

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

最像子提交的提交得到分支名称,然后这个名称指向 last 提交:

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

Git使用最后一个(或 tip)提交以查找其父项,然后使用父项查找祖父项,依此类推,但是因为哈希ID看起来是随机的--并且故意地几乎不可能预测--甚至Git自己也想有一个 name,通过它可以找到链中的 * 最后一个 * 提交。哈希ID特别重要,因为Git会使用这个提交来查找其他提交。这给我们一个类似于下面的画面:

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

(我已经停止绘制箭头的内部反向,并将每个提交的哈希ID替换为圆点)。
不过,中间一行的提交有点令人费解:它们在哪个分支上?Git的答案是它们在 * 两个 * 分支上。Git提交不是属于第一次提交的分支,而是属于 * 每一个 * 分支--嗯,每一个分支 name--指向它。
要在某个分支上添加新的提交,你可以对该分支执行git checkout,照常操作,根据需要执行git add,然后运行git commit,这样就写出了一个新的提交,它指向当前提交作为父提交:

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

然后,无论 * 新 * 提交的哈希值是什么,Git都会将哈希值 * 写入分支名称 *。为了知道 * 哪个 * 名称需要更新,Git会将你的HEAD附加到其中一个分支名称上。一旦新提交的哈希值被安全存储,我们就可以将更新后的图片画成:

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

这是树枝生长的正常方式之一。
1子进程会记住父进程,而不是父进程记住子进程。由于提交是不可变的,因此这是必要的。就像人类的父进程和子进程一样,父进程在子进程创建时就已经存在,但子进程在父进程创建时还不存在。由于提交只能记住过去,因此父进程不能回忆其子进程。

标记还标识提交

标记名就像分支名一样,直接指向一个提交。但与分支名不同的是,Git不会自动 * 更改 * 标记名以使其指向任何其他提交。事实上,一般来说,你也不应该这么做--这并不是说它会破坏你自己的Git,而是它会破坏其他人 * 对 * 你的Git仓库的 * 期望。一旦他们有了标记名到哈希ID的Map,他们可能会认为从那时起他们就有了正确的哈希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,然后删除 namebranch2。没有了名称branch2,我们刚刚添加的最后一个提交将不再是可查找的:

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

然而,tag 名称v1.2仍然存在,它使得标记提交是可找到的。该标记提交位于 no 分支上(在图中,它的父代和祖父代都不是,但它的曾祖父代仍然位于branch1上)。

名称保护提交

我在上面提到过,提交 * 大多数 * 是永久性的。最后一个提交,它已经没有名字了,现在是 unprotected。Git有一个叫做 garbage collector 的设备,它就像一个死神一样清除剩余的、不需要的东西。这个死神收集器,git gc,会在整个Git数据库中搜索所有的提交,同时也使用all names 来查找所有提交。可以通过某个名称(任何名称,包括标记名)找到的提交将被标记为保留。* 不可 * 通过这种方式找到的提交(和其他Git对象),以及从命名提交中 * 不可访问 * 的提交,将被收集并销毁。

这个过程让Git可以自由地生成对象,并且只在最后一刻才决定真实的使用它们。它还允许你随时移动分支名称。只要提交有名称保护,它们就会一直存在。一旦它们 * 没有 * 名称,它们将可用于垃圾回收。这就是您如何像git stash这样的命令的工作原理是创建不在分支上的提交,但这些提交受refs/stash名称的保护(或者它的 reflog,我在这里就不详细介绍了)。最终git gc真实的地将其移除。
标签保护带标签的提交,以及任何更早的(父)提交,就像分支名称一样。如果你删除标签,现在未命名的提交将容易受到git gc的攻击。但在此之前,即使它根本不在分支上,它也可以很高兴地继续存在。
请注意,由于GitHub特有的和内部的原因,GitHub 在默认情况下 * 从不 * 对提交进行垃圾收集,即使Git现在已经丢弃了它。因此,如果你知道提交的哈希ID,并且它 * 曾经 * 存在于GitHub上的某个仓库中,你仍然可以通过它的哈希ID在GitHub上的那个仓库中访问它。你可以要求GitHub操作人员手动清除它(尽管当他们收到邮件时,数据可能已经逃逸了--有“scraperbot”在寻找这些东西!--因此建议在发现这类问题时立即更改密码)。

of1yzvn4

of1yzvn42#

因为这个commit也是一个标记,正如你在这里看到的:


可以是几个选项:
1.它位于给定的分支上,但该分支已被删除
1.在分支上提交,并在分支上执行reset

1是最有可能发生的

内容是在功能分支中开发的,最后一次提交被赋予一个标记,分支被删除
下面是一个示例场景:

  • 我已经创建了要素分支
  • 已将内容提交给它
  • 已标记此提交
  • 已删除分支,但未将其合并回任何其他分支
  • 列出标签并在列表中

第一次

相关问题