【发布时间】:2016-11-15 18:37:02
【问题描述】:
- Git 中的某些操作(例如
checkout)似乎假定提交是工作树的快照或状态。 - Git 中的其他操作(例如
rebase)似乎假定提交是一种更改:一种可以应用于工作树的运算符。
那么究竟什么是 Git 提交?
【问题讨论】:
标签: git
checkout)似乎假定提交是工作树的快照或状态。rebase)似乎假定提交是一种更改:一种可以应用于工作树的运算符。那么究竟什么是 Git 提交?
【问题讨论】:
标签: git
简答: 两者都。
中等答案: 视情况而定。
长答案: Git 有点像量子现象:这两种观点都不能单独解释所有观察结果。继续阅读。
在内部,Git 将使用这两种表示,这取决于(在概念上)它认为在给定提交的存储空间和执行时间方面哪个更有效。快照表示是主要的。
从用户的角度来看,然而,这取决于你做什么:
确实有些命令只有在你使用
将提交视为工作树的快照。
这对于checkout 最为明显,但对于
stash 和 fetch 和 reset 至少一半。
对于其他命令,当你尝试 以这种方式考虑提交。 对于那些其他命令,提交明确地被视为更改,
show、diff)apply、cherry-pick、pull)rebase)merge、cherry-pick)对偶 1 的副作用可能会让 Git 新手感到震惊 习惯于其他版本控制系统。 事实上,Git 似乎甚至没有提交自己的提交。
嗯?
假设你创建了一个分支 X 包含你喜欢的想法
作为您的提交A 和B。
但是master进步了一点,所以你把rebaseX改成master。
当您将A 和B 视为更改时,而将master 视为快照
(嘿,粒子和波在一个实验中!),
这不是问题:
只需将更改 A 和 B 应用到快照 master。
这种想法是如此自然,以至于你几乎不会注意到 Git
现在已经重写了你的提交 A 和 B:他们现在有不同的
快照内容,因此使用不同的 SHA-1 ID。
在 Git 中,您认为作为开发人员的概念提交
不是一劳永逸的事情,而是
由于与您的合作而发生变化的一些流体对象
存储库。
相比之下,如果您想到所有三个(A、B 和 master)
作为快照或所有三个作为更改,
你的大脑会受伤,你将一事无成。
以上是一个非常简化的描述。 在 Git 现实中,
并且不要对 Pro Git 书籍对 Git 的第一个特征(在“Git Basics”部分)是“快照,而不是差异”这一事实感到困惑。。
Git 毕竟是复杂的。
【讨论】:
B 是A 的子级,则B 的变更集就是B - A。
提交是一种快照状态。当您执行git diff 时,它会计算与父级的差异。这就是为什么可以有多个父级(合并时的情况)。在内部,存在增量压缩,但版本控制模型不是基于补丁的。
git 中的一个核心概念是索引。这是一个包含被跟踪对象树的大对象。当更改从工作副本传播到索引时,它们会被暂存;这会将索引置于修改状态。提交操作将该状态转换为新的提交。
【讨论】:
虽然可以理解为两者,但 GitHub 工程团队很清楚(2020 年 12 月):
开头Commits are snapshots, not diffs
对象 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 对目录的表示:树。
树是路径条目的有序列表,与对象类型、文件模式和该路径处对象的 OID 配对。
子目录也表示为树,因此树可以指向其他树!
最后:
提交:及时快照
提交是时间的快照。每个提交都包含一个指向其根树的指针,代表当时工作目录的状态。
该提交具有与先前快照相对应的父提交列表。
没有父级的提交是根提交,而有多个父级的提交是合并提交。
提交还包含描述快照的元数据,例如作者和提交者(包括姓名、电子邮件地址和日期)以及提交消息。
提交消息是提交作者描述该提交相对于父母的目的的机会。尽管提交是快照,但我们经常在历史视图或 GitHub 上将提交视为差异。事实上,commit message 经常引用这个 diff。
差异是通过比较提交的根树及其父级从快照数据动态生成的。 Git 可以及时比较任意两个快照,而不仅仅是相邻的提交。
计算差异是启用
git cherry-pick或git rebase的原因。
而且由于提交没有差异......
Git 不跟踪重命名。 Git 内部没有数据结构来存储在提交与其父项之间发生重命名的记录。
相反,Git 尝试在动态差异计算期间检测重命名。此重命名检测有两个阶段:精确重命名和编辑重命名。在首次计算差异后,Git 会检查该差异的内部模型以发现添加或删除了哪些路径。
自然地,从一个位置移动到另一个位置的文件将显示为从第一个位置删除并在第二个位置添加。 Git 尝试匹配这些添加和删除以创建一组推断的重命名。
【讨论】: