Git:当我将不同代码从不同的计算机"推送"到同一个远程分支时会发生什么?

idr*_*ris 2 git push pull

假设我使用两台计算机:说,我在家用计算机上处​​理项目,但前一天我忘记从工作计算机上推送最新修改.

在推/拉的情况下会发生什么?如果我推开计算机,我最终会选择两个分支机构吗?如何解决冲突?

tor*_*rek 5

在这种情况下,它与你和Bob(或Carol或其他任何人 - 不是你)在repos中工作的情况完全相同,你们都是从共享位置克隆的,而其中一个人将更改推回到另一个之前.第一个推动"胜利".

Git不知道或关心你是谁.1 它只是看起来推动推动是否是"快进"操作.如果是这样,默认情况下允许推送.2 当第一个让我们说鲍勃获胜的时候 - 当鲍勃做出推动时,他所拥有的新提交将直接堆叠在接收推送的存储库分支的顶端.那是快进的,所以这是允许的.然后你尝试推送,但你的新提交堆叠在一起,一些提交现在在repo中:

          D-E   <-- branch (updated by Bob)
         /
...-A-B-C
         \
          F-G   <-- what you're trying to push
Run Code Online (Sandbox Code Playgroud)

[注:这是远程看到的,即远程已经branch指向E. branch指向G和你没有DE你的资料库.通过推送,你给遥控器F,G然后让它改变它对branch点的想法.]

您要求遥控器设置branch为指向G.如果遥控器这样做,提交DE迷路,所以它只是告诉你不,除非你强行推动.

即使您也是Bob(在您的另一台计算机上)也是如此.

解决问题:两种方式

在这种情况下,您需要提交Bob推送的提交:

$ git fetch
Run Code Online (Sandbox Code Playgroud)

这将在提交DE,在这种特殊情况下.一旦你拥有了这些,那么你需要修改你想要把以某种方式整合什么D,并E为您FG.

使用git merge和,有两种简单的方法可以做到这一点git rebase.

合并

此时您通常可以简单地运行:

$ git merge
Run Code Online (Sandbox Code Playgroud)

(因为git会知道一个上游).什么git merge是尝试组合分叉开发并创建一个指向两个链的新"合并提交" :

          D-E
         /   \
...-A-B-C     M   <-- branch
         \   /
          F-G
Run Code Online (Sandbox Code Playgroud)

[注意:这里和下面,这是在本地存储库中的内容.]

如果自动合并进展顺利,你应该确保结果是好的,因为git不是魔术,它只是遵循一堆做合并的规则.如果自动合并失败,你必须协助git,因为它的规则没有做到这一点.

如果按这个,则要求遥控器设置branch为指向M.虽然M没有确切的顶部直接堆放E,它确实回来E作为一个其父母的,所以M是一个(稍微复杂)快进.(其实M只是需要有E作为一些的祖先,而不是直接祖先,但在其历史上的任何地方.最关键的是,没有提交将会丢失.)

实际合并的优势在于它显示了真正发生的事情:鲍勃赢得了推进竞赛,然后你调整了你的东西以匹配.缺点是难以通过合并来回溯内容:如果Bob的更改中存在错误,或者您的错误中存在错误,则很难准确判断错误的位置; 历史"看起来很复杂",并发症往往不是很有用.所以这就是人们使用第二种选择的原因.

变基

而不是git merge,你通常可以在这一点上运行:

$ git rebase
Run Code Online (Sandbox Code Playgroud)

(出于同样的原因,您可以在git merge没有额外参数的情况下运行:git通常知道上游并且可以找出要重新绑定的内容).这样做是企图"复制"你的提交,在这种情况下FG,到稍微不同的版本,根本上的最新获取犯,在这种情况下,最终堆栈E.如果一切顺利,结果如下:

          D-E
         /   \
...-A-B-C     F'-G'   <-- branch
         \
          F-G   [to be abandoned]
Run Code Online (Sandbox Code Playgroud)

你原来的F并且G仍然在那里(并且在名称下可用,ORIG_HEAD直到另一个rebase或其他任何覆盖ORIG_HEAD;如果你需要恢复它们,它们也会在默认情况下停留在reflogs中30天)但是很大程度上被遗忘 - 如果rebase发生了好.

与合并案例一样,您需要确保rebase确实有效:即使git认为它一切正常,它只是遵循简单的规则,就像合并一样.3 但通常它只是工作,原始的F,G可以忽略,所以我们可以重绘这样的提交图,调用副本F,G现在:

...-A-B-C-D-E-F-G   <-- branch
Run Code Online (Sandbox Code Playgroud)

如果您现在尝试将其推送到远程,它会发现您只是在最后一次提交的基础上堆叠两个提交:这是一个快进,所以它是允许的.

怎么样git pull

pull脚本实际上只是一个方便的事情,git fetch后面跟着git mergegit rebase.它实际上调用git fetch(以一种稍微复杂的方式,在旧版本的git中,防止一些有用的东西发生,虽然细节在这里不是很重要).当获取完成时,它会逐字调用,git merge除非你已经告诉它使用它git rebase.如果你已经告诉它使用rebase你也可以告诉它是否调用git rebase--preserve-merges.在所有情况下,它以稍微复杂的方式调用合并或rebase.但是,是否以及何时使用--preserve-merges超出了本答案的范围.

在旧的(但不是太旧的)旧版本的git中,如果你已经git pull进行了rebase,它也能够从常规git fetch; git rebase序列不能的上游rebase中恢复.在较新版本的git中,rebase使用新的--fork-point东西来自己解决这个问题,这样pull脚本就没有什么大的优势了.

呃,TL; DR-so,我应该什么时候使用git pull

我曾经避免它,因为它有一堆错误.我认为他们现在大部分已经修好了,所以我主要是为了避免习惯.随意使用它,只记得它只是fetch当时的短手merge- 或 - rebase.决定您是喜欢合并还是重新定义,并相应地设置"基于拉动的基础"设置.和git一样,它太复杂了. 或者git pull像我一样避免,以便您不必弄清楚所有branch.autosetuprebase值的含义.


1,通过它常常使该方法护理,例如,使用ssh和钥匙.但是这种认证发生在涉及git之前; 一旦git运行,它就不在乎了,因为它不需要关心.

即使您创建提交,git也只使用您配置的名称和电子邮件.您可以随时将这些设置为任何内容,因此在特定意义上它仍然不关心您是谁.

2关于什么是允许和不允许的准确规则取决于谁运行你推送的系统."默认默认"规则,即如果设置并且不更改任何内容,则获得的是,如果是快进或者设置了强制标志,则允许推送到分支.如果标签不存在或者设置了强制标志,则允许推送到标签.(某些旧版本的git也会将分支规则应用于标记,但较新的版本更严格.)

3实际上,所有工作都是通过公共代码完成的,即git中的"合并引擎".这几乎用于所有事情:只要git可以在提交图中找到共同的祖先,它就可以进行3向合并.

  • 另请参阅https://github.com/aanand/git-up以获取"git up"扩展,该扩展更新所有远程分支并将当前负责人的工作重新绑定.完全取代我工作流程中的pull fetch和rebase. (2认同)