git不会合并 - 说'已经是最新的'。什么时候不是

时间:2016-04-22 21:47:30

标签: git

我有一个我从未遇到的git问题。

我有2个分支,有2个文件不同。
如果我将一个合并到另一个(任何一个方向),它会说“已经是最新的”。

为什么它认为它们是最新的,我怎样才能让它合并?

我读过这篇文章git merge does not merge(和其他人),他们都说“如果它说得最新,那么你已经正确合并了。”

我意识到git 认为它们被合并了,但它们不是 - 我在2个分支中的文件不同。

即使我进入并进行更改并且我合并它仍然说'已经是最新的'。

我可以让git“忘记”我已合并它们并再次合并吗?

我不知道在这种状态下发生了什么,但这是一场噩梦。必须有一些方法来解决这个问题。

用我所做的更新:

我是唯一一个在此工作的人。我被要求添加一些新功能,所以我从master,new_features创建了一个分支,并开始研究它们。然后我被告知将它放在一边并开发更高优先级的新功能,所以我在master,new_features2上创建了另一个分支,然后我开始研究这些功能。然后我被告知我们需要一些bug_fixes,然后我做了另一个分支,bug_fixes。我修复了bug并将bug_fixes合并到master中。然后我回到工作new_features2,让那些东西工作,将master合并到new_features2,测试,然后将new_features2合并到master。到目前为止都很好。

现在我回到了new_features工作。我完成了这些工作,并将master合并到new_features中。它合并了一些东西。我测试,我发现new_features2的东西和bug_fixes都没有正常工作。我看一下合并的代码,似乎new_features2和bug_fixes的一些变化都在new_features中,但不是全部。我尝试再次合并 - 它说“已经是最新的。”

1 个答案:

答案 0 :(得分:5)

大编辑:你可能想要改变。

我把rebase部分放在顶部,但你可能想先阅读其余部分。

衍合

git中最有用的东西之一就是挑选一个完整的" branchlet" (以分支提示结尾的提交字符串)实际上移动它们。也就是说,假设您现在对采摘樱桃感到满意(如果需要,请参见下文),让我们假设您在提交图中有类似的内容:

             o--o         <-- fix-bug-1234
            /    \
...--o--o--o---o--o--o    <-- develop
      \
       o--o--o--o         <-- feature

你刚刚开始feature工作,然后不得不对develop进行一些紧急更改,并且现在已经合并了一个紧急错误修复程序。现在是时候回到feature上工作了。

但是,确定feature的{​​{1}}版本从develop中的最新好东西开始,确实会很不错。

所以,让我们做到这一点。我们首先根据develop的提示检查新的功能分支:

git checkout -b new-feature develop

给我们这个:

             o--o         <-- fix-bug-1234
            /    \
...--o--o--o---o--o--o    <-- develop, HEAD->new-feature
      \
       o--o--o--o         <-- feature

现在我们只是挑选所有四个feature提交。我们可以编写feature~4..feature,但这需要我们计算提交,所以既然我们知道所有关于git set符号, 1 我们使用develop..feature来找到同一组提交。这允许我们使用第二个git命令:

git cherry-pick develop..feature

复制这四个提交:

             o--o                   <-- fix-bug-1234
            /    \
...--o--o--o---o--o--o              <-- develop
      \               \
       \               o--o--o--o   <-- HEAD->new-feature
        \
         o--o--o--o                 <-- feature

现在我们将feature重命名为old-feature,然后将new-feature重命名为feature

git branch -m feature old-feature
git branch -m new-feature feature

或者我们使用git rebase以简单的方式完成所有这些操作。我们不必从git checkout -b开始,然后是git cherry-pick和两个重命名步骤,我们只需执行一个git checkout后跟一个git rebase

git checkout feature
git rebase develop

rebase命令使用我们的一个参数develop来:

  • 弄清楚哪些提交到了挑选:develop..feature
  • 查看复制位置:在develop
  • 的提示之后

