git push 是原子的吗?

Krz*_*iak 6 git git-pull git-push

git push与几个提交一个原子操作?

  1. 关于git push同一分支的其他操作
  2. 关于git pull来自同一分支的操作

对于情况 1. 它必须是。否则我的提交会干扰其他人的提交,可能会造成不一致或无效的状态。Git 通过强迫我首先集成其他人的更改(如果我输掉比赛)或强迫其他人集成我的更改(如果我赢得比赛)来防止这种情况。

但是 2. 呢?如果我的存储库如下所示:

          C---D---E master
         /
    A---B origin/master
Run Code Online (Sandbox Code Playgroud)

有没有人git pull在我做的git push时候看到 A---B 或 A---B---C---D---E,或者他们也能得到介于两者之间的任何东西,例如 A--- B---C---D?

tor*_*rek 7

有效,是的。

请注意,您对其他人对其存储库所做的操作具有零控制权。但是,当您对git push其他一些存储库(例如 GitHub 上的一个)进行操作时,真正发生的是:

  • 你的 Git 发送任何提交和/或他们的 Git 需要的其他对象,以便你的 Git 发出它的创建或更新或删除请求。名称只能命名存储在存储库中的某些实际对象,因此要让他们将其master分支设置为 commit a123456...,您的 Git 必须首先确保他们commit a123456...

  • 然后,对于您希望他们更新(或创建或删除)的每个名称,您的 Git 会要求(常规推送)或命令(git push --force以及其他设置强制标志的操作)他们进行更新。您向他们发送名称N并散列new-hash,作为更新(或创建或删除)请求的列表。每个请求都有一个,有时是两个,如下所示。(全零哈希表示“删除”。)

    • 你的 Git 可以向他们发送一个礼貌的请求,如果它是一个新的分支或标签,或者如果它是一个删除请求,或者如果它是一个分支名称更新并且更新是快进的,他们的 Git 会服从。(除了这些约束之外,控制他们的 Git 的人可以设置他们喜欢的任何其他约束,但这些是默认值。)

    • 您的 Git 可以无条件发送命令。默认情况下,他们的 Git 会服从(但和以前一样,控制他们的 Git 的人可以设置额外的约束)。

    • 或者,你的 Git 可以发送一个命令,但有你自己的条件,形式如下:我相信你的名字 N 代表散列 ID old-H(对于某些名称和散列,如果你期望它们,old-H 是全零)还没有名字)。如果他们的名字N有 hash old-H(和以前一样,控制他们的 Git 的人可以设置额外的约束),他们的 Git 将服从命令。

  • 这个更新过程发生在他们的Git 在他们的存储库中设置的锁定下。就您的 Git 而言,此锁使更新成为全有或全无。对于你发送的每个名字,更新要么发生——被接受,现在他们的名字N代表你的 Git 询问/命令的新哈希——或者不发生并且被拒绝并且名称没有改变。

当您(或任何人)运行时,git pull您实际上是在运行,git fetch然后是第二个纯本地 Git 命令。这git fetch类似于git push您的 Git 调用其他一些 Git,但这次数据传输以另一种方式进行:

  • 你的 Git 从他们的 Git 中得到他们所有名字和哈希 ID 的列表。如果有一个持续的推动下,每对名称和哈希ID,要么是从之前请求或命令的更新,或从后:有没有在两者之间可见的,因为他们的Git尊重自己的锁。

  • 然后,使用在此步骤中找到的名称和哈希 ID,您的 Git 会根据此列表引入您想要和没有的新对象。

  • 在此过程结束时,您的 Git不会触及您的任何分支名称——至少在默认情况下不会(您可以使用refspec参数覆盖它)。相反,您的 Git 会更新您的远程跟踪名称,例如origin/master,以匹配其名称。(根据您的运行方式git fetch,您可以限制您的 Git 只更新您的一个或几个名称,而不是所有名称;如果您只想更新您的origin/master,您的 Git 可以跳过下载仅可访问的新对象从他们feature-X那将成为你的origin/feature-X。)

第二个纯本地命令可以执行第二个命令(通常是合并,除非您选择 rebase)可以执行的任何操作。这部分通常不是原子的:例如,在 rebase 期间,您的 rebase 可能会在中间停止,只复制一些提交,迫使您修复冲突并运行git rebase --continue. 但这一切都在您的存储库中,没有其他人共享。(您的 Git 还会在您自己的分支名称和其他名称更新中执行自己的锁定/解锁操作,以防您在后台运行另一个 Git 命令,或通过 cron 作业或其他任何方式。)

通常,您的 CI 系统将拥有自己的 Git 存储库,它通过从您指定为其上游的任何存储库(例如,GitHub存储库)进行复制来更新该存储库。您的 CI 系统将运行git fetch进行 origin/master更新。您的 CI 系统如何检查和构建该origin/master提交取决于它。