Git fu:识别“远程对手”背后的提交

时间:2015-08-10 19:50:25

标签: git heroku diff commit git-push

这应该很简单:

我正在尝试在Heroku上将分支(master)推向生产:

$ git push production
To git@heroku.com:my-app.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@heroku.com:my-app.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.

我意识到这意味着远程存储库中的代码比我本地存储库中的代码“更新”。建议是git pull ...,但这是我们只推动的Heroku回购。

我也意识到我可以push -f强制进行更改,但我想了解为什么git会抛出此消息,特别是因为这是一个生产部署。

所以,我想深入了解Git认为错误的内容......如何判断本地丢失了哪些提交?当我将本地大师与制作大师区分开时,我看到我要推出的数十(数百?)个差异线。我想看到的是git 认为存在于本地不存在的production / master上。

2 个答案:

答案 0 :(得分:4)

我怀疑你有一个基本的误解,或者两个在这里误导你的方向错误。在git中,提交只是一个标识源树的某个特定版本的对象(以及通常的提交消息和父提交ID列表;最后一个很重要,因为它们是git如何构造"提交图表" - 导致"分支"在下面)。

这尤其意味着提交不是"提前"或"背后"还要别的吗。它就在那里,或者不在那里:它可以存在或不存在。

在git中 - 显然这是通过heroku暴露的 - 还有一个"分支"的概念。不幸的是,这个术语过度重载。它可以指这些中的一个(或多个):

  • 提交图的一个子集(" DAGlet")
  • 分行标签名称
  • a"远程分支"标签

另请参阅this question以获取更多信息,以及一些漂亮的图形,说明分支标签如何指向git根据每个branch-tip提交中存储的父提交ID构造的提交图的部分。

这些分支标签是"提前"和/或"背后"他们的远程同行。

在这种情况下,您的heroku服务器上有一个共享的远程存储库(您称之为production:这是一个"远程名称"而不是分支名称)。您还在本地计算机上拥有自己的本地存储库(并且您的协作者在其计算机上他们的拥有本地存储库,所有存储库都与单个共享heroku存储库分开)。当一些合作者,让我们称他为鲍勃只是为了方便,做出一些改变和推动,他将他的提交提交给共享存储库 - 但不要'拥有它们,你只有你的提交:你之前共享的所有提交,以及你刚刚制作的一些尚未共享的新提交。

所以,现在Bob已经做了一些新的提交并成功推送了它们。现在他的回购和共享回购大多数都是你提交的,再加上他的新回复。您没有那些新的,但您确实拥有自己的新。也就是说,你有这样的事情:

A <- B <- C <- D <- F   <-- master

此处master分支标签。和AF(请注意,我跳过了E;我们会在短时间内看到为什么会看到您看到的40个字符的SHA-1提交ID的缩写如果你运行git log。我在每次提交之间绘制了向后箭头,因为每次提交都记录了它的父ID(按40个字符的SHA-1编号),所以我们可以说每次提交都指向&#34;它自己的父母。

Bob和heroku改为:

A <- B <- C <- D <- E   <-- master

其中E是Bob推送的新提交。

Git不知道或者真的关心Bob(你也不必关心),当你git push知道它在互联网上调用heroku时所知道的一切 - 电话,通过您的提交F发送,然后要求heroku的git指向标签master以提交F

如果heroku-side git这样做,那么远程存储库将拥有与您拥有的相同的提交链,其中F指向D,其指向C }, 等等。提交E不再有人指向它,因此它会丢失。那就是远程(heroku)git告诉你的git,然后你的git告诉你:如果遥控器做了你的git要求它做出的改变,那么至少有一个提交会丢失(或者,使用--force,你的git会命令它来制作)。

