重置后无法推送到分支

148
我们使用git,有一个主分支和开发者分支。我需要添加一个新功能,然后将提交合并到主分支,然后推送主分支到CI服务器。
问题在于,如果我在合并时遇到冲突,在合并完成后我无法推送到我的远程开发者分支(在Github上),直到我拉取我的远程分支。这会导致重复提交。当没有冲突时,工作正常。
问题是:在重新设置基础和解决冲突后,如何同步本地和远程开发者分支,而不创建重复提交?
设置:
// master branch is the main branch
git checkout master
git checkout -b myNewFeature

// I will work on this at work and at home
git push origin myNewFeature

// work work work on myNewFeature
// master branch has been updated and will conflict with myNewFeature
git pull --rebase origin master

// we have conflicts
// solve conflict
git rebase --continue

//repeat until rebase is complete
git push origin myNewFeature

//ERROR
error: failed to push some refs to 'git@github.com:ariklevy/dropLocker.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

// do what git says and pull
git pull origin myNewFeature

git push origin myNewFeature

// Now I have duplicate commits on the remote branch myNewFeature

编辑

看起来这会破坏工作流程:

developer1 正在开发 myNewFeature developer2 正在开发 hisNewFeature 两者都使用主分支 master

developer2 将 myNewFeature 合并到 hisNewFeature 中

developer1 变基,解决冲突,然后强制推送到 myNewFeature 的远程分支

几天后,developer2 再次将 myNewFeature 合并到 hisNewFeature 中

这会让其他开发人员讨厌 developer1 吗?


不是解决方案,只是一个想法。这里的“我们”是指除你以外的团队成员吗?那些比我更懂的人说,如果你分享你的代码,就不应该使用rebase。为什么不直接使用git pullgit merge呢? - AdamT
抱歉,我们是开发团队。 - Matt
1
所以,当你分享代码时,最好不要进行变基操作。这可能会导致你必须执行下面列出的操作(强制推送)。 - AdamT
可能是重复的问题,参考 Git push rejected after feature branch rebase - Barett
虽然那里的 OP 理解了问题,但仍然不是直接的重复,只是想知道更好的方法。即使看一下那个问题,也很好地解释了,可能几乎是一个答案... https://dev59.com/tWox5IYBdhLWcg3w1Xs3 - Frank N
显示剩余3条评论
7个回答

102

首先,你和你的合作者需要确定一个主题/开发分支是用于共同开发还是只是你自己的。其他开发人员知道不要在我的开发分支上合并,因为它们随时会被重新设置基线。通常的工作流程如下:

o-----o-----o-----o-----o-----o       master
 \
   o-----o-----o                      devel0
                \
                  o-----o-----o       devel1

为了保持对远程的最新了解,我将执行以下操作:

 git fetch origin
 git checkout master
 git merge --ff origin/master

我这样做有两个原因。首先,它可以让我在不需要切换到我的开发分支的情况下查看远程更改。其次,它是一种安全机制,以确保我不会覆盖任何未存储/提交的更改。此外,如果我无法快进合并到主分支,那么意味着要么有人重新设置了远程主分支(他们需要受到严厉的责罚),要么我意外地提交到了主分支,并需要清理我的端。

然后,当远程有更改并且我已经快速转发到最新版本时,我将执行rebase:

git checkout devel0
git rebase master
git push -f origin devel0

其他开发者将需要以我的最新版本为基础重新设置他们的开发分支:

git fetch <remote>
git checkout devel1
git rebase <remote>/devel0

这导致历史记录更清晰:
o-----o                                 master
       \
         o-----o-----o                  devel0
                      \
                        o-----o-----o   devel1

请勿随意来回合并提交。这不仅会创建重复的提交,使历史记录难以跟踪,而且从特定更改中找到回归变得几乎不可能(这就是你首先使用版本控制的原因,对吗?)。你遇到的问题正是由于这样做造成的。
另外,听起来其他开发人员可能会向您的开发分支提交代码。您能确认一下吗?
唯一合并的时机是当您的主题分支已准备好被接受到“主分支”中时。
此外,如果多个开发人员正在向同一个存储库提交代码,您应该考虑为每个开发人员创建命名分支以区分开发人员的开发分支。例如:
git branch 'my-name/devel-branch'

因此,所有开发人员的主题分支都驻留在自己的嵌套集中。


1
还有,听起来其他开发人员可能正在向您的开发分支提交代码。您能确认一下吗?是的,他们正在提交代码。 - Matt
你还提醒了我另一个问题。在办公室和家之间工作时,我需要在两个地方合并代码,在一天结束时进行推送,以便我可以从上次离开的地方继续工作。但是,当需要进行变基操作时,所有这些提交会导致混乱。我需要将我的分支视为一个分支并进行变基操作。 - Matt
@Matt 是的,这两个问题会真正破坏你的工作流程。对于第一个问题,我建议与其他开发人员沟通,只有所有者才能提交到命名分支(例如“matt/devel”)。在我看来,任何两个开发人员都不应该提交到同一个分支,这只会造成混乱。对于后一个问题,重新设置和强制推送应该可以保持两个位置的最新状态。 - Trevor Norris
很好的回答。我有一个旁边的问题,为什么你在执行 git push -f origin devel0 时使用了 --force 标志? - Aurelio
2
@Nobita 当 git push 无法快速前进(即 sha 历史记录不匹配)时,您必须强制推送以使用从 git rebase 生成的新历史记录覆盖先前的历史记录。 - Trevor Norris
有没有一种方法可以模仿这个流程,但不使用 git push origin -f 呢? - Jeaf Gilbert

