git - git将不会 merge -说“已经是最新的”。当不是

标签 git

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

我有2个分支,其中有2个不同的文件。
如果我将一个合并到另一个中(在任一方向上),它会显示“已更新”。

为什么它认为它们是最新的,以及如何合并它?

我阅读了git merge does not merge(和其他)信息,他们都说:“如果它是最新信息,则说明您已正确合并。”

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

即使我进行更改并合并,它仍然会显示“已经是最新的”。

我可以让git来“忘记”我已经合并的它们,然后再次合并吗?

我不知道在这种状态下会发生什么,但这是一场噩梦。必须有某种方法可以解决此问题。

更新我的工作:

我是唯一从事此工作的人。我被要求添加一些新功能,因此我创建了master,new_features分支,并开始进行开发。然后,我被告知将其搁置一旁,并开发更高优先级的新功能,因此我又从master分支了另一个分支new_features2,然后开始着手开发这些功能。然后我被告知我们需要一些bug_fixes,然后我又在master分支中创建了一个bug_fixes。我修复了错误,并将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中,但并非全部。我尝试再次合并-它说“已经是最新的”。

最佳答案

大型修改:您可能想重新设定基准。

我将rebase部分放在顶部,但您可能需要先阅读其余内容。

变基

git中最有用的事情之一是挑选整个“branchlet”(一串以分支尖端结尾的提交),以有效地移动它们。也就是说,假设您现在对选择樱桃感到满意(如果需要,请参阅下文),假设您在提交图中有类似以下内容:

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

您前一段时间开始了feature的工作,然后不得不对develop进行一些紧急更改,并重新合并了一个紧急错误修复程序;现在是时候回到feature了。

