重做之后,Git提交在同一分支中被复制

我了解Pro Git中提供的有关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现在包含两个相同的提交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之后应用C5C6
  • 我怎样才能避免这种情况? 这样做明智吗?

  • 你不应该在这里使用rebase,一个简单的合并就足够了。 你链接的Pro Git书基本上解释了这种确切的情况。 内部工作原理可能略有不同,但以下是我如何可视化它:

  • C5C6暂时从dev退出
  • C7应用于dev
  • C5C6C7之上播放,创建新的差异并因此创建新的提交
  • 因此,在您的dev分支中, C5C6实际上不再存在:它们现在是C5'C6' 。 当你推到origin/dev ,git将C5'C6'视为新的提交并将它们粘贴到历史记录的末尾。 事实上,如果你看看origin/dev C5C5'之间的区别,你会注意到虽然内容相同,但行号可能不同 - 这使得提交的散列不同。

    我将重申Pro Git规则: 永远不要重新提交曾经存在但除了本地存储库之外的任何提交 。 改用合并。


    简短的回答

    您省略了您运行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来通过你的post-rebase提交来更新远程(按照user4405677的回答)。
  • 与多个开发人员同时在一个分支上工作,那么你可能不应该首先使用git rebase 。 要更新dev与变更master ,你应该,而不是运行git rebase master dev ,运行git merge master ,而对dev (按照贾斯汀的答案)。
  • 稍微长一点的解释

    Git中的每个提交哈希都基于许多因素,其中之一就是提交之前提交的哈希。

    如果您对提交进行重新排序,您将更改提交哈希值; 重新定义(当它做了什么)将改变提交散列。 因此,运行git rebase master dev (其中devmaster不同步)的结果将创建具有与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之前:

    查看reflog( git 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
    

    在重复提交之前显示线性提交的Git日志

    或者,以另一种方式显示:

    在重复提交之前显示线性提交的日志图形

    在这种情况下,我们想删除重复的提交,但保留我们基于它们的提交 - 我们希望通过C10保留C6。 与大多数情况一样,有很多方法可以解决这个问题:

    或者:

  • 在最后一个重复的commit1中创建一个新分支,将每个提交(C6到C10) cherry-pick到该新分支上,并将该新分支视为规范。
  • 运行git rebase --interactive $commit ,其中$commit是在重复提交之前的提交2。 在这里我们可以彻底删除重复的行。
  • 1你选择哪一个,无论ba7688a还是2a2e220正常工作。

    2在这个例子中它将是85f59ab

    TL; DR

    advice.pushNonFastForward设置为false

    git config --global advice.pushNonFastForward false
    

    我想你在描述你的步骤时跳过了一个重要的细节。 更具体地说,你最后一步, git push dev,实际上会给你一个错误,因为你通常不会推送非快速更改。

    所以你在最后一次推送之前做了git pull ,这导致C6和C6'作为父母的合并提交,这就是为什么两个都将保持在日志中列出的原因。 更漂亮的日志格式可能会使它更加明显,它们是重复提交的合并分支。

    或者你做了一个git pull --rebase (或者没有显式的--rebase如果它被你的配置隐含的话),它将原来的C5和C6拉回到你的本地开发中(并且进一步将以下的哈希值重新转换为新的哈希值,C7'C5“'C6”')。

    解决这个问题的一个办法可能是git push -f在发出错误并从原点擦除C5 C6时强制推送,但如果有其他人在你擦除它们之前也拉动了它们,那么你会花费很多时间更麻烦......基本上每个拥有C5 C6的人都需要采取特殊措施来摆脱它们。 这正是他们说为什么你不应该改变已经发布的任何东西的原因。 不过,如果说“发布”属于一个小团队,那还是可行的。

    链接地址: http://www.djcxy.com/p/48927.html

    上一篇: Git commits are duplicated in the same branch after doing a rebase

    下一篇: Accidentally pushed commit: change git commit message