63
你需要强制推送,因为你已经把提交往下移了,而Git期望你在分支的末尾添加提交。 git push -f origin myNewFeature 将解决你的问题。
提示:以上是强制推送的合理用法。永远不要在公开可访问的仓库上重写历史,否则很多人会讨厌你。

1
我认为这是保持工作流程最优化的最佳方式。 - Syed Priom
2
谢谢,伙计。我使用了这个命令来强制将我的本地分支与远程分支合并。 - wei
14
使用 git push --force-with-lease 比使用 git push --force 更安全。 - user5047085
git push --force-with-lease origin HEAD - 假设你的目标分支已经被检出 - Lucaci Sergiu
@LucaciSergiu --force-with-lease 是什么意思? - Nasik Shafeek

33

在这里需要记住的主要是 pull 和 rebase 在幕后所做的事情。

pull 基本上会执行两个操作:获取和合并。当你包含 --rebase 时,它将执行一个变基而不是合并。

变基就像是将自从你分支以来的所有本地更改暂存起来,将你的分支快进到目标上的最新提交,并按顺序取消暂存你的更改。

(这就解释了为什么在进行变基时可能会出现多个冲突解决提示,而在合并时只有一个冲突解决提示。你有机会解决每个正在变基的提交中的冲突,以便保留你的提交。)

你永远不想将变基后的更改推送到远程分支,因为这会重写历史记录。当然,“永远”这个词有点过于绝对,因为几乎总会有例外情况。例如,你需要维护本地仓库的远程版本以在特定环境中工作。

这将需要你有时使用强制方式推送变基后的更改:

git push -f origin newfeature

或者在某些情况下,您的管理员可能已经删除了强制执行的能力,因此您必须删除并重新创建:

git push origin :newfeature
git push origin newfeature

无论哪种情况,如果有其他人与您在远程分支上协作,您必须确保自己知道自己在做什么。这可能意味着您要最初一起使用合并(merges)进行工作,并在转到主分支之前将其重新格式化为更可管理的提交形式,并删除您的工作分支。
请记住,您几乎总是可以利用git的GC来回退,方法是:
git reflog

这是一个非常重要的生命救星,如果你在所有的rebase/conflict管理中迷失了方向,你可以重置回到一个更稳定的状态。


上面的删除和重新创建选项对我非常有效。不允许在我的存储库中强制执行! - Simon Holmes

2
我同意MrCholo的观点,也许Trevor Norris可以考虑更新他的好答案以替换


git push -f origin devel0

使用

git push --force-with-lease origin devel0

2
你需要进行强制推送,即git push -f origin myNewFeature 顺便提一句,你最好确保别人不要基于你的开发分支——通常情况下,你不应该发布重写历史记录的分支(或者说一旦发布就不要重写历史记录)。一种方法是使用类似于wip/myNewFeature这样的分支名称,然后提到wip分支将不时地从主分支上合并。

1
使用 git push --force-with-lease 比使用 git push --force 更安全。 - user5047085
@MrCholo 在 Git 中添加一个类似于正常强制推送的 -f 短选项之前,人们不会开始使用它 :) - ThiefMaster
嗯,是的,我用它在一个自动化脚本中。 - user5047085

2
已经给出的一般答案是使用git push -f origin myNewFeature来推送重定位的更改,这是一个很好的起点。我写这篇文章是为了回答关于它是否会破坏你的工作流程的编辑问题。
如果我们假设您将使用git pull --rebase ...(或其变体)后跟强制推送到远程分支,则在您的示例中打破工作流程的事情是developer2正在将myNewFeature合并到hisNewFeature中。只要没有其他人在该分支上工作,就可以对自己的功能分支进行重定位,因此您需要规则来划分分支领域。
您可以通过a)建立一个规则,即您仅从master合并,或b)创建一个集体develop分支,在此基础上建立自己的myNewFeature分支,并建立一个规则,即您仅从develop合并。然后,master将仅保留里程碑或发布(或者您希望如何设置),而develop将是您将每个功能推送到其他功能分支进行集成时的位置。
我认为这可能被认为是Gitflow工作流程的简化版本。

0

因为这个帖子是this google search的唯一结果,所以我希望将这个命令和其他命令放在一起,供任何疯狂滚动的人使用:

git reset --soft HEAD~1

取消提交您的更改,您的文件将不会被修改,如果您运行git status,它们将恢复为似乎已编辑的状态。


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