推送到新的远程分支时出现了“更新被拒绝”错误

4

我已经fork了一个github仓库,我的fork现在比原始仓库超前几个提交。现在,我想将这些提交中的一个作为PR提供给原始仓库。

按照这个问题的答案所述,我执行了以下操作:

git remote add official [URL to original repo]
git checkout -b hotfix-for-feature official/master
git cherry-pick [feature-hash]
git push -u origin hotfix-for-feature

结果是我得到了:

! [rejected]        hotfix-for-feature -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/myusername/repositoryname'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: (e.g. 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

请注意以下几点:
  1. 在执行git push -u时,分支参数会被完全忽略,它会推送到origin/master。
  2. origin/hotfix-for-featureofficial/hotfix-for-feature都不存在于远程仓库中。
  3. official/master没有做任何更新——我刚刚才拉取过。

如果尝试使用git branch hotfix-for-feature --set-upstream=origin/hotfix-for-feature命令,则会提示应该使用git push -u来实现。 如果尝试使用git push -u origin/hotfix-for-feature命令,则会得到以下提示:

fatal: You are pushing to remote 'origin/hotfix-for-feature',
which is not the upstream of your current branch 'hotfix-for-feature',
without telling me what to push to update which remote branch.

除了作为如何不编写错误消息的良好示例外,我不理解"which"。我指定要推送的内容(当前分支)和要更新的远程分支。

我发现有很多关于此错误的问题,但通常是关于某个远程分支领先于本地分支,在这种情况下没有现有的远程分支。此外,“pushed branch”和“remote [branch]”之间有什么区别?


尝试运行 git push -u origin hotfix-for-feature:hotfix-for-feature - dan1st
@dan1st,没错,那个方法可行。我之前不知道这种语法存在。如果你把它写成答案,我会接受的。 - Richard Vock
还有一件事,涉及到“推送的分支”和“远程[分支]”之间的区别: “推送的分支”是您要尝试推送的本地分支,而“远程”则是在特定错误消息中推送之前在远程已经存在的分支。 - joanis
3个回答

2
您收到以下错误消息:
! [rejected]        hotfix-for-feature -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/myusername/repositoryname'
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: (e.g. 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

重要的部分是

hotfix-for-feature -> master

您正在尝试将本地分支hotfix-for-feature推送到远程分支master

git push -u 的分支参数完全被忽略,它想要推送到 origin/master

它并没有被忽略。它将本地分支hotfix-for-feature推送到远程分支master

git push文档提到了使用refspec

指定用什么源对象更新目标引用。 参数格式是一个可选的加号+,后跟源对象,后跟冒号:,后跟目标引用。

...

在第二个参数中,您不仅可以指定要推送的(本地)分支,还可以指定要推送到的(远程)分支(git push <remote> <sourcebranch>:<destinationbranch>)。

知道这一点,您只需使用git push -u origin hotfix-for-feature:hotfix-for-feature将本地分支hotfix-for-feature推送到远程分支hotfix-for-feature


谢谢您的解释!由于最初已经有了一个已跟踪的主分支,因此我认为没有冒号的git push origin hotfix-for-feature被解释为git push origin hotfix-for-feature:master - Richard Vock
有趣的(奇怪的)一件事情是,refspeb(vs b:b通常 意味着b:b。但是,正如文档所说,“如果git push [<repository>]没有任何<refspec>参数设置为使用remote.<repository>.push配置变量将目标处的某个ref更新为<src>,则可以省略:<dst>部分 - 这样的推送将更新一个不带命令行上的任何<refspec>时<src>通常会更新的引用。否则,缺少:<dst>表示要更新与<src>相同的ref。”因此,这个特定的 repo 必须有一个默认的推送配置。 - torek
最终,我认为这里的真正问题是 Git 在 Git(1.0 之前)的开始时就把 git push 的所有默认设置都搞错了,而自那以后发生的所有补丁都只是让它变得一团糟。现在已经没有办法解决了,因为我们都被训练成期望这种扭曲。 :-) - torek
是的,但冒号语法可以修复导致错误默认值的任何问题。 - dan1st

2

简述:

不要从official/master创建您的分支。

详细说明:

当您首次创建分支时,请先检出official/master,然后执行git checkout -b hotfix-for-feature而不指定应从何处创建。

您也可以执行git checkout -b hotfix-for-feature master,但是明确地说git checkout -b hotfix-for-feature official/master会以您不想要的方式设置上游。

这个问题一开始让我感到惊讶,因为我经常使用几乎与您完全相同的工作流程,但默认情况下git push -u origin newbranchname会在origin上创建分支newbranchname

所以,我尝试了解您的工作流程和我的区别,并且我刚刚找到了答案。

当您运行:

git checkout -b hotfix-for-feature official/master

您特别要求 Git 创建分支 hotfix-for-feature 来跟踪 official/master,这意味着您已经提前使用了 -u 并设置错误。

在我的工作流程中,我会采取以下方式:

git checkout official/master
git checkout -b hotfix-for-feature

创建一个没有上游分支的新分支。

然后,当我按照您的语法推送此分支时,在您的场景中,上游现在设置为您想要的内容:

git push -u origin hotfix-for-feature

origin 上创建一个名为 hotfix-for-feature 的分支,并将 origin/hotfix-for-feature 分支设置为我的本地 hotfix-for-feature 分支。

或者,您可以从本地分支创建新分支,这样也不会设置上游分支:

git checkout -b hotfix-for-feature master

你只需避免在最后一个参数上使用official/master


1
我确实更喜欢那种工作流程 - 我实际上不知道 git checkout official/master 是可行的。虽然对于已经存在的情况,我仍然更喜欢另一个答案作为标记解决方案,但是对于未来的热修复,我绝对会按照您的建议去做。感谢您的努力! - Richard Vock
是的,这种语法会让你进入“分离头指针”状态,通常情况下你不想停留在这个状态,但对于这个工作流程来说,这正是你想要的,因为你将在那里创建一个新分支,并在一秒钟后回到正常的HEAD状态。 - joanis

2
我个人不同意其他答案。(我觉得用不同的名称追踪远程分支很容易混淆,也不喜欢检出共享分支的本地副本。)在分支创建时,我唯一调整的是以下这行代码:
git checkout -b hotfix-for-feature official/master

不想跟踪远程分支,可以像这样操作:
git checkout -b hotfix-for-feature official/master --no-track

请注意,当您从远程分支创建新的本地分支时,一些UI工具会为您执行此操作。例如,在Visual Studio中,有一个名为“Track remote branch”的复选框,默认情况下被选中,直到您将分支名称更改为其他内容时,它会自动为您取消选中该框(并在幕后添加--no-track)。
提示:如果您忘记使用--no-track,可以随时使用以下命令进行更正:
git branch --unset-upstream

这将解决您当前的问题,之后您可以使用 git push -u 进行初始推送以正确设置上游分支。

附注:我可能还会调整同一行的语法,采用更新的 checkout 语法:

git switch -c hotfix-for-feature official/master --no-track

1
啊,我也喜欢 --no-track 这个选项——我之前不知道它的存在,但是我应该料到它会有! - joanis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接