我认为,避免git pull 可以很好地解释你所缺少的。尽管如此,让我们假设git pull 有一个假设的--merge 选项,因此我们可以说您正在运行git pull --merge origin master。 (您已经获得了合并;如果它是显式选项,则此选项将是默认选项。)也就是说,您的 git pull origin master 运行相当于:
-
git fetch origin;那么
-
git merge -m "merge branch master of <url>" origin/master。
这会产生图表,他们将其绘制为:
A--B--C--D--E--F <-- master
\ /
X----Y
(我在这里把它转过来了。旋转 90˚ ccw 以匹配。)
我现在想建议像这样重绘它:
D--E
/ \
A--B--C F <-- master
\ /
X--Y
现在我已经以 this 的方式绘制了图表,哪些提交是“on”分支master?如果你选了A-B-C-D-E-F,那你为什么不选X-Y呢?如果你选A-B-C-X-Y-F,为什么不选D-E?
事实上,所有八个提交,包括D-E 和 X-Y,都是“on”分支master。 名称 master 标识提交F,但提交F 是一个合并提交。它返回到两个不同的提交:E 和 Y。这两个不同的提交分别返回到D 和X,这两个不同的提交返回到一个共同的共享起点C。
Commit C 是两个 tip 提交的 合并基础,当时您通过 git pull 让 Git 运行 git merge。因此,Git 通过运行提交C 和E 中的快照之间的差异,找出了你在C-to-E 上所做的事情。然后 Git 通过运行 C 和 Y 之间的差异,在 C-to-Y 分支上找到了他们所做的事情。然后 Git 获取两个差异并将它们组合,将组合结果应用到来自提交 C 的共享快照,并使用它来进行新的合并提交 F。
合并提交F 有一个快照,就像其他所有提交一样。它与其他提交的不同之处在于它有 两个 父级,E 和 Y。所以你可以问 Git:*从 E 到 F 的变化是什么,你会得到由于合并的较低(在我的绘图中)腿而带来的变化;或者您可以询问从Y 到F 发生了什么变化,您会看到由于合并的上半部分带来了哪些变化。
无论如何,这是合并的工作(和要点):合并工作,记录合并工作的事实。您现在可以确切地看到发生了什么:您在他们工作的时候做了一些事情,他们在您工作的时候做了一些事情,然后您一次将它们组合在一起。
使用 rebase 可以创建“更清晰”的历史记录:看起来他们做了某事,您等待他们完成,然后您开始执行您的任务,知道他们做了什么,完成了您的工作并提交了它。这并不是真正发生的事情,但也许它同样好。也许它更好,因为对于未来的你、他们或任何人来说,它更简单:它不需要弄清楚在工作组合过程中是否出了问题。但是,如果某事确实出了问题,它可能会隐藏某事的本来面目,让未来的你/他们/任何人更糟。 p>
这就是为什么你有一个选择:一个可能比另一个更好,或者不是。
[编辑:]git push 做了什么
当你运行时:
git push origin master
或其等效但更明确的变体:
git push origin master:master
您的 Git 将:
- 使用名称
origin 查找此git push 操作的URL(git config --get remote.origin.pushurl;如果未设置,则为git config --get remote.origin.url);
- 调用任何响应此 URL 的内容:应该是另一个 Git 软件,连接到另一个存储库;
- 提议通过其哈希 ID 向他们发送您最新的
master 提交;和
- 从那里继续。
首先假设您使用了rebase,因此您最新的master 提交哈希ID 是提交E' 的哈希ID。你的 Git 提议将此提交发送到他们的 Git。他们从未听说过这个哈希 ID,所以他们说是的,请发送那个,然后告诉我它的父代。然后你的 Git 告诉他们提交 D' 的哈希 ID;他们也没有听说过那个,所以你的 Git 告诉他们D's parent Y。此时他们对你的 Git 说:啊,我已经提交 Y,你现在可以停止发送东西了;打包我所要求的提交所需的内容,知道我已经提交 Y以及之前的每个提交。
或者,让我们暂时假设您使用了git merge。你的 Git 将提供提交F(通过哈希 ID)。他们的 Git 会对那个人说 yes,所以你的 Git 现在会提供发送 both 父母 E 和 Y。他们会对Y 说不,谢谢,因为他们已经有了那个,但是是的,请 对E,所以你的Git 会提供D;他们也会同意那个,然后你的 Git 要么提供C,要么意识到他们有C,因为他们有Y:如果你的Git 确实提供C,他们会说他们没有'不需要它,所以这两种方法都是一样的(如果你的 Git 更聪明,它会更有效)。
既然您的 Git 知道要发送哪些提交,以及他们已经拥有哪些提交,您的 Git 会创建一个相当小的精简包——这在技术上取决于所选的推送协议,但每个人都应该使用这些天来的“智能”协议——包含必要的提交和对象,知道另一个 Git 存储库已经拥有与他们已经拥有的所有提交相关的所有对象。然后,您的 Git 会将这个“精简包”发送给他们,如果一切顺利,他们会将其保存起来以备进一步使用。
最后,您的 Git 会发送以下形式的礼貌请求:如果没问题,请将您的分支名称 master 设置为 ________。如果没问题,请告诉我。 你的 Git 用你自己的 master 的哈希 ID 填充空白。然后,他们的 Git 会检查新提交是否添加到他们自己的 master 分支,而不会从他们的 master 中删除他们之前的任何提交。
这两种情况——你要求他们添加F,或者你要求他们添加E'——都添加,将他们现有的提交Y保留在他们的分支中,所以他们可能会接受你的礼貌请求。
请注意,他们从不知道或关心您使用什么分支名称来查找这些提交。他们只关心 他们 被要求设置什么分支名称、哈希 ID 以及其中涉及的各种提交。