Git中的提交:是快照/状态/图像还是更改/差异/补丁/增量?

时间:2016-11-15 18:37:02

标签: git

  • Git中的某些操作(例如checkout)似乎假设提交是工作树的快照或状态。
  • Git中的其他操作(例如rebase)似乎假设提交是一种更改:一种可以应用于工作树的运算符。

那么什么是Git提交,真的吗?

3 个答案:

答案 0 :(得分:3)

了解Git粒子/波对偶性

简短回答: 两者

中等回答: 取决于。

长答案: Git有点像量子现象:两种观点都不能解释所有观察结果。请继续阅读。

在内部,Git将使用这两种表示,这取决于(概念上)它认为在存储空间和给定提交的执行时间方面更高效的表示。快照表示是主要表示。

从用户的角度来看,这取决于你做了什么:

对偶性1:作为快照提交与提交作为更改

确实一些命令只是在你有意义时才有意义 将提交视为工作树的快照。 这对于checkout来说最为明显,但也是如此 stash至少fetchreset的一半。

对于其他命令,当您尝试时,可能会导致疯狂 以这种方式思考提交。 对于那些其他命令,提交明确被视为更改

  • 或者是你可以看到的补丁形式 (例如showdiff
  • 或以运营商的形式,您可以申请修改您的工作树 (例如applycherry-pickpull
  • 或以运营商的形式,您可以申请修改其他提交 (例如rebase
  • 或以运营商的形式,您可以申请创建新的提交 (例如mergecherry-pick

二元性2:作为一个固定的东西提交而不是提交作为流动的东西

双重性1的副作用可以震撼Git新手 习惯于其他版本控制系统。 事实上,Git似乎甚至没有承诺提交它。

咦?

假设您创建了一个包含您想要的内容的分支X. 作为您的提交AB。 但是master进展了一点,所以你rebase X到master

当您将AB视为更改,而将master视为快照时 (嘿,粒子和波浪在一次实验中!), 这不是问题: 只需将更改AB应用于快照master

这种想法很自然,你几乎不会注意到Git 现在已经重写了您的提交AB:它们现在有所不同 快照内容,因此不同的SHA-1 ID。 在Git中,您认为是开发人员的概念提交 不是一件固定的事情,而是 一些流体物体因使用你的物体而发生变化 库中。

相反,如果您考虑所有三个(ABmaster) 作为快照或所有三个作为更改, 你的大脑会受伤,你将无处可去。

声明

以上是一个非常简化的描述。 在Git现实中,

  • 提交根本不是快照,它是一段元数据 (快照的 who / when / why )加上指向快照的指针;
  • 快照在Git lingo中被称为;
  • 提交即更改内部表示使用 packfiles ;
  • 上面提到的一些命令还有其他角色 不符合相同的特征;
  • 甚至对于给定的角色,它在某种程度上也是一个问题 尝试某些命令所属的类别(或者)。

并且不要因为Pro Git书首次对Git进行描述(在#34; Git Basics"部分中)是&em>&而感到困惑#34;快照,而不是差异" 。

Git 毕竟是

答案 1 :(得分:2)

提交是快照状态。执行git diff时,它会计算父项的差异。这就是为什么可能有多个父母(合并时的情况)。在内部,正在进行增量压缩,但版本控制模型不是基于补丁的。

git中的一个核心概念是索引。这是一个包含被跟踪对象树的大对象。当变量从工作副本传播到索引时,会进行更改;这会将索引置于修改状态。提交操作将该状态转换为新提交。

答案 2 :(得分:2)

虽然可以将其解释为两者,但 GitHub 工程团队很清楚(2020 年 12 月):

<块引用>

Commits are snapshots, not diffs

Derrick Stolee

开头
  • 对象 ID
  • blob(文件内容)
  • 树(目录列表)
  • 提交:快照!
<块引用>

对象 ID

了解 Git 对象最重要的部分是 Git 通过其对象 ID(简称 OID)引用每个对象,为对象提供唯一名称。
我们将使用 git rev-parse <ref> 命令来发现这些 OID。
每个对象本质上都是一个纯文本文件,我们可以使用 git cat-file -p <oid> 命令检查其内容。

<块引用>

Blob(文件内容)

要发现当前修订版文件的 OID,请运行 git rev-parse HEAD:<path>
然后,使用 git cat-file -p <oid> 查找其内容。

<块引用>

树(目录列表)

请注意,blob 包含文件内容,但不包含文件名称
这些名称来自 Git 对目录的表示:trees.
树是路径条目的有序列表,与对象类型、文件模式和该路径上对象的 OID 配对。
子目录也表示为树,因此树可以指向其他树!

<块引用>

最后:

<块引用>

提交:及时快照

提交是时间的快照。每个提交都包含一个指向其根树的指针,代表当时工作目录的状态
提交具有与先前快照对应的父提交列表。
没有父级的提交是根提交,有多个父级的提交是合并提交。
提交还包含描述快照的元数据,例如作者和提交者(包括姓名、电子邮件地址和日期)以及提交消息。
提交消息是提交作者向父项描述提交目的的机会。

https://github.blog/wp-content/uploads/2020/12/commit.png?resize=399%2C268?w=399

尽管提交是快照,但我们经常将历史视图或 GitHub 上的提交视为差异。事实上,提交消息经常提到这个差异。

diff 是通过比较提交的根树和它的父级从快照数据动态生成的。 Git 可以及时比较任意两个快照,而不仅仅是相邻的提交。

计算差异是启用 git cherry-pickgit rebase 的原因。

而且由于提交没有区别...

<块引用>

Git 不跟踪重命名。 Git 内部没有数据结构来存储在提交与其父项之间发生重命名的记录。
相反,Git 会尝试在动态差异计算期间检测重命名。此重命名检测有两个阶段:精确重命名和编辑重命名。

在第一次计算差异之后,Git 检查该差异的内部模型以发现添加或删除了哪些路径。
自然地,从一个位置移动到另一个位置的文件将显示为从第一个位置删除并在第二个位置添加。 Git 尝试匹配这些添加和删除以创建一组推断的重命名。