rebase有更好的形式让我们将提交列表与樱桃选择分开,但对于典型情况,我们不需要这些

请注意,当我们执行rebase时,rebase命令通常忽略复制中的合并提交,但副本提交&#34;后面&#34;合并提交,除非它们被设置符号(develop..feature东西)排除。当序列中有合并提交时,这会产生令人惊讶的结果。

此外,因为重新定位副本提交 - 实际上是一系列的挑选操作 - 如果你<你只能100%安全地执行此操作/ em>是唯一拥有这些提交原件的人。如果分享您的作品的人(通过提取或推送)也有原件,他们可能正在使用它们,并且他们可能会重新引入这些原件以及您的重新定位的副本。 如果您是唯一拥有原件的人,那么这不是问题。如果那些的其他人都知道所有关于您的重新定位的副本,他们可以处理这个问题他们自己(虽然他们可能想要)。

1 如果你没有知道这套符号,你就会遗漏一些使git生活更轻松的东西。

你错过了合并的重点 - 或者说合并点缺少你想做的事情会更准确。

让我们首先看看合并点。

假设你和Alice正在做两件事。你应该让小部件左右切片,而不是仅向右切片。爱丽丝应该让横杆保持更好。

你们两个都是从一个公共基础开始的,其中小部件只向右切片,而且交叉开关会中断。

本周你设法让小部件向左切片,并且Alice修复了横杆。所以你(或爱丽丝)现在想要把你的工作和她的工作结合起来:

$ git checkout larry-widget-fixes
$ git merge alice-crossbar-fixes

如果这使得您的所有文件都与Alice的文件完全匹配,那么将从本周撤消您的所有工作

因此,git merge 不会使事情匹配。相反,它会返回公共基础以查看(1)您更改的内容,以及(2)Alice更改的内容。然后,它尝试将这两组更改合并为一个组合(合并)更改。

在这种情况下,它通过她的更改添加到您的身份来实现(因为我们将她的工作合并到您的工作中,而不是将您的工作合并到她的工作中)。

请注意,合并基础 - 共同的起点 - 是此处的关键项目之一。

如果你真的不想合并怎么办?

我们假设你不想结合一些变化。你想要使一些文件匹配。

