通常,当我将提交推送到远程时,它们会被拒绝,因为远程包含我本地没有的工作。我想知道git遵循什么准确的逻辑来得出这个结论?我试过谷歌搜索,但没有找到一个很好的帐户的过程。我可以假设,为了将提交推送到远程分支,这个提交被提交必须让远程分支上的所有提交都可用,因为它是祖先,它是否正确?例如,存在以下内容:
A-B-C (remote master branch)
\
A-D (local master branch)
当我执行git push origin master git接受origin / master上可用的所有提交(可能来自共同祖先 - A
这里)并检查我尝试推送的最后一次提交(现在它是commit { {1}})将它们作为它的祖先。在伪代码中有类似的东西:
D
答案 0 :(得分:2)
远程允许或拒绝推送,具体取决于结果是"快进"。当标签的新目标具有该标签的旧目标作为祖先时,就会发生快进(正如您已经猜到的那样)。
我认为你需要在这里理解两件事。第一个是git基于"引用"这样做,并且如果它快速前进"则允许非强制推送,所以你需要得到(在一些深刻的把握 - 感觉是什么"快进"是
第二种,也许在这种情况下实际上更重要的是,它是远程决定你的操作是否是快进"。这一点非常重要,因为git是分布式的,你的推送可能会在其他人的推动下同时发生 1 。
让我们先处理其中的第一个,因为它更容易一些。正如您已经注意到的那样,"有祖先"是关键的考验。让我像你一样绘制一些提交链,但是我将唯一地标记每个节点并添加一些分支并重新合并。而且,我会添加一些标签,指向每个"最尖端"提交:
B
/ \
A - C - D - E <-- L1
\
F - G <-- L2
现在,假设有另一个标签(例如master
)指向其中一个提交A
到G
。我们可以要求git将任何提交中的任何标签移动到任何其他标签git update-ref
(一个简单地遵循我们的低级命令,而不是检查是否明智之举):
git update-ref <refname> <commit-id>
或者我们可以更安全,并使用更智能,更高级别的命令来检查某些操作是否是快进#34;。什么是快进?
好吧,想象一下我们的标签问题指向提交A
。如果我们告诉git将其移动到任何其他提交,git只测试一件事:&#34;是A
新提交的祖先?&#34;如果是这样,那就是&#34;快进&#34;。如果没有,那就没有了。由于A
是(唯一的)根提交,它始终是每个其他提交的祖先,所以这总是一个快进。
想象一下,我们的标签指向B
。如果我们要求git将其移至A
,则该不会快进。如果我们要求git将其移至F
或G
,那么这些也不是快进操作。即使C
也不是快进,因为C
的唯一祖先是A
。但D
和E
都有B
作为祖先,因此将我们的标签从B
移至E
是快进的。
另一种(可能更简单)的方式是:来自任何提交的,该提交历史记录中的所有提交都是可查找的(&#34;可达&#34 ;),因为每个提交都指向其父级。 (在D
的情况下,它指向B
和C
:它是合并提交并且有两个父母。)鉴于此事实,转发标签移动是一个移动,如果做出,将保留从该标签可以到达的每个提交 ,仍然可从该标签访问。非快进会使一些当前可查找的提交,不再可达 - 至少不是来自该标签。 (其他一些标签,如L1
或L2
,可以在更全面的意义上保持其可达,但在定义快进时并不算数。)
现在让我们来看看第二部分。当您运行git push remote refspec
, 2 时,您的git会联系一个远程git(或git模拟器),并与它进行一些对话:&#34;您拥有什么标签,以及什么是SHA- 1s他们指向?&#34;然后你的git会找出哪些提交和其他对象(1)你有,(2)他们没有,以及(3)如果你的推动要成功他们将需要。你的git将这些包装起来 3 并将它们发送出去。 之后只有 所有这一点,遥控器决定是否允许推送。
一旦遥控器上的所有东西都存在,遥控器会查看你的git给它的内容,以及你的git告诉它设置的标签。它会查看遥控器上那些标签指向现在的位置。如果标签不存在(例如,您正在创建新的分支或标签),那很好(至少到目前为止)。 4 如果标签 存在,标签可能必须移动。
对于分支标签,遥控器将检查该移动是否为远程上的,快进。如果是这样,那么移动是允许的(至少到目前为止)。如果不是,那么这一举动将被拒绝作为一个非快进的&#34;除非你设置了强制标志。 5
(对于标记移动,较旧的gits使用分支规则,而较新的gits除非被强制,否则说不。)
最后,在远程查看了所有建议的标签更改后,它会允许部分或全部,或拒绝其中的部分或全部。然后,一旦完成,遥控器就会有一组新的标签指向新的和更新的,或相同的旧提交。
此时,如果其他人也在排队等待推送,则遥控器允许他们通过交换信息,打包对象和提议标签移动来开始推送。 (事实上,这通常是由服务器上的一个单独的进程处理的,至少假设一个标准的git构建。如果两个已经启动&#34;同时&#34;,他们就有竞争锁定:一个胜利先行,另一个等待直到锁定被释放。如果有两个以上的人被启动,那么再次有胜利者,之后剩余的按钮再次进入锁定状态,依此类推。)
这个简单但有效的规则的原因是,根据定义,快进永远不会输掉提交。它总是简单地向图形添加一些(可能为零) new 提交。可以访问的所有内容仍然可以访问,也许还有更多内容。
如果您想事先知道是否允许推送,您可以连接到遥控器并询问其标签现在的位置。这正是git fetch
的作用:它向遥控器询问遥控器的内容,并记录存储库中的所有内容。现在您可以对存储库中的内容进行测试 - 但是这种方法的缺陷是,无论您得到什么答案,它都可能在您能够运行git push
时过期。但是,通常需要一个非常繁忙的存储库才能成为问题。
1 阿尔伯特爱因斯坦尽管如此:-) ...在这种情况下&#34;同一时间&#34;是真的&#34;由远程&#34;决定,它会阻止其他远程更新,而反之亦然。反之亦然。
2 在这种情况下,即使您拼写master:master
,您的refspec也会为master
。 您的方面最重要的部分是您在左侧标识的提交;但远程中最重要的部分是您在右侧提供的标签名称,因为这是您要求远程更改的名称。
3 使用&#34; smart&#34;协议,这是真的:你的git构建了一个&#34;瘦包&#34;然后发送它。这是&#34;计算对象&#34;和&#34;压缩&#34;是关于:构建包,然后在它上面投入大量的CPU时间,使其尽可能小,然后通过网络发送。
4 遥控器可以拥有&#34; git hooks&#34;这可能会产生额外的要求,例如,只有以用户release
身份进入的人才可以移动master
&#34;或者&#34;任何人都可以创建一个分支,只要它的名字以refs/heads/user
开头,否则只有受祝福的人可以创建它们&#34;。这些天,大多数花哨的控制似乎是用噱头完成的。
5 您可以使用--force
或带有前导加号的per-refspec全局设置此标记。