解释承诺的亲子关系

时间:2017-02-26 21:37:47

标签: git

假设提交A1是提交A2的父级。它真正告诉我什么?

为了澄清我的问题,这里有两个不正确的解释:

1)提交A2是基于提交A1创建的,意思是用户签出了A1,做了一些编辑,并且提交了A2(没有任何介入的git命令)。由于变基,这是错误的。

2)每个git commit都存储相对于其父级的delta,因此您必须反向按箭头并应用每个delta来重建提交的内容。这是错误的,因为与许多其他VCS不同,git提交存储完整的快照而不是增量。

这是一个似乎差不多正确的解释的例子,但非常模糊:

3)提交A2包含提交A1表示的所有工作以及一些额外的工作。 “工作”用于简单地添加,删除和编辑文件。

2 个答案:

答案 0 :(得分:2)

解释2是完全错误的,但它包含一个正确的项目:你做(或Git确实)必须遵循Git存储的向后箭头,以构建图形。每个提交“指向”其父提交(通过存储它们的真实名称哈希ID),使每个提交充当单个顶点(或节点)加上一组传出弧,一旦收集,形成有向无环图或DAG。在CS或信息学的大多数图表中,我们都有从父母到孩子的传出弧线,但在Git中,箭头都是向后的。 (这样父母在孩子存在之前不需要知道他们的子ID,同时也允许父提交一旦创建就是只读的。因为每个哈希ID仅由每个对象的内容决定,并且他们故意难以为了计算,在知道内容之前不能知道任何哈希ID。因此,父提交必须是只读的:你不能更新它们以添加它们的子项;这将改变它们的哈希ID。 1

解释1大多是正确的,但缺少一些关键项目。 As Jim Deville said in his answer,Git的各种管道命令允许您构造几乎任意的提交图节点(即提交对象)。命令git commit-tree特别使用任意数量的有效父提交ID(-p选项),一个有效的树ID和提交消息,并使用您的配置和计算机构建新的提交。设置作者和提交者名称,电子邮件和时间戳字段的当前时间的概念(或者如果设置了环境变量,则使用环境变量覆盖)。新的提交对象存储在数据库中,没有指向它的任何内容,因此您必须快速 2 设置引用(例如分支或标记名称)以保留它。 (或者,您可以创建另一个提交以保留刚刚创建的提交,但 提交需要一个名称或另一个需要某些提交的提交,等等。)

这意味着父信息取决于创建提交的命令。

当您使用git rebase时,创建新提交的步骤通常 - 或者可能是 - git commit本身,git commit根据结果设置新提交的父级阅读HEAD(然后立即更新HEAD,或更常见的是HEAD命名的分支。 rebase操作通常与“分离的HEAD”一起使用,其中HEAD包含现有提交的原始哈希ID,而不是包含分支名称的更常见的HEAD

因此,rebase的工作方式是分离HEAD,使其指向--onto目标(默认为<upstream>参数),然后一次提交一个提交。它通过将原始提交转换为增量,将增量应用于当前索引和工作树,并使每个提交,以及提交la git commit。 (rebase的实际机制是使用git cherry-pickgit am实现的,两者都是用C语言编写的,并使用git commit中的代码。在某些情况下,交互式rebase可能会用于壁球步骤或使用--root时,而不是运行git commit而不是运行git cherry-pick--preserve-merge git merge rebase使用交互式机制并逐字地运行git diff来创建新的合并。细节变得相当复杂。)

请注意,从快照到更改集/ delta的转换是通过对提交的已记录父级运行git commit-tree来完成的。因此,设置一个奇怪的父ID不是有用的。您可以这样做(使用git show)但除非您永远不会挑选或重新定位或git gc --auto提交,所有这些都使用父ID将快照更改为delta,这将是糟糕的计划。

1 当然,可以将每个提交对象分成参与散列的只读部分和不参与散列的读/写部分。这将允许Git向父母添加子ID。但这会使Git 更少稳定且更少安全:只读对象不会像读/写对象那样被破坏,并且部分提交不参与在它的散列中意味着该部分不受保护散列。

2 默认情况下,git gc,其他Git命令不时运行,为您提供两周的时间来完成此任务。如果花费的时间超过了这个时间,则自动{{1}}可能会删除您尚未引用的提交。

答案 1 :(得分:1)

我想说所有A1都是A2的父级意味着在给定分支的git tree-ish中,A1是A2之前的立即提交。

我不确定,但我相信您可以使用git plumbing直接编写提交和树,从而进行与前一次提交完全无关的提交。但是,即使在这种情况下,它也会像两者之间的步骤一样删除所有文件并添加新文件。