有很多方法可以做到这一点,具体取决于你想要的结果。让我们看看其中三种方式。

  1. 通过提取其他版本,只需将一个文件匹配。

    让我们说在修理小部件时,你不小心打破了垫片。 Alice的分支有shims.data的好副本(就合并基础而言,就此而言,让我们只使用Alice这里):

    git checkout alice-crossbar-fixes -- shims.data
    

    这会从Alice的分支提示中提取文件shims.data的版本,现在您拥有了所需的文件。

  2. 只需通过提交ID取消一次提交。

    让我们说你意识到你在提交badf00d中打破了垫片,你在那里做的所有其他改变也应该撤消,或者撤销它们也没关系。然后你可以运行:

    git revert badf00d
    

    git revert命令将给定的提交转换为补丁,然后反向应用补丁,以便取消该提交的效果,并从结果中进行新的提交。

  3. 你想完全放弃一切。你意识到你本周所做的一切,虽然它使小部件向左和向右切片,但这是一个错误,因为小部件根本不应该向右切片。您可以在此处使用git reset

    我不会举一个例子,因为这(a)这可能不是你想要的; (b)git reset过于复杂(git人们将大约五到八个逻辑上不同的操作塞进一个命令中); (c)StackOverflow上已经有很多很好的例子。 (对于步骤2中的git revert也是如此,但git revert更加简单,所以我把它留在了。)

  4. 如果您进行了错误的合并怎么办?

    这更复杂。

    请记住,当我们在第一个例子中进行合并时,我们有一个共同的起点,在您开始之前的一周开始,Alice开始了不同的任务:

    ...--o--*--o--o--o    <-- larry-widget-fixes
             \
              o-o-o-o-o   <-- alice-crossbar-fixes
    

    公共合并基础是提交*

    但是,一旦完成合并,我们就有了这个(我将标记为合并提交M1以便于查看):

    ...--o--o--o--o--o--M1   <-- larry-widget-fixes
             \         /
              o-o-o-o-o      <-- alice-crossbar-fixes
    

    现在假设Alice再做一些修复,让你再做一次修改:

    ...--o--o--o--o--o--M1-o     <-- larry-widget-fixes
             \         /
              o-o-o-o-*--o--o    <-- alice-crossbar-fixes
    

    现在你再次合并来选择这些:

    git merge alice-crossbar-fixes
    

    和git找到公共合并库。这是两个分支上最近的提交,我再次用*标记。 Git会找出自*以来Alice更改的内容,以及自*以来您更改的内容,并尝试合并这些更改。你已经完成了Alice之前所有的更改 - 他们已经在你的分支中,M1 - 所以git不会再看那些是好事。 Git会尝试将Alice自*以来的新更改添加到自*以来的更改(您的Alice版本更改)。

    但是,如果你弄错了怎么办?如果您的Alice版本在M1中的更改是一堆错误的文件会怎么样?

    此时您必须选择是否重试合并,或者使用git的许多其他工具来解决问题。

    重试合并

    要重试合并,您必须备份到合并发生前的某个点。例如,假设我们在M1之前的提交中绘制一个新箭头,如下所示:

                       ..........<-- retry
                      .
    ...--o--o--o--o--o--M1-o     <-- larry-widget-fixes
             \         /
              o-o-o-o-o--o--o    <-- alice-crossbar-fixes
    

    (想象一下它是一个箭头,而不仅仅是一些蹩脚的ASCII点,无论如何)。我们可以将现有的分支标签留在那里,指向它们的位置,我们只需添加这个新的retry

    git branch retry <commit-id> && git checkout retry
    

    或:

    git checkout -b retry <commit-id>
    

    (这些基本上都做同样的事情,让我们retry指向所需的提交。现在,如果我们这样做:

    git merge alice-crossbar-fixes
    

    git会尝试将M1指向的提交前 - retryalice-crossbar-fixes的(新的,当前的)提示合并。 Git将再次找到一个合并库,这是这两个分支上最近的提交,这是我们原来的合并基础。

    换句话说,我们将重新进行合并,忽略错误的合并M1,但也忽略了从那时起所做的新工作。

    樱桃采摘

    我们可以git checkout -b <commit>在任何特定提交上返回该状态,然后开始执行git cherry-pick之类的操作以重新添加特定提交。

    让我们说我们喜欢commit-before - M1很多(它看起来非常接近),所以我们让新的分支 - 让它称之为{{1这点指向:

    retry

    现在,我们可以使用git checkout -b rework <commit-ID> 从其他提交中进行选择,只提取所做的更改。我们为它提供了更多的提交ID,更多的git cherry-pick字符串,并将该提交之前的状态与该提交的状态进行比较。结果是一个补丁,但事实上,它比常规的diff-only补丁更好一些,原因有两个:

    • 它附加了一个提交消息,git可以重用,
    • 它在提交图中有一个特定的位置,因此git可以(如果需要)找到合并基础并在应用它时进行3向合并!通常这不是什么大问题,但对某些情况来说它非常方便。

    省略第二个优势并将其视为补丁,197a3c2...做的是将补丁应用于当前提交,然后进行新提交,重新使用原始提交消息。

    如果你想以这种方式重新申请多次提交,你可以提交git cherry-pick多次提交,它会全部完成:

    git cherry-pick

    这适用于git cherry-pick xbranch~3..xbranch 上的每个提交,除了从xbranch及更早开始的提交,即我们沿xbranch~3计算三个步骤(假设没有合并...)和应用剩下的三个提交:

    xbranch

    (并参见顶部的变基)