“魔力”就在于ID。提交ID由许多因素组成,但基本上它是这个
SHA-1 hash的哈希值。
内容(所有内容,而不仅仅是差异),作者,日期,日志信息,父ID。
更改其中任何一个,您需要创建一个新的提交并生成一个新的ID。请注意,父ID已包含在内。
对于Git来说,这意味着如果我告诉您我有提交“ABC123”,而您也有提交“ABC123”,那么我们知道我们具有相同的提交,包括相同的内容、相同的作者、相同的日期、相同的信息和相同的父级。这些父级具有相同的ID,因此它们具有相同的内容、相同的作者、相同的日期、相同的信息和相同的父级。以此类推。如果ID匹配,则它们必须具有相同的历史记录,无需进一步检查。这是Git的一个伟大优势,它深深地融入了其设计中,您无法理解Git而没有它。
一个 pull 操作是 fetch 和 merge 的结合。执行
git pull origin master
相当于执行
git fetch origin
,再执行
git merge master origin/master
(或使用
--rebase
进行 rebase)。fetch 操作大致如下...
remote @ http://example.com/project.git
F - G [bugfix]
/
A - B - C - D - E - J [master]
\
H - I [feature]
local
origin = http://example.com/project.git
F - G [origin/bugfix]
/
A - B - C - D - E [origin/master] [master]
- [local] 你好远程,你有哪些分支?
- [remote] 我在 G 分支上有 bugfix。
- [local] 我也在 G 分支上有 bugfix!完成了。还有什么?
- [remote] 我在 I 分支上有 feature。
- [local] 我没有 feature,也没有 I。I 的父分支是什么?
- [remote] I 的父分支是 H。
- [local] 我没有 H,H 的父分支是什么?
- [remote] H 的父分支是 J。
- [local] 我没有 J,J 的父分支是什么?
- [remote] J 的父分支是 E。
- [local] 我有 E!请把 J、H 和 I 发给我。
- [remote] 好的,马上发送。
- [local] 将 J、H 和 I 添加到仓库并将 origin/feature 设为 I好的,还有其他的吗?
- [remote] 我在 J 分支上有 master。
- [local] 我在 E 分支上有 master,你已经将 J 发送给我了。将 origin/master 移动到 J 还有其他的吗?
- [remote] 没有了!
- [local] 拜拜了您嘞!
现在本地看起来像这样...
local
origin = http://example.com/project.git
F - G [origin/bugfix]
/
A - B - C - D - E [master] - J [origin/master]
\
H - I [origin/feature]
然后它将执行
git merge master origin/master
完成拉取,这将快进到 J。推送类似,只是过程相反(本地发送提交到远程),并且只会快进。这就是
Pro Git所称的“愚蠢协议”,当您的远程是简单的HTTP服务器时使用。
智能协议更常用,更少喋喋不休,并且有许多优化。但是您可以看到两者都非常高效。没有必要通信整个历史记录,他们只需要发送20字节的哈希密钥,直到找到一个共同的祖先。这里有一些参考资料和进一步阅读。