Mercurial/Git flow - 什么时候创建新头像?

Mercurial/Git flow - When are new heads created?

我们有一门学习 Mercurial 的课程。我有一个关于 Mercurial 的问题,但我相信 Git 在这里的行为完全相同。

为了回答这个问题,我使用了 Mercurial Workbench 并在每个命令后遵循图表。

这是问题: 对于以下 mercurial 操作序列,请解释哪一行 导致任何引用存储库中的头数发生变化(比如哪个 存储库受到影响,以及原因)。假设主存储库最初有一个 头,有一些现有数据:

(我把我认为是新人头的线条加粗了)

  1. /home/user> hg clone http://remoteserver/mainrepository clone1

  2. /home/user> hg clone http://remoteserver/mainrepository clone2

  3. /home/user> cd clone1
  4. /home/user/clone1> hg 标签初始值
  5. /home/user/clone1> echo one > a.txt # 创建一个包含“one”的新文件“a.txt”
  6. /home/user/clone1> echo two > b.txt # 创建一个包含“two”的新文件“b.txt”
  7. /home/user/clone1> hg 添加 a.txt
  8. /home/user/clone1> hg commit -m “添加了一个文件”
  9. /home/user/clone1> hg tag file-one
  10. /home/user/clone1> hg push -f ../clone2
  11. /home/user/克隆1> cd ../克隆2
  12. /home/user/clone2> echo three > c.txt # 创建一个新文件“c.txt”包含 “三”
  13. /home/user/clone2> hg 添加 c.txt
  14. /home/user/clone2> hg commit -m “添加了另一个文件”
  15. /home/user/clone2> hg push -f ../clone1
  16. /home/user/克隆2> cd ../克隆1
  17. /home/user/clone1> hg up initial
  18. /home/user/clone1> hg 添加 b.txt
  19. /home/user/clone1> hg commit -m “添加了另一个文件”
  20. /home/user/clone1> hg up file-one
  21. /home/user/clone1> hg push -f ../clone2

我得出这样的结论:

我们最终在 clone1 中有 3 个头,在 clone2 中有 3 个头。

我的问题是:

  1. 这是正确的吗?
  2. 您能向我解释一下添加新行的每一步会发生什么吗? 我试着想出自己的理由,但我需要专家的意见。

谢谢

对于 (1):是的,没错。对于 (2),我不确定你在问什么("at each step a new row is added"—— 这个词在这里是什么意思?)。 [根据评论进行编辑]让我们看看第 14、15、19 和 21 步相对于前面的步骤。

在第 14 步,您 运行 hg commit -m "Added another file"current commit 不是现有的 tip commit,因为在第 12 步你进入了 "clone2" 目录/克隆,其 current commit从您制作克隆时(在第 2 步)开始没有变化。也就是说,如果当前提交在第 2 步是 numeric-ID 0 和 hash-ID a1234567....,那仍然是当前提交,即使第 10 步向该存储库添加了更多提交. (当前分支可能是 default,尽管这不太重要,因为 clone1 和 clone2 在同一个分支上,并且添加到 clone1 的提交现在存在于同一个分支上的 clone2 中。关键是由于 pushed-in 提交,clone2 中的当前 commit 不是 clone2 中当前分支的头。)

在更典型的情况下,例如第 7 步中的情况,当您进行新提交时,您将进行 head 提交(没有 children 的提交)。进行新提交会创建新提交,并将当前提交作为其 parent,因此 头(没有 children)的提交是 no更长的头(有一个 child);新提交本身是新的,没有 children 因此是一个头。因此,heads 的数量被调整为 -1(失去之前的 head)和 +1(通过新提交获得新的 head)。

但是您在第 14 步所做的新提交有一个 not-a-head-anymore 提交作为它的 parent。当它获得 child 提交时,它的 "headness" 在第 10 步被 git push 删除。现在它有两个children,剩下not-a-head;而新的 child 提交,作为一个新的提交,有 no children,使它成为一个头。因此,人数调整为+1,没有抵消-1。

在第 15 步,您 运行 hg push -f ../clone1。这会将您刚刚在第 14 步所做的新提交放入 clone1。由于您所做的新提交是一个头,clone1 获得一个新的头。

第 19 步类似,但我们回到克隆 1(由于第 16 步)。由于第 17 步(hg up initial 使用标签找到 switch-to 的提交,current 提交不是一个现有的头。你做了一个新的提交,它会自动成为一个新的头;但是您将此新提交添加为新 child 的提交本身并不是一个头,因此头数增加了一个。第 21 步只是将这个新的提交推送到 clone2 中:它是 clone2 的新提交,所以它是一个新的头部,但它不会将任何现有的头部转换为 non-head.

你还没有到这里,但是...

请注意,当您使用 hg branch <newbranch> 然后使用 hg commit 时,您会为当时的任何提交创建一个新的 child,但是新的 child 是在不同的分支。成为当前提交的新提交是新(现在存在的)分支的新负责人,并且它是曾经是当前提交的 child 这一事实是无关紧要的,因为 "head-ness" 由同一分支.

中是否有child次提交决定

更普遍(pre-edit 文本在下面继续)

... I believe Git acts the exact same way here

它在提交方面确实如此,但在方面却没有。

Mercurial 将 head 定义为 任何没有外向弧的提交进入(child 提交)同一个分支。 Mercurial 的分支与Git的非常不同,Git或多或少将一个头定义为"any commit that has a branch name pointing directly to it"(虽然Git称这些提示 提交,并将分支名称本身称为 "head").

Git的分支没有永久性:它们存在只是因为分支名称本身存在,分支名称只有在你不告诉Git删除它时才会存在。删除名称后,分支将不复存在。 commits 一直存在,直到不再有对它们的引用,此时垃圾收集器(一旦它 运行s)丢弃它们。一些引用被保存在 reflogs 中,每个分支都有一个单独的引用日志(删除分支时删除)加上一个特殊名称 HEAD从未 删除),所以即使分支的引用日志引用消失了,HEAD 引用日志引用往往会保持 deleted-branch 提交一段时间。

如果那些 (in-Git) 提交可以从某些 other 分支到达,它们将因此而继续存在。同时,Git 中的每个提交通常都可以同时从 N 个分支到达(N 是任何非负整数,通常 N≥1)。我们说这些提交 包含在 那些分支中。相比之下,Mercurial 中的每个提交都在 exactly one 分支上,其分支已被选择在commit-time,它永远不会改变。

换句话说,我们可以说 在 Mercurial 中,分支的 存在取决于 分支上的提交; 但在 Git 中, 提交的 存在取决于 包含该提交的(分支和其他)名称。 任何名称都可以,包括标签名称——Git 这里有一个比 Mercurial 更大的命名空间,因为它的标签存储在外部(这使得它们不受版本控制,这意味着所有这些)。

为了在 Mercurial 中工作,"head-ness" 是一个自动的 属性 提交,为了在 Git、"head-ness"(或 branch-tip-ness) 改为 手动管理 。为了使其主要-自动,git commit将自动更新当前分支(其名称存储在HEAD中)以指向刚刚进行的新提交。

两个 VCS 都以相同的方式添加新提交:它们采用建议提交中的任何内容——存储在 Git 的 index 或 Mercurial 的 [=147] =] 如 hg summary 所示——并将其打包到一个真正的提交中,其 parent 是当前提交。在 Mercurial 中,提交后,我们就完成了:它自动成为一个 head,因为它没有 children。在 Git 中,完成新的提交后,我们的最终任务是将提交的 ID 写入当前分支名称,以便分支名称现在指向新的提示提交。