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

我理解ProGit中关于的场景。作者主要告诉您如何避免重复提交:

不要重新设置推送到公共存储库的提交的基础

我将告诉您我的特殊情况,因为我认为它不完全适合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/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中的解释,所以我想知道两件事:

  • 为什么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中的每个提交散列都基于许多因素,其中一个因素是它之前提交的散列

    如果您重新排序提交,您将更改提交哈希;重定基址(当它做某事时)将改变提交哈希。这样,运行
    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