git“落后或领先 X 次提交”的真正含义是什么?

kev*_*der 3 git

于是git status回来了

Your branch is ahead of 'origin/Dev-Branch' by 5 commits.
  (use "git push" to publish your local commits)
Run Code Online (Sandbox Code Playgroud)

我这样做了,现在git status回来了

Your branch is up to date with 'origin/Dev-Branch'.
Run Code Online (Sandbox Code Playgroud)

git log显示我的最后一次提交是前一天的:即今天没有任何内容被推送到原点

Git 如何计算后面和前面的提交消息?

tor*_*rek 7

如果记住以下几点,对理解 Git 会有很大帮助:

\n
    \n
  • Git 就是关于提交。分支 \xe2\x80\x94 或分支名称\xe2\x80\x94 仅在让您(和 Git)查找哈希 ID 方面重要。哈希 ID 是提交的真实名称。哈希 ID 看起来是随机的,因此我们需要名称来查找它们。分支名称(例如master或)保存一个哈希 ID:该分支中最后一次dev提交的哈希 ID 。

    \n

    这些哈希 ID 是通用的!它们在每个 Git 存储库中始终相同。Git 存储库要么具有提交,该提交具有哈希 ID……要么根本没有该提交。任何哈希 ID 都不能重复用于不同的提交。1

    \n
  • \n
  • Git 被称为分布式,但您可能认为它是复制的更好。(不过从技术上来说,分布式是一个更好的词。使用任何你需要的东西,并牢记在心。)

    \n
  • \n
  • 每个存储库(通常情况下)都有它所见过的所有提交的完整副本。第一次使用git clone其他存储库时,您将获得其所有提交的完整副本。不过,在那之后,两个克隆体可能会逐渐分开,除非你让一个克隆体呼叫另一个克隆体。

    \n
  • \n
  • 克隆仅在您连接\xe2\x80\x94 时才相互通信,方法是让一个存储库调用另一个存储库(通常通过https://...ssh://...URL),但通常您会将此 URL 隐藏在一个简单的名称(如origin.

    \n

    您可以使用git fetch(“获取提交”)和git push(“给予提交”)进行连接。该git pull命令在这里会让人分心:它实际上只是意味着run git fetch,然后运行第二个 Git 命令。 它是git fetch让你的 Git 与另一个 Git 对话的部分。

    \n
  • \n
\n

因此,要使用他们获得或制作的任何新内容更新您的克隆,您可以运行. 你的 Git 调用他们的 Git,你的两个 Git 进行存储库交互,并且由于方向是“从他们那里获取新东西”,所以无论他们拥有什么,你现在也拥有它。但您的 Git会使用您的 Git远程跟踪名称记住它们所拥有的内容。您的 Git 询问他们的. 他们可能会说:我的是 commit。如果您还没有该提交(以及与之相关的任何早期提交),您的 Git 让他们发送该提交(以及您也需要的任何早期提交)。一旦你的 Git 提交了,你的 Git 就会设置你的\xe2\x80\x94 而不是你的!\xe2\x80\x94 以记住他们所说的.git fetchmastermastera123456...origin/mastermaster mastera123456

\n

要使用您获得或制作的任何新内容更新他们的克隆\xe2\x80\x94显然您必须自己制作它,或者从不是他们的地方获得它\xe2\x80\x94您让您的 Git 调用他们的 Git并说:你有承诺吗 如果没有,你就向他们做出承诺,以及完成工作所需的任何其他承诺。然后你的 Git 会说:现在,如果你愿意的话,请将你的设置2 请注意,他们没有设置远程跟踪名称!他们没有类似的东西;他们只是有他们的b789abc...masterb789abc...brian/masterkevin/master master

\n

如果他们的 Git 返回并说“OK”,我将 my 设置masterb789abc...,那么,现在你的 Git 知道他们的master意思是哈希 ID。所以你的 Git 更新你的 origin/master以记住他们的主人记得b789abc...

\n

这让我们了解了远程跟踪名称的含义origin/master这些是您的 Git 的记忆,记录了 Git 所记住的哈希 ID。 这些哈希 ID 可能已过时!运行git fetch会让您的 Git 从其 Git 获取任何新内容,并更新您的远程跟踪名称,因此现在您的 Git 拥有正确的信息。如果您已经有一段时间没有运行了git fetch,那么您的 Git 可能已经过时了。3

\n
\n

1这种唯一性约束实际上稍微宽松一些:如果两个 Git 永远不会互相进行 Git-sex,则两者之一可以重复使用另一个 Git 用于不同内部对象的哈希 ID。除了这个例外, Git 的所有功能都严重依赖于哈希 ID 的唯一性。它们是所有魔法发挥作用的原因。这就是为什么它们看起来如此随机,尽管它们实际上只是严格计算的加密校验和:它们必须是唯一的。

\n

2git push即使他们已经这么做了,你也会这样做b789abc...。该git push命令由两部分组成:如果/根据需要发送提交,所有这些都通过唯一的哈希 ID 来工作,后面跟着对其他 Git 的请求或命令:将分支名称 X 设置为哈希 ID H1,将分支名称 Y 设置为哈希 ID H2, 等等。

\n

3 “一会儿”有多长?这取决于其他 Git 存储库的活跃程度。也许他们每天都会收到新的提交。也许只是每年一次。或者也许他们每小时会收到数千个新提交,即使只有半秒,为什么,那几乎是永远的!

\n
\n

接下来,您需要了解提交是相互关联的

\n

在 Git 中,一个提交\xe2\x80\x94 具有唯一哈希 ID\xe2\x80\x94 的东西是:

\n
    \n
  • 所有文件的快照(不是对文件的更改,而是完整快照)
  • \n
  • 加上一些元数据:\n
      \n
    • 您的姓名和电子邮件地址,以及您进行此提交的日期和时间戳:这是提交者数据
    • \n
    • 作为作者数据,同样重复了额外的时间(如果您复制提交,这可能会有所不同)
    • \n
    • 您的日志消息,您在下个月/下一年告诉其他人或您自己为什么进行此提交
    • \n
    • 对于 Git 来说至关重要的是,一些先前提交的原始哈希 ID
    • \n
    \n
  • \n
\n

最后一点就是 Git 历史的存在方式。提交一个快照,但具有前一个快照的哈希 ID。也就是说,如果我们有一些丑陋的大哈希 ID\xe2\x80\x94,我们只需将其称为H“哈希”\xe2\x80\x94,它定位一个提交,该提交内部包含前一个提交的哈希 ID。我们将其称为第二个哈希 ID G。然后:

\n
      ... G <-H\n
Run Code Online (Sandbox Code Playgroud)\n

H出现在之后G,但指向更早的提交G。当然G里面也保存了一些又大又难看的哈希ID,所以G指向F

\n
... <-F <-G <-H\n
Run Code Online (Sandbox Code Playgroud)\n

F再次指向后面,等等。

\n

有了这样的链,我们可以从任何提交一直追溯到第一次提交。该提交不会指向较早的提交,因为它不能:没有较早的提交。Git 将此称为提交。假设只有 8 个提交,A一直到H,都在一个很好的线性行中,并且名称保存了 最后一次提交master的哈希 IDH

\n
A--B--C--D--E--F--G--H   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n

我们说这master指向H. (我将提交之间的箭头切换为线条,因为它们更容易绘制,尤其是在接下来的几张图中!但它们仍然全部指向向后。请记住,Git 向后工作;每隔一段时间,了解一下这一点很有用。在这里,这并不重要。)

\n

现在让我们进行更多提交,但在两个不同的分支上像这样br1进行br2

\n
          I--J   <-- br1\n         /\n...--G--H   <-- master\n         \\\n          K   <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n

该名称包含 commit :指向br1的哈希 ID 。名字指向.Jbr1Jbr2K

\n

Git 的棘手之处之一是,现在所有三个分支上的提交H都是如此。(其他版本控制系统不会这样做。)如果我们现在进行新的提交,它会获得另一个新的、唯一的哈希 ID,并且名称会移动到指向它:mastermaster

\n
          I--J   <-- br1\n         /\n...--G--H--L   <-- master\n         \\\n          K   <-- br2\n
Run Code Online (Sandbox Code Playgroud)\n

当您将提交添加到存储库时,现有提交不会以任何方式发生更改。(它们实际上无法更改,因为它们唯一的哈希 ID 只是其内容的校验和。如果您更改任何提交的任何内容,您得到的只是一个新的、不同的、唯一的提交,具有新的、不同的、唯一的哈希 ID。 )但是分支名称动了!根据定义,分支名称始终指向分支中的最后一次提交。

\n

现在我们可以画出你的情况

\n

让我们转到您的Git 存储库,或者靠近它的存储库。我不知道你总共有多少提交\xe2\x80\x94可能比我可以使用的26个大写字母\xe2\x80\x94多得多,但是让我们这样画:

\n
...--G   <-- origin/Dev-Branch\n      \\\n       H--I--J--K--L   <-- Dev-Branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n

这里HEAD表示这是您现在已经签出的分支。如果你有很多分支\xe2\x80\x94,甚至只有两个\xe2\x80\x94,我们需要知道你在“哪个”,这样就git status可以说on branch Dev-Branch,这样当你进行新的提交时,Git 就知道要移动的分支名称。

\n

我们origin/Dev-Branch输入的是远程跟踪名称。你的 Git 已经与他们的 Git\xe2\x80\x94 进行了对话origin,这个名称保存了你的 Git 用来与他们对话的 URL\xe2\x80\x94 在某个时刻,他们说我的Dev-Branch名字提交了G,所以你的 Git 有你的origin/Dev-Branch指向(共享)提交G.

\n

同时,你Dev-Branch要承诺的要点L

\n

提交总是向后指向。新提交指向您进行提交时所做的任何提交,因此L指向K,指向J,依此类推。

\n

如果从 开始计数L,并在到达指定的提交时停止,那么有多少次提交origin/Dev-Branch

\n

领先5,落后1

\n

现在,假设要运行git fetch ,并且他们有一个新的提交\xe2\x80\x94,让我们称之为它NM由于某种原因跳过提交后立即出现的\xe2\x80\x94H,你最终会在存储库中得到这个:

\n
...--G-----------N   <-- origin/Dev-Branch\n      \\\n       H--I--J--K--L   <-- Dev-Branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n

那是因为你的 Git 会询问他们的 Git Dev-Branch,他们会说“哦,那是提交N”。您的 Git 将获得提交N,然后看到您已经提交G并完成了该阶段,然后您的 Git 将更新您origin/Dev-Branch以指向N.

\n

现在,如果您有git status计数提交,那么您的提交中有多少Dev-Branch共享? 您有多少提交origin/Dev-Branch共享? (请注意,此处共享意味着这两个名称之间。因此,提交G- 和之前的内容共享的,但 H 和之后的内容不是共享的。我们不关心另一个 Git 中的实际内容,只关心我们的 Git 记住它们的内容吉特。)

\n

假设这是他们存储库中的实际情况(他们有 commit N)。N即使您自己的存储库中没有/没有,您现在也可以运行git push. 你的 Git 会调用他们的 Git 并向他们发送你的H-I-J-K-L链,现在他们将拥有与我们这里相同的绘图(但使用他们的名字,而不是你的名字)。然后你的 Git 会要求他们 更改Dev-Branch指向 commit L

\n
...--G-----------N   <-- Dev-Branch [in origin]\n      \\\n       H--I--J--K--L   <-- proposed new Dev-Branch\n
Run Code Online (Sandbox Code Playgroud)\n

如果他们将自己的Dev-Branch名字移动到所指向的位置L,会发生什么情况N?答案是:它实际上什么也没发生,但现在,他们没有它的名字,也找不到它了。箭头只能向后移动:没有​​办法从GN,只能从NG。所以如果你这样做,他们只会说不,我不会移动我的Dev-Branch. (他们会称之为非快进。)

\n

此时,您需要在自己的存储库中进行合并提交,或者确保它们不会丢失提交N。此类合并可能如下所示:

\n
...--G--------------N   <-- origin/Dev-Branch\n      \\              \\\n       H--I--J--K--L--M   <-- Dev-Branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n

您的新合并提交 M将引用您现有的提交L,但也会引用他们的(现在也是您的)提交N。(如果您还没有N,则需要git fetch先获得,才能获得 N。)

\n

成功后git push,更新您的图纸

\n

我们再回到这张图:

\n
...--G   <-- origin/Dev-Branch\n      \\\n       H--I--J--K--L   <-- Dev-Branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n

你跑git push(或git push origin Dev-Branch)。你的 Git 调用他们的 Git,H-I-J-K-L如果他们没有的话给他们提交\xe2\x80\x94如果他们有的话,你的 Git 发送他们仍然需要的那些\xe2\x80\x94,然后要求他们将其设置Dev-Branch为点提交L。他们说“好吧,我做到了”,所以你的 Git 更新你的 origin/Dev-Branch以记住他们接受了你的请求:

\n
...--G\n      \\\n       H--I--J--K--L   <-- Dev-Branch (HEAD), origin/Dev-Branch\n
Run Code Online (Sandbox Code Playgroud)\n

现在,当git status计算提交时,它将找出您的 上有多少提交Dev-Branch未与您共享origin/Dev-Branch(您的 Git 对其的内存 Dev-Branch,以及您的 上有多少提交origin/Dev-Branch未与您自己的共享Dev-Branch。由于这两个名称完全匹配,因此前面没有提交,后面也没有提交。

\n

这一切都基于您在当地拥有的任何信息。 此时他们的 Git 有什么并不重要。他们的 Git 拥有的内容在你运行时git fetch和运行时很重要git push,但在你运行时并不重要git status

\n

当您查看各种提交时,例如L,Git 会向您显示该提交中的内容。任何元数据都无法更改。任何快照都无法更改。元数据表明提交是昨天进行的,所以这就是您将看到的内容。

\n

(为了显示快照,Git 实际上会检索 中的快照L 及其直接父级中的快照K。然后 Git 会比较两个快照以查看发生了什么变化。 一般来说,发生变化的内容比所有内容更有用,当你想查看这样的提交时。但每个提交仍然是完整的快照。)

\n