Git提交在执行重基后在同一分支中重复
我理解ProGit中关于的场景。作者主要告诉您如何避免重复提交: 不要重新设置推送到公共存储库的提交的基础 我将告诉您我的特殊情况,因为我认为它不完全适合Pro-Git场景,并且我仍然会重复提交 假设我有两个远程分支和它们的本地对应分支:Git提交在执行重基后在同一分支中重复,git,branch,rebase,Git,Branch,Rebase,我理解ProGit中关于的场景。作者主要告诉您如何避免重复提交: 不要重新设置推送到公共存储库的提交的基础 我将告诉您我的特殊情况,因为我认为它不完全适合Pro-Git场景,并且我仍然会重复提交 假设我有两个远程分支和它们的本地对应分支: origin/master origin/dev | | master dev 所有四个分支都包含相同的提交,我将在dev中开始开发: origin/master : C1 C2 C3 C4 maste
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/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
在两次提交之后,我将更改推送到origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
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
现在包含两个相同的提交C5'
和C6'
,它们与Git不同。现在,如果我将更改推送到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中的解释,所以我想知道两件事:
C7
之后应用C5
和C6
您不应该在这里使用rebase,简单的合并就足够了。您链接的Pro Git书籍基本上解释了这种情况。内部工作原理可能略有不同,但我是这样设想的:
和C5
临时从C6
dev
应用于C7
dev
和C5
在C6
之上回放,创建新的差异,从而创建新的提交C7
dev
分支中,C5
和C6
实际上不再存在:它们现在是C5'
和C6'
。当您推到origin/dev
时,git会将C5'
和C6'
视为新提交,并将它们附加到历史记录的末尾。实际上,如果查看origin/dev
中C5
和C5'
之间的差异,您会注意到尽管内容相同,但行号可能不同——这使得提交的哈希不同
我将重申Pro-Git规则:决不要对除了本地存储库之外的任何地方都存在的提交重新设置基础。请改用“合并”。我认为您在描述步骤时忽略了一个重要的细节。更具体地说,您在dev上的最后一步,
gitpush
,实际上会给您一个错误,因为您通常无法推动非快进更改
因此,您在最后一次推送之前执行了git pull
,这导致了以C6和C6'为父级的合并提交,这就是为什么这两个都将保留在日志中的原因。更漂亮的日志格式可能会使它们更明显,它们是重复提交的合并分支
或者你做了一个git-pull--rebase
(或者如果你的配置暗示了它,那么没有显式的--rebase
),它将原来的C5和C6拉回到了你的本地开发中(并且进一步将下面的C5和C6重设为新的哈希,C7'C5''C6'')
解决这个问题的一个方法可能是git push-f
在出现错误时强制推送,并从源代码中擦除C5 C6,但是如果其他人在擦除它们之前也将它们拉出,那么您将面临更多的麻烦。。。基本上,每个有C5 C6的人都需要做一些特殊的步骤来去除它们。这就是为什么他们说你永远不应该对已经发表的任何东西重新定基。不过,如果说“发布”是在一个小团队中进行的,那么这仍然是可行的。简短回答
您忽略了以下事实:您运行了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
- 与多个开发人员同时在一个分支上工作,那么您首先可能不应该使用
。要使用git-rebase
中的更改更新master
,您应该在dev
()上运行dev
,而不是运行git merge master dev
git rebase master dev
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
(这就是你必须相信我的话的地方
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
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
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
git config --global advice.pushNonFastForward false
...
[pull]
rebase = preserve
...
git reset --hard HEAD~n
git pull upstream <correct remote branch> --rebase