(请注意,即使heroku git丢失了这个提交, Bob 的git也不会受到影响:他仍然会有他的提交E。但这意味着你&# 39; d强迫鲍勃恢复他的工作,也许是告诉heroku git再次忘记你的工作并改用他。他们没办法合作。:-))

至于如何处理,你有两个(或三个取决于你的计算方式)选择:

  • 对Bob进行粗暴对待:用强制推动摧毁他的提交
  • 合作:将您的工作合并或重新组合到Bob的
  • 上面

(如果你将最后一个分成&#34;与&#34合并;作为一个选项,&#34;重新定义到&#34;作为第二个,你有三个选择。)

两者&#34;合作&#34;选项要求您选择Bob的提交。你可以从鲍勃那里得到它,但是现在从heroku那里获得它更容易。为此,您从git fetch production开始:这将获得heroku所拥有的所有提交 - 在这种情况下意味着提交E - 并将它们复制到您自己的本地存储库,把它们放在&#34;远程分支上#34; (旁注:你可能只能运行git fetch,没有远程名称,或git fetch --all;我在这里使用远程名称来使事情明确,并且因为你在git push命令。)

因为你已经命名了heroku remote git production,所以这个&#34;远程分支的名称是&#34; label - 它实际上是您的存储库本地的(在经典的git-confuses-everyone风格:-)) - 将是production/master。在您的存储库中,我们可以绘制更完整的提交图:

                   F   <-- master
                 /
A <- B <- C <- D
                 \
                   E   <-- production/master

现在,您有两个标签指向两个提交,这两个提交与提交AD的公共分支 - 祖先(&#34; DAGlet&#34;)不同。

此时您必须决定是否&#34;合并&#34; (使用两个父母进行新的提交,以保留不同的历史记录)或&#34; rebase&#34; (更改历史记录,将旧提交F复制到与F基本相同的新提交,但作为其父项提交{commit} E而不是commit { {1}})。关于哪种方式是正确的方式存在很多哲学论证,但是现在让我们说'#reb; rebase是正确的方式&#34;,并说明rebase,你可以通过正在运行D

要执行rebase,git所做的就是将原始提交git rebase production/master与其父F进行比较,以查看您所讨论的内容。然后检出提交D使您之前做出的相同的更改,并使用您之前编写的相同消息进行新的提交E,好:

F'

我故意将标签带到这里,因为在完成新提交后,git会做最后一件事:它会删除你的 F / A <- B <- C <- D \ E - F' 标签(指向production)和绘制一个新的F指向副本production,该副本与F'相同。现在你无法看到原文,我们可以删除它,理顺图中的向下扭结,并绘制这个新图:

F

没有理由再将其称为A <- B <- C <- D <- E <- F' ,所以现在我们只需将其称为F',然后重新打开标签:

F

(两个标签现在指向A <- B <- C <- D <- E <- F <-- master, production/master )。它现在看起来好像你从未写过你原来的F;看起来好像你等待Bob完成他的工作,然后根据Bob的最新信息写下你的提交F。而且,现在您可以将提交F推送到heroku远程,因为F的父提交是F,所以如果你得到那个git来使 E指向master,这次它不会丢失任何提交。

这两个步骤 - F,然后git fetch production - 可以使用单个git命令完成,即git rebase production/master。 (令人困惑的用户混淆是使用git的模式 - 这不会在任何地方使用远程分支标签git pull --rebase production master。但是,当您使用两个单独的命令时,必须使用远程分支标签其中包含斜杠。)我经常更喜欢这两个单独的命令,因为这意味着你可以在开始变换之前看到发生了什么

一旦你完成了获取(但既不是合并也不是rebase),要他们拥有的不是你的,反之亦然,给{{1一个额外的参数:

production/master

或:

git log

这个双点语法告诉git&#34;查找从右侧标签开始并通过提交图返回的提交,但是不要显示找到的提交从左边的标签开始。&#34;让我们再次回到中间图:

$ git log master..origin/master

如果我们从$ git log origin/master..master 开始,我们会收到提交 F <-- master / A <- B <- C <- D \ E <-- production/master 。从那里向后工作,我们得到production/master,然后是E,然后继续。从D开始,我们得到C,然后是master,然后是F,依此类推。因此,D表示&#34;显示E,但之后不会显示D或C或之前提交的任何内容&#34;:从{{1}开始工作,它们都可以到达}。这样就向我们展示了我们从远程git中获取的内容,而没有向我们展示我们之前已有的内容。特别是,它显示了我们的主人在&#34;背后&#34; on,这是他们未来&#34;提前&#34;。

如果您反转两个点的左侧和右侧C,它会告诉git选择master..production/masterF上未提交的提交。这是我们的主人在&#34;未来&#34;或他们的后面&#34;背后&#34;。

摘要:使用production/master..master然后使用双点语法查看他们没有的内容,反之亦然。使用master复制您拥有的内容。

答案 1 :(得分:1)

你的小组中的其他人是否同时被推,所以你因为那个而支持Heroku回购?你可以尝试git获取origin --dry-run&#34;看看会得到什么?如果你git获取origin / master&#34;只需将新更改提取到origin / master,就可以执行git日志列出origin / master的提示与本地跟踪master分支之间的差异,以查看delta,例如&#34; git log origin / master - not master&#34;