需要澄清涉及 git 拉取和拉取请求的 git 工作流

Need clarity with git workflow involving git pull and pull requests

上图让我们很好地理解了 git pull 和 git pull --rebase。 我对这里的一件事感到困惑。让我详细说明-

1.案例 1 -> git pull --rebase origin master

命令后我的本地主分支 - A B C X Y D' E'

命令后我的远程主分支 - A B C X Y

如果我现在执行 git push origin master:master,我的远程主分支将看起来像 - A B C X Y D' E'

2。案例 2 -> git pull origin master

命令后我的本地主分支 - A B C D E F

命令后我的远程主分支 - A B C X Y

git push origin master:master 在这种情况下会如何表现?我无法理解为什么在任何情况下我们都想使用 git pull without --rebase?

我认为,您缺少的是 避免 git pull 的最佳解释。不过,让我们假设 git pull 有一个假设的 --merge 选项,这样我们就可以说你是 运行ning git pull --merge origin master。 (您已经进行了合并;如果此选项是显式选项,则此选项将是默认选项。)也就是说,您的 git pull origin master 运行s 等同于:

  1. git fetch origin;然后
  2. 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

现在我已经用这种方式绘制了图形,哪些提交在“分支”上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 是一个 合并提交 。它返回到两个不同的提交:EY。这两个不同的提交分别返回到 DX,而这两个不同的提交返回到一个共同的共享起点 C.

提交 C 是两个 提示 提交的 合并基础,当时您有 Git 运行 git merge,来自 git pull。所以 Git 通过 运行 在提交的快照 CE。然后 Git 通过 CY 之间的差异 运行 找到了他们在 CY 腿上所做的事情。然后 Git 获取两个差异并 组合 它们,将组合结果应用到来自提交 C 的共享快照,并使用它来进行新的合并提交 F.

合并提交 F 有一个快照,就像其他所有提交一样。它与其他提交的不同之处在于它有 两个 parent、EY。所以你可以问 Git:*从 EF 有什么变化,你会得到的是由于合并的下端(在我的绘图中)带来的变化;或者您可以询问 YF 发生了什么变化,您会看到由于合并的上端带来了哪些变化。

无论如何,这是合并的工作(和要点):合并工作,记录合并工作的事实。你现在可以清楚地看到发生了什么:你在他们工作的时候做了一些事情,他们在你工作的时候做了一些事情,然后你同时把它们结合起来。

使用变基使得历史“更干净”:看起来他们做了什么,你等他们完成,然后你开始你的任务,知道他们做了什么,做了你的工作并提交了它。那不是真的发生了什么,但也许它一样好。也许它 更好 因为对于未来的你、他们或任何人来说,它 更简单: 它不需要弄清楚在work-combining。但是,如果确实出了问题,它可能会隐藏某事的内容,使更糟未来 you/them/whoever.

这就是为什么你有选择的原因:一个可能比另一个更好,也可能不是。

[编辑:]git push 的作用

当你 运行:

git push origin master

或其等价物但 more-explicit 变体:

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 commit hash ID 就是commit E' 的hash ID。您的 Git 提议将此提交发送到他们的 Git。他们从未听说过这个哈希 ID,所以他们说 是的,请发送那个,并告诉我它的 parent(s)。然后你的 Git 告诉他们提交的哈希 ID D';他们也没有听说过那个,所以你的 Git 告诉他们 D's parent Y。此时他们对你的Git说:啊,我已经提交了Y,你现在可以停止发送东西了;打包我要求的提交所需的内容,知道我已经提交 Y 和每个较早的提交 .

或者,让我们暂时假设您使用了 git merge。您的 Git 将提供发送提交 F(通过哈希 ID)。他们的 Git 会对那个说 ,所以你的 Git 现在会主动发送 both parents, EY。他们会对Y不,谢谢,因为他们已经有了那个,但是是的,请E,所以你的Git 将提供 D;他们也会同意那个,然后你的 Git 要么提供 C,要么意识到他们有 C 因为他们有 Y:如果你的 Git 确实提供了 C 他们会说他们不需要它,所以无论哪种方式,结果都是一样的(如果你的 Git 更聪明,效率会更高)。

既然您的 Git 知道要发送哪些提交,以及他们已经拥有哪些提交,您的 Git 可以合理地最小化 thin pack——这技术上取决于选择的推送协议,但现在每个人都应该使用“智能”协议——包含必要的提交和 objects,知道另一个 Git 存储库已经拥有所有 objects与他们已经拥有的所有提交一起使用。然后,您的 Git 将这个“精简包”发送给他们,如果一切顺利,他们会将其保存起来以供进一步使用。

最后,您的 Git 发送了一个礼貌的请求,格式如下:如果可以,请将您的分支名称 master 设置为 ________。让我知道它是否正常。 您的 Git 使用您自己的 master 的哈希 ID 填充空白。他们的 Git 然后检查新提交是否 添加 到他们自己的 master 分支,而不是从他们的 master 之前的任何提交中删除.

这两种情况——你要求他们添加 F,或者你要求他们添加 E'——继续添加,将他们现有的提交 Y 保留在他们的分支中,所以他们可能会接受您的礼貌请求。

请注意,他们永远不会知道或关心您使用什么分支名称来查找这些提交。他们只关心他们他们被要求设置什么分支名称,什么散列ID,以及涉及的各种提交。