不过,从feature的最新好东西开始,使用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表示法一无所知,因此我们可以使用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命令通常会忽略复制中的合并提交,但除非在设置符号(develop..feature东西)中将它们排除,否则复制提交将在合并提交之后“提交”。当序列中有合并提交时,这会产生令人惊讶的结果。

    此外,(因为重新定型副本将提交)(这确实是一系列的樱桃选择操作),如果您是唯一拥有这些提交原始内容的人,则这样做只有100%安全。如果共享您的作品(通过抓取或推送)的人也拥有原件,则他们可能正在使用它们,并且他们可能会在重新定型的副本中重新引入这些原件。 如果您是唯一拥有原始作品的人,那么这不是问题。 如果拥有副本的其他人都知道您重新设置了副本的副本,那么他们可以自己解决问题(尽管他们可能不想这样做)。

    1如果您不知道此设置符号,则可能会丢失一些使git生活变得轻松得多的东西。

    您错过了合并的要点-或者说合并点缺少要执行的操作可能更准确。

    首先让我们看一下合并点。

    假设您和爱丽丝在做两件事。您应该获得可向左和向右切片的小部件,而不是仅向右切片。爱丽丝应该使横梁保持得更好。

    你们俩都从一个共同的基础开始,在这个基础上,小部件仅向右切片,而交叉开关中断。

    本周,您设法使小部件向左切片,而Alice修复了交叉开关。因此,您(或爱丽丝)现在想将您的工作和她的工作结合起来:
    $ git checkout larry-widget-fixes
    $ git merge alice-crossbar-fixes
    

    如果这样做使您的所有文件与Alice的文件完全匹配,那将撤消本周的所有工作。

    因此,git merge不会使事情匹配。取而代之的是,它返回到通用基础,以查看(1)您更改了什么,以及(2)Alice更改了什么。然后,它尝试将这两组更改合并为一个合并(合并)更改。

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

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

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

    假设您不想合并一些更改。您想改为使某些文件匹配。

    有多种方法可以执行此操作,具体取决于所需的结果。让我们看一下其中的三种方式。
  • 通过提取另一个版本,使一个文件匹配。

    假设在修复小部件时,您不小心摔破了垫片。爱丽丝的分支有一个shims.data的很好的副本(就此而言,合并基础也是如此,但是让我们在这里使用爱丽丝的用法):
    git checkout alice-crossbar-fixes -- shims.data
    

    这将从Alice分支的尖端中提取文件shims.data的版本,现在您有了所需的版本。
  • 通过提交ID撤消一次提交。

    假设您意识到自己已在commit badf00d中破坏了填充,并且您在该处所做的所有其他更改也应该都撤消,或者可以撤消它们。然后可以运行:
    git revert badf00d
    
    git revert命令将给定的提交转换为补丁,然后反向应用补丁,以使该提交的效果未完成,并根据结果进行新的提交。
  • 您想完全放弃一切。您意识到,本周所做的所有操作都会使小部件向左和向右切片,但这是一个错误,因为小部件根本不应该向右切片。在这里您可以使用git reset

    我不会举一个例子,因为这(a)这可能不是您想要的; (b)git reset过于复杂(git伙计们在一个命令中填充了大约五到八个逻辑上不同的操作); (c)StackOverflow上已经有很多很好的例子。 (第2步中的git revert也是如此,但是git revert简单得多,因此我将其保留了下来。)

  • 如果合并不好怎么办?

    这更复杂。

    请记住,在第一个示例中进行合并时,在您和Alice开始执行不同任务之前的一周开始时,我们有一个共同的起点:
    ...--o--*--o--o--o    <-- larry-widget-fixes
             \
              o-o-o-o-o   <-- alice-crossbar-fixes
    

    常见的合并基础是commit *

    但是,一旦完成合并,我们就可以做到这一点(我将合并提交M1标记为易于查看):
    ...--o--o--o--o--o--M1   <-- larry-widget-fixes
             \         /
              o-o-o-o-o      <-- alice-crossbar-fixes
    

    现在,假设爱丽丝进行了更多修复,让我们也进行另一处更改:
    ...--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的所有早期更改-它们已经在您的分支中,使用M1-所以git不再查看这些更改是一件好事。 Git只会尝试将*之后的Alice新更改添加到*之后的更改中(您的Alice更改版本)。

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

    此时,您必须选择是重试合并还是使用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指向的retry之前的commit与alice-crossbar-fixes的(新的,当前的)提示合并。 Git将再次找到合并基础,这是这两个分支上最近的提交,这又是我们原始的合并基础。

    换句话说,我们将重新进行合并,而忽略不良合并M1,而且还将忽略此后所做的新工作。

    采摘樱桃

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

    假设我们非常喜欢M1之前的retry(它看起来确实很近),因此我们创建了新的分支(让我们将其称为git cherry-pick之外的其他名称),它指向此处:
    git checkout -b rework <commit-ID>
    

    现在,我们可以使用197a3c2...从其他提交中选择,从中提取所做的更改。我们给它更多的提交ID,更多的git cherry-pick字符串,并且它将提交之前的状态与该提交时的状态进行比较。结果是一个补丁,但是实际上,它比常规的仅diff补丁要好一些,原因有两个:
  • 它附加了一个提交消息,git可以重复使用,
  • 它在提交图中有特定的位置,因此git可以(如有必要)找到一个合并基础,并在应用时进行三向合并!通常这不是什么大问题,但在某些情况下非常方便。

  • 忽略第二个优点,只是将其视为补丁,git cherry-pick所做的就是将补丁应用于当前提交,然后进行新提交,重新使用原始提交消息。

    如果您想以这种方式重新应用许多提交,可以给xbranch多次提交,它将全部完成:
    git cherry-pick xbranch~3..xbranch
    

    这适用于xbranch~3上的所有提交,但从xbranch及更早版本开始的提交除外,即,我们沿ojit_code倒退了三个步骤(假设没有合并...),并应用其余三个提交:
        N N  Y Y Y   [do we take it?]
    
        | |  | | |
        v v  v v v
    
    ...-o-o--o-o-o   <-- xbranch
    ...-o--o         <-- larry-widget-fixes (before cherry pick)
    

    (并在顶部查看变基)

    关于git - git将不会 merge -说“已经是最新的”。当不是,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36803814/

    相关文章:

    git - Flutter无法切换 channel

    git - 将我所有的 GitHub 存储库转移到 GitLab 的最快方法是什么?

    git - 当我没有做任何更改时,git interactive rebase 中怎么会有冲突?

    git - 看不到远程分支,.git/config 有问题吗?

    git - 为什么我 pull 的时候需要一直做 `--set-upstream-to`?

    android - 如何有效地支持代码中稍有不同的多个Android项目?

    git - 强制 Git 提示输入用户名和密码

    Github 提交(推送) Gist

    git - 将 Visual Studio Online 设置为持续部署 : There are no projects available

    linux - 是否可以在没有 git 前缀的情况下运行 git 命令