我理解Pro Git中关于the risks of git rebase
的情景。作者基本上告诉你如何避免重复提交:
不要重新推送已推送到公共存储库的提交。
我将告诉你我的具体情况,因为我认为它并不完全符合Pro Git方案,我仍然最终会提交重复的提交。
假设我与当地同行有两个远程分支机构:
origin/master origin/dev
| |
master dev
所有四个分支包含相同的提交,我将在dev
开始开发:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
在几次提交后,我将更改推送到origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
我必须回到master
进行快速修复:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
回到dev
我对这些更改进行了修改,以便在实际开发中加入快速修复:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
如果我使用GitX / gitk显示提交历史记录,我注意到origin/dev
现在包含两个与Git不同的相同提交C5'
和C6'
。现在,如果我将更改推送到origin/dev
,则结果为:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
也许我不完全理解Pro Git中的解释,所以我想知道两件事:
C5
之后应用C6
和C7
?答案 0 :(得分:80)
您忽略了运行git push
,出现以下错误,然后继续运行git pull
的事实:
To git@bitbucket.org:username/test1.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.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.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
尽管Git试图提供帮助,其“git pull”建议很可能不是你想做的事情。
如果你是:
git push --force
以使用您的rebase后提交更新远程(as per user4405677's answer) git rebase
。要使用dev
的更改来更新master
,您应该在git rebase master dev
(as per Justin's answer)上运行git merge master
,而不是运行dev
。< / LI>
Git中的每个提交哈希都基于许多因素,其中一个因素是它之前提交的哈希值。
如果您重新提交提交,则会更改提交哈希值;变基(当它做某事时)将改变提交哈希。这样,运行git rebase master dev
,dev
与master
不同步的结果将创建具有相同内容的新提交(因此哈希)正如dev
上的那些,但在master
之前插入了提交。
你可以通过多种方式结束这种情况。我能想到的两种方式:
master
提交dev
工作dev
上提交已经被推送到远程的提交,然后您继续进行更改(重新提交提交消息,重新提交提交,压缩提交等)。让我们更好地了解发生的事情 - 这是一个例子:
您有一个存储库:
2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0
然后您继续更改提交。
git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing
(这是你必须接受我的意思:有很多方法可以改变Git中的提交。在这个例子中我更改了C3
的时间,但是你要插入新的提交,更改提交消息,重新排序提交,压缩提交等等。)
ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0
重要的是要注意提交哈希值是不同的。这是预期的行为,因为你已经改变了关于它们的东西(任何东西)。这没关系,但是:
尝试推送会显示错误(并提示您应该运行git pull
)。
$ git push origin master
To git@bitbucket.org:username/test1.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@bitbucket.org:username/test1.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.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
如果我们运行git pull
,我们会看到此日志:
7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
或者,以另一种方式显示:
现在我们在本地有重复的提交。如果我们要运行git push
,我们会将它们发送到服务器。
为了避免进入这个阶段,我们可以运行git push --force
(我们改为运行git pull
)。这会将我们的新哈希提交发送到服务器而不会出现问题。要在此阶段解决问题,我们可以在运行git pull
:
在我们运行git reflog
之前,查看reflog(git pull
)以查看提交哈希的内容。
070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0
上面我们看到ba7688a
是我们在运行git pull
之前所做的提交。使用该提交哈希值,我们可以重置为该值(git reset --hard ba7688a
),然后运行git push --force
。
我们已经完成了。
如果你以某种方式没有注意到提交是重复的并且继续在重复提交之后继续工作,那么你真的为自己弄得一团糟。混乱的大小与重复顶部的提交数量成正比。
这是什么样的:
3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0
或者,以另一种方式显示:
在这种情况下,我们希望删除重复的提交,但保留我们基于它们的提交 - 我们希望保留C6到C10。与大多数事情一样,有很多方法可以解决这个问题:
或者:
cherry-pick
每次提交(包括C6到C10)到新分支上创建一个新分支,并将该新分支视为规范。git rebase --interactive $commit
,其中$commit
是提前提交到重复的提交 2 。在这里,我们可以直接删除重复项的行。 1 您选择的两个中的哪一个无关紧要,ba7688a
或2a2e220
都可以正常工作。
2 在示例中,它将是85f59ab
。
将advice.pushNonFastForward
设为false
:
git config --global advice.pushNonFastForward false
答案 1 :(得分:73)
你不应该在这里使用rebase,简单的合并就足够了。你链接的Pro Git书基本上解释了这个确切的情况。内部工作可能略有不同,但这是我如何想象它:
C5
和C6
暂时退出dev
C7
已应用于dev
C5
和C6
在C7
之上播放,创建新差异,因此新提交因此,在您的dev
分支中,C5
和C6
实际上不再存在:它们现在是C5'
和C6'
。当您推送到origin/dev
时,git会将C5'
和C6'
视为新提交,并将其固定到历史记录的末尾。实际上,如果您查看C5
中C5'
和origin/dev
之间的差异,您会注意到虽然内容相同,但行号可能不同 - 这使得提交的哈希值不同。
我将重述Pro Git规则:永远不会重新提交除本地存储库以外的任何地方的提交。改为使用合并。
答案 2 :(得分:12)
我认为您在描述步骤时跳过了一个重要的细节。更具体地说,您在dev上的最后一步git push
实际上会给您一个错误,因为您通常无法推送非快速更改。
所以你在最后一次推送之前做了git pull
,这导致了C6和C6'作为父项的合并提交,这就是为什么两者都将保留在日志中的原因。一个更漂亮的日志格式可能使它们更明显地是重复提交的合并分支。
或者你创建了一个git pull --rebase
(或者如果你的配置暗示没有明确--rebase
),而是将原来的C5和C6拉回到你的本地开发中(并进一步重新定位跟随新哈希,C7'C5''C6'')。
出于这种情况的一种方法可能是git push -f
在发出错误时强制推动并从原点擦除C5 C6,但是如果其他人在你擦拭它们之前也将它们拉了,你就会在更麻烦的是......基本上每个拥有C5 C6的人都需要采取特殊措施来摆脱它们。这正是为什么他们说你永远不应该重新发布任何已发布的内容。如果说“发布”在一个小团队中,它仍然可行。
答案 3 :(得分:1)
我发现在我的情况下,这个问题是Git配置问题的结果。 (涉及拉动和合并)
问题描述:
Sympthoms:在rebase之后提交在子分支上重复,这意味着在rebase期间和之后有很多合并。
<强>工作流:强> 以下是我正在执行的工作流程的步骤:
作为此工作流程的后果,自上次更改以来,“功能分支”的所有提交都重复...: - (
问题是由于在rebase之前拉动子分支的变化。 Git默认拉取配置是“合并”。这是更改在子分支上执行的提交的索引。
解决方案:在Git配置文件中,配置pull以在rebase模式下工作:
...
[pull]
rebase = preserve
...
希望它可以提供帮助 JN Grx
答案 4 :(得分:0)
您可能已从不同于当前分支的远程分支拉出。例如,当您的分支机构正在开发跟踪开发时,您可能已退出Master。如果从非跟踪分支中提取,Git将尽职地提取重复的提交。
如果发生这种情况,您可以执行以下操作:
git reset --hard HEAD~n
其中n == <number of duplicate commits that shouldn't be there.>
然后确保从正确的分支中拉出,然后运行:
git pull upstream <correct remote branch> --rebase
使用--rebase
进行拖拽将确保您不会添加无关紧要的提交,而这些无关紧要的提交会混淆提交历史。