这应该很简单:
我正在尝试在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上。
答案 0 :(得分:4)
我怀疑你有一个基本的误解,或者两个在这里误导你的方向错误。在git中,提交只是一个标识源树的某个特定版本的对象(以及通常的提交消息和父提交ID列表;最后一个很重要,因为它们是git如何构造"提交图表" - 导致"分支"在下面)。
这尤其意味着提交不是"提前"或"背后"还要别的吗。它就在那里,或者不在那里:它可以存在或不存在。
在git中 - 显然这是通过heroku暴露的 - 还有一个"分支"的概念。不幸的是,这个术语过度重载。它可以指这些中的一个(或多个):
另请参阅this question以获取更多信息,以及一些漂亮的图形,说明分支标签如何指向git根据每个branch-tip提交中存储的父提交ID构造的提交图的部分。
这些分支标签是"提前"和/或"背后"他们的远程同行。
在这种情况下,您的heroku服务器上有一个共享的远程存储库(您称之为production
:这是一个"远程名称"而不是分支名称)。您还在本地计算机上拥有自己的本地存储库(并且您的协作者在其计算机上他们的拥有本地存储库,所有存储库都与单个共享heroku存储库分开)。当一些合作者,让我们称他为鲍勃只是为了方便,做出一些改变和推动,他将他的提交提交给共享存储库 - 但你不要'拥有它们,你只有你的提交:你之前共享的所有提交,以及你刚刚制作的一些尚未共享的新提交。
所以,现在Bob已经做了一些新的提交并成功推送了它们。现在他的回购和共享回购大多数都是你提交的,再加上他的新回复。您没有那些新的,但您确实拥有自己的新。也就是说,你有这样的事情:
A <- B <- C <- D <- F <-- master
此处master
是分支标签。和A
到F
(请注意,我跳过了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再次忘记你的工作并改用他。他们没办法合作。:-))
至于如何处理,你有两个(或三个取决于你的计算方式)选择:
(如果你将最后一个分成&#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
现在,您有两个标签指向两个提交,这两个提交与提交A
到D
的公共分支 - 祖先(&#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/master
上F
上未提交的提交。这是我们的主人在&#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;