关于版本控制:你什么时候使用git rebase而不是git merge?

When do you use git rebase instead of git merge?

建议何时使用git rebasegit merge

在成功的重新平衡之后,我还需要合并吗?


短版

  • merge接受一个分支中的所有更改,并在一次提交中将它们合并到另一个分支中。
  • Rebase说我希望分支的点移动到新的起点

你什么时候用这两个?

合并

  • 假设您创建了一个分支,目的是开发一个特性。当您希望将这些更改带回master时,您可能希望合并(您不关心维护所有的临时提交)。

重碱

  • 第二种情况是,如果您开始进行一些开发,然后另一个开发人员进行了不相关的更改。您可能希望从repo的当前版本中提取更改,然后重新调整其基值。


这很简单,有了Rebase,您就可以说使用另一个分支作为您工作的新基础。

例如,如果您有一个分支master,并且您创建了一个分支来实现一个新功能,比如说您将它命名为cool-feature,那么主分支当然是新功能的基础。

现在,在某一点上,您希望添加在master分支中实现的新功能。您只需切换到master并合并cool-feature分支:

1
2
$ git checkout master
$ git merge cool-feature

但是这样就添加了一个新的虚拟提交,如果您想避免意大利面历史记录,可以重新设置:

1
2
$ git checkout cool-feature
$ git rebase master

然后在master合并:

1
2
$ git checkout master
$ git merge cool-feature

这一次,由于主题分支具有相同的master提交和带有新特性的提交,合并将只是一个快速前进。


为了补充我刚才提到的答案,

  • 在合并之前,REBASE通常是一个很好的主意,因为它的思想是,将合并所依据的分支Y的工作集成到分支B中。但同样,在合并之前,您可以解决分支机构中的任何冲突(即:"重新平衡",如"从分支机构B的最近点开始在分支机构中重放我的工作")。如果操作正确,那么从分支到分支B的后续合并可以很快进行。

  • 合并直接影响目标分支B,这意味着合并最好是微不足道的,否则分支B可能很长时间才能恢复到稳定状态(解决所有冲突的时间)

the point of merging after a rebase?

在我描述的情况下,我将B重新定位到我的分支机构,只是为了有机会从B最近的一个点重放我的工作,但同时留在我的分支机构。在这种情况下,仍然需要合并以将我的"重放"工作带到B上。

另一种情况(例如,在Git Ready中描述)是通过一个REBASE直接将您的工作带到B中(它保存了您所有的好承诺,甚至给您机会通过一个交互式的REBASE重新订购它们)。在这种情况下(当您在B分支中重新设置基码时),您是对的:不需要进一步合并:

当我们没有合并或重新调整基值时,默认为git树

rebase1

我们通过再平衡得到:

氧化镁

第二个场景是关于:如何将新特性恢复到master中。

我的观点是,通过描述第一个REBASE场景,提醒每个人,一个REBASE也可以作为实现这一点的一个初步步骤(即"将新特性恢复为主特性")。您可以使用rebase将master"引入"新功能分支:rebase将重放来自HEAD master的新功能提交,但仍在新功能分支中,有效地将分支起点从旧的master commit移到HEAD-master。这允许您解决分支中的任何冲突(意味着,在孤立的情况下,如果冲突解决阶段花费的时间太长,则允许master继续并行发展)。然后,您可以切换到master并将new-feature(或者,如果要保留在new-feature分支中完成的提交,可以将new-feature重新设置为master)。

所以:

  • "重新平衡与合并"可以被看作是两种导入工作的方式,例如,master
  • 但是,"重新平衡然后合并"可以是一个有效的工作流程,首先单独解决冲突,然后恢复工作。


DR

如果您有任何疑问,请使用合并。好的。简短的回答

Rebase和Merge之间的唯一区别是:好的。

  • 产生的历史树结构(通常只在查看提交图时才明显)是不同的(一个将有分支,另一个将没有分支)。
  • 合并通常会创建一个额外的提交(例如树中的节点)。
  • 合并和重新平衡将以不同的方式处理冲突。REBASE将一次提交一个冲突,合并将一次提交所有冲突。

因此,简短的答案是根据您希望的历史外观选择REBASE或MERGE。好的。冗长的回答

在选择要使用的操作时,您应该考虑几个因素。好的。您要从与团队之外的其他开发人员(如开源、公共)共享的分支中获取更改吗?

如果是这样,就不要重新平衡。REBASE会破坏分支,而那些开发人员将拥有断开/不一致的存储库,除非他们使用git pull --rebase。这是一个让其他开发人员很快感到不安的好方法。好的。您的开发团队的技能如何?

Rebase是一个破坏性的操作。这意味着,如果不正确地应用它,可能会丢失已提交的工作和/或破坏其他开发人员存储库的一致性。好的。

我曾在团队中工作过,在这个团队中,开发人员都来自一个公司可以负担得起专门的员工来处理分支和合并的时代。那些开发人员不太了解Git,也不想知道太多。在这些团队中,我不会冒险以任何理由建议重新平衡。好的。分支本身是否表示有用的信息

有些团队使用每个特性模型的分支,其中每个分支表示一个特性(或错误修复或子特性等)。在这个模型中,分支帮助识别相关提交集。例如,可以通过恢复该分支的合并来快速恢复功能(公平地说,这是一个罕见的操作)。或者通过比较两个分支(更常见)来区分功能。REBASE会破坏分支,但这并不简单。好的。

我也曾在使用每个开发人员模型的分支的团队中工作过(我们都去过那里)。在这种情况下,分支本身不传递任何附加信息(提交已经有了作者)。再平衡不会有任何危害。好的。是否出于任何原因恢复合并?

与恢复合并相比,恢复(如撤消)钢筋网相当困难和/或不可能(如果钢筋网有冲突)。如果您认为有机会要还原,请使用合并。好的。你在一个团队工作吗?如果是这样,您愿意对这个分支采取全盘还是全盘的方法?

需要使用相应的git pull --rebase来执行重新平衡操作。如果你是一个人工作,你可能会记得在适当的时候应该使用哪一种。如果你在一个团队工作,这将很难协调。这就是为什么大多数REBASE工作流建议对所有合并使用REBASE(对所有拉取使用git pull --rebase)。好的。常见的神话合并销毁历史记录(挤压提交)

假设您进行了以下合并:好的。

1
2
3
    B -- C
   /      \
  A--------D

有些人会说合并"破坏"了提交历史记录,因为如果您只查看主分支(A-D)的日志,您将错过B和C中包含的重要提交消息。好的。

如果这是真的,我们就不会有这样的问题。基本上,您将看到B和C,除非您明确要求不要看到它们(使用——第一个父级)。这对你自己来说很容易。好的。REBASE允许更安全/更简单的合并

这两种方法的合并方式不同,但不清楚一种方法总是比另一种方法好,这可能取决于开发人员的工作流程。例如,如果开发人员倾向于定期提交(例如,当他们从工作过渡到家庭时,可能一天提交两次),那么对于给定的分支可能会有很多提交。这些提交中的许多看起来可能与最终产品不同(我倾向于为每个特性重构我的方法一次或两次)。如果其他人正在处理相关的代码区域,并且他们试图重新平衡我的更改,这可能是一个相当繁琐的操作。好的。Rebase更酷/更性感/更专业

如果您想将rm化名为rm -rf,以"节省时间",那么rebase可能适合您。好的。我的两分钱

我一直认为总有一天我会遇到这样一个场景:Git-Rebase是解决问题的绝佳工具。就像我认为我会遇到这样一个场景:Git reflog是一个很好的工具,可以解决我的问题。我已经和吉特合作五年多了。事情没有发生。好的。

混乱的历史对我来说从来都不是真正的问题。我从来没有像读一本激动人心的小说那样读过我的承诺历史。大多数时候,我需要一段历史,不管怎样,我都会用吉特指责或吉特平分。在这种情况下,提交合并实际上对我很有用,因为如果合并向我引入了有意义的信息。好的。更新(4/2017)

我觉得有义务提到,我个人在使用REBASE方面有所软化,尽管我的一般建议仍然有效。我最近一直在与角2材料项目的很多互动。他们使用REBASE来保持一个非常干净的提交历史记录。这使我能够很容易地看到提交修复了给定缺陷的内容,以及该提交是否包含在一个发布中。它是正确使用REBASE的一个很好的例子。好的。好啊。


这里有很多答案说,合并会将所有提交转换为一个提交,因此建议使用REBASE来保留提交。这是不正确的。如果你已经推动了你的承诺,这是个坏主意。

合并不会抹去你的承诺。合并保留历史记录!(看看Gitk)Rebase重写了历史,这是一件糟糕的事情,在你推了它之后。

使用merge——不要在已经推过的时候重新设置基码。

这是Linus(Git的作者),请接受。这真是一本好书。或者你也可以阅读我自己的观点。

在主服务器上重新设置分支:

  • 提供有关如何创建提交的错误概念
  • 用一堆可能没有经过良好测试的中间提交来污染master
  • 实际上可以在这些中间提交上引入构建中断,因为在创建原始主题分支的时间和重新调整分支的时间之间对master所做的更改。
  • 使在主人那里找好去处结帐变得困难。
  • 导致提交上的时间戳与树中的时间顺序不一致。所以您会看到commit a先于master中的commit b,但commit b是首先编写的。(什么?!)
  • 产生更多的冲突,因为主题分支中的各个提交都可能涉及到必须单独解决的合并冲突(关于每个提交中发生的事情的历史记录中有进一步介绍)。
  • 是对历史的改写。如果正在重新平衡的分支被推到任何地方(与你以外的任何人共享),那么自从你重写了历史之后,你就把所有拥有该分支的人都搞砸了。

相反,将主题分支合并到master中:

  • 保留创建主题分支的历史记录,包括从主分支到主题分支的任何合并,以帮助保持其最新。当开发人员构建代码时,您真的能够准确地了解开发人员使用的代码。
  • master是一个主要由合并组成的分支,每个合并提交通常是历史上的"好点",可以安全地签出,因为主题分支已经准备好集成了。
  • 主题分支的所有单个提交都被保留,包括它们在主题分支中这一事实,因此隔离这些更改是很自然的,您可以在需要时进行钻取。
  • 合并冲突只需解决一次(在合并点),因此主题分支中所做的中间提交更改不必单独解决。
  • 可以顺利完成多次。如果您定期将主题分支集成到master中,那么人们可以继续在主题分支上进行构建,并且可以继续独立地进行合并。


合并意味着:创建一个新的提交,将我的更改合并到目标中。

REBASE的意思是:使用我当前的提交集作为提示,创建一个全新的提交系列。换言之,计算一下如果我从重新平衡的角度开始进行更改,我的更改会是什么样子。因此,在REBASE之后,您可能需要重新测试您的更改,并且在REBASE期间,您可能会有一些冲突。

既然如此,你为什么要重新平衡呢?只是为了保持发展历史的清晰。假设您正在处理功能X,完成后,您将在中合并所做的更改。目的地现在将有一个提交,它将沿着"添加的特性X"这一行说一些话。现在,如果您重新平衡然后合并,那么目的地开发历史记录将在一个逻辑进程中包含所有单个提交,而不是合并。这使得以后查看更改更加容易。想象一下,如果50个开发人员一直在合并各种特性,那么回顾开发历史是多么困难。

这就是说,如果您已经将要处理的分支推到了上游,那么您不应该重新平衡,而应该合并。对于没有被推到上游的分支,重新设置、测试和合并。

另一个时候,您可能希望重新平衡,那就是在向上游推进之前,您希望从分支中除去提交。例如:提交会在早期引入一些调试代码,而其他提交则会进一步清除这些代码。要做到这一点,唯一的方法是执行一个交互式的重新平衡:git rebase -i

更新:在使用Git与不支持非线性历史的版本控制系统(例如Subversion)接口时,您还希望使用Rebase。当使用git svn桥时,非常重要的一点是,合并回Subversion中的更改是主干中最新更改之上的连续更改列表。只有两种方法可以做到这一点:(1)手动重新创建更改;(2)使用REBASE命令,这要快得多。

更新2:考虑一个Rebase的另一种方法是,它支持从开发样式到您要提交的存储库中接受的样式的一种映射。假设你喜欢小而小的投入。您有一个提交来修复拼写错误,一个提交来消除未使用的代码,等等。当你完成了你需要做的事情时,你已经有了一系列的承诺。现在,假设您要提交的存储库鼓励进行大量提交,因此对于您正在进行的工作,一个可能需要一个或两个提交。您如何获取您的提交字符串并将其压缩到预期值?您将使用一个交互式的钢筋,并将您的小提交压缩成更少的更大的块。如果需要反向操作,情况也是如此——如果您的风格是一些大的承诺,但回购需要长串的小的承诺。你也可以用一根钢筋来做这个。如果您已经合并了,那么现在您已经将提交样式移植到主存储库中了。如果有很多开发人员,您可以想象在一段时间后,使用几种不同的提交样式跟踪一个历史是多么困难。

更新3:Does one still need to merge after a successful rebase?是的,是的。原因是,REBASE基本上涉及到"转移"提交。正如我上面所说,这些提交是计算出来的,但是如果从分支点开始有14个提交,那么假设您的REBASE没有任何问题,那么在完成REBASE之后,您将提前14个提交(在您重新平衡的点之前)。你以前有一个分支。之后你会有一个同样长度的树枝。发布更改之前仍需要合并。换言之,根据您的需要多次重新设置基片(同样,只有在您没有将更改推到上游的情况下)。仅在重新设置基后合并。


合并/重新平衡前:

1
2
3
4
A <- B <- C    [master]
^
 \
  D <- E       [branch]

git merge master之后:

1
2
3
4
A <- B <- C
^         ^
 \         \
  D <- E <- F

git rebase master之后:

1
A <- B <- C <- D' <- E'

(a、b、c、d、e和f为承诺)

这个例子和更多关于git的详细说明信息可以在这里找到:http://excess.org/article/2008/07/ogre-git-tutorial/


虽然合并绝对是集成更改的最简单和最常见的方法,但并不是唯一的方法:Rebase是集成的另一种方法。

理解融合得更好一点

当Git执行合并时,它将查找三个提交:

  • (1)公共祖先提交如果您遵循一个项目中两个分支的历史,那么它们总是至少有一个相同的提交:此时,两个分支都有相同的内容,然后演变成不同的内容。
  • (2)+(3)每个分支的端点集成的目标是合并两个分支的当前状态。因此,他们各自的最新修订版具有特殊意义。将这三个承诺结合起来将导致我们的目标集成。

快进或合并提交

在非常简单的情况下,两个分支中的一个自分支发生后没有任何新的提交——它的最新提交仍然是共同的祖先。

enter image description here

在这种情况下,执行集成非常简单:Git只需将另一个分支的所有提交添加到公共祖先提交之上。在Git中,这种最简单的集成形式称为"快进"合并。然后两个分支共享完全相同的历史。

氧化镁

然而,在许多情况下,两个分支都是单独向前移动的。氧化镁

要进行集成,Git必须创建一个包含它们之间差异的新提交——合并提交。

氧化镁

人工提交和合并提交

通常,一个承诺是由一个人精心创造的。它是一个有意义的单元,只包装相关的更改,并用注释对其进行注释。

合并提交有点不同:它不是由开发人员创建的,而是由Git自动创建的。它的目的不是包装一组相关的更改,而是像一个结一样连接两个分支。如果您希望稍后了解合并操作,则需要查看两个分支的历史记录和相应的提交图。

与REBASE集成

有些人宁愿不进行这种自动合并提交。相反,他们希望项目的历史看起来像是在一条直线上发展的。没有迹象表明它在某个时刻被分割成多个分支。

氧化镁

让我们一步一步地完成一个重新平衡操作。该场景与前面的示例中的场景相同:我们希望将branch-b的更改集成到branch-a中,但现在使用rebase。

enter image description here

我们分三步来做

  • git rebase branch-A // syncs the history with branch-A
  • 江户十一〔一〕号
  • 埃多克斯1〔2〕
  • 首先,Git将"撤消"分支A上的所有提交,这些提交发生在行开始分支之后(在公共祖先提交之后)。但是,当然,它不会丢弃它们:相反,您可以将这些承诺视为"暂时被保存了"。

    enter image description here

    接下来,它将应用我们想要集成的branch-b中的提交。此时,两个分支看起来完全相同。

    氧化镁

    在最后一步中,分支A上的新提交现在被重新应用——但是在新的位置上,在分支B的集成提交之上(它们是重新建立的)。结果看起来发展是直线的。而不是包含所有组合更改的合并提交,而是保留原始提交结构。

    enter image description here

    最后,您得到了一个干净的分支——一个没有不需要的和自动生成的提交的分支。

    注:摘自git-tower的《了不起的邮报》。在同一篇文章中,rebase的缺点也是一个很好的读物。


    这句话明白了:

    In general the way to get the best of both worlds is to rebase local
    changes you’ve made but haven’t shared yet before you push them in
    order to clean up your story, but never rebase anything you’ve pushed
    somewhere.

    来源:http://www.git-scm.com/book/en/v2/git分支重新平衡rebase-vs.-合并


    这个答案广泛围绕Git流。这些表是用漂亮的ASCII表生成器生成的,而历史树则是用这个奇妙的命令(别名为git lg)生成的:

    1
    git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

    表按相反的时间顺序排列,以便更符合历史树。另请参见git mergegit merge --no-ff之间的区别,首先(您通常希望使用git merge --no-ff,因为它使您的历史更接近现实):

    江户十一〔一〕号

    命令:

    1
    2
    3
    4
    5
    6
    7
    Time          Branch"develop"             Branch"features/foo"
    ------- ------------------------------ -------------------------------
    15:04   git merge features/foo
    15:03                                  git commit -m"Third commit"
    15:02                                  git commit -m"Second commit"
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    * 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
    |           Third commit - Christophe
    * 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
    |           Second commit - Christophe
    * 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    埃多克斯1〔2〕

    命令:

    1
    2
    3
    4
    5
    6
    7
    Time           Branch"develop"              Branch"features/foo"
    ------- -------------------------------- -------------------------------
    15:04   git merge --no-ff features/foo
    15:03                                    git commit -m"Third commit"
    15:02                                    git commit -m"Second commit"
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    *   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/foo' - Christophe
    | * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
    | |           Third commit - Christophe
    | * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
    |/            Second commit - Christophe
    * c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    git mergegit rebase的比较

    第一点:总是将特性合并到开发中,而不是从特性重新平衡开发。这是重新平衡的黄金法则的结果:

    The golden rule of git rebase is to never use it on public branches.

    换句话说:

    Never rebase anything you've pushed somewhere.

    我个人会补充说:除非它是一个功能分支,并且您和您的团队知道后果。

    因此,git mergegit rebase的问题几乎只适用于特征分支(在下面的例子中,--no-ff在合并时总是被使用)。注意,由于我不确定是否有一个更好的解决方案(存在争论),我将只提供两个命令的行为方式。在我的例子中,我更喜欢使用git rebase,因为它可以生成更好的历史树:)

    特征分支之间江户十一〔一〕号

    命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Time           Branch"develop"              Branch"features/foo"           Branch"features/bar"
    ------- -------------------------------- ------------------------------- --------------------------------
    15:10   git merge --no-ff features/bar
    15:09   git merge --no-ff features/foo
    15:08                                                                    git commit -m"Sixth commit"
    15:07                                                                    git merge --no-ff features/foo
    15:06                                                                    git commit -m"Fifth commit"
    15:05                                                                    git commit -m"Fourth commit"
    15:04                                    git commit -m"Third commit"
    15:03                                    git commit -m"Second commit"
    15:02   git checkout -b features/bar
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    *   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/bar' - Christophe
    | * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
    | |           Sixth commit - Christophe
    | *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
    | |\            Merge branch 'features/foo' into features/bar - Christophe
    | * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
    | | |           Fifth commit - Christophe
    | * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
    | | |           Fourth commit - Christophe
    * | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
    |\ \ \            Merge branch 'features/foo' - Christophe
    | |/ /
    |/| /
    | |/
    | * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
    | |           Third commit - Christophe
    | * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
    |/            Second commit - Christophe
    * 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    。江户十一〔七〕号

    命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Time           Branch"develop"              Branch"features/foo"           Branch"features/bar"
    ------- -------------------------------- ------------------------------- -------------------------------
    15:10   git merge --no-ff features/bar
    15:09   git merge --no-ff features/foo
    15:08                                                                    git commit -m"Sixth commit"
    15:07                                                                    git rebase features/foo
    15:06                                                                    git commit -m"Fifth commit"
    15:05                                                                    git commit -m"Fourth commit"
    15:04                                    git commit -m"Third commit"
    15:03                                    git commit -m"Second commit"
    15:02   git checkout -b features/bar
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    *   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/bar' - Christophe
    | * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
    | |           Sixth commit - Christophe
    | * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
    | |           Fifth commit - Christophe
    | * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
    | |           Fourth commit - Christophe
    * |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
    |\ \            Merge branch 'features/foo' - Christophe
    | |/
    | * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
    | |           Third commit - Christophe
    | * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
    |/            Second commit - Christophe
    * ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    。从develop到一个功能分支江户十一〔一〕号

    命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Time           Branch"develop"              Branch"features/foo"           Branch"features/bar"
    ------- -------------------------------- ------------------------------- -------------------------------
    15:10   git merge --no-ff features/bar
    15:09                                                                    git commit -m"Sixth commit"
    15:08                                                                    git merge --no-ff development
    15:07   git merge --no-ff features/foo
    15:06                                                                    git commit -m"Fifth commit"
    15:05                                                                    git commit -m"Fourth commit"
    15:04                                    git commit -m"Third commit"
    15:03                                    git commit -m"Second commit"
    15:02   git checkout -b features/bar
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    *   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/bar' - Christophe
    | * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
    | |           Sixth commit - Christophe
    | *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
    | |\            Merge branch 'develop' into features/bar - Christophe
    | |/
    |/|
    * |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
    |\ \            Merge branch 'features/foo' - Christophe
    | * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
    | | |           Third commit - Christophe
    | * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
    |/ /            Second commit - Christophe
    | * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
    | |           Fifth commit - Christophe
    | * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
    |/            Fourth commit - Christophe
    * 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    。江户十一〔七〕号

    命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Time           Branch"develop"              Branch"features/foo"           Branch"features/bar"
    ------- -------------------------------- ------------------------------- -------------------------------
    15:10   git merge --no-ff features/bar
    15:09                                                                    git commit -m"Sixth commit"
    15:08                                                                    git rebase development
    15:07   git merge --no-ff features/foo
    15:06                                                                    git commit -m"Fifth commit"
    15:05                                                                    git commit -m"Fourth commit"
    15:04                                    git commit -m"Third commit"
    15:03                                    git commit -m"Second commit"
    15:02   git checkout -b features/bar
    15:01   git checkout -b features/foo
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    *   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/bar' - Christophe
    | * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
    | |           Sixth commit - Christophe
    | * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
    | |           Fifth commit - Christophe
    | * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
    |/            Fourth commit - Christophe
    *   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
    |\            Merge branch 'features/foo' - Christophe
    | * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
    | |           Third commit - Christophe
    | * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
    |/            Second commit - Christophe
    * d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    号旁注埃多克斯1〔17〕

    当您只需要一个特定的提交时,git cherry-pick是一个很好的解决方案(-x选项在原始提交消息体上附加了一行"cherry picked from commit…",因此使用它通常是一个好主意—git log 来查看它):

    命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Time           Branch"develop"              Branch"features/foo"                Branch"features/bar"          
    ------- -------------------------------- ------------------------------- -----------------------------------------
    15:10   git merge --no-ff features/bar                                                                            
    15:09   git merge --no-ff features/foo                                                                            
    15:08                                                                    git commit -m"Sixth commit"            
    15:07                                                                    git cherry-pick -x <second_commit_sha1>  
    15:06                                                                    git commit -m"Fifth commit"            
    15:05                                                                    git commit -m"Fourth commit"            
    15:04                                    git commit -m"Third commit"                                            
    15:03                                    git commit -m"Second commit"                                            
    15:02   git checkout -b features/bar                                                                              
    15:01   git checkout -b features/foo                                                                              
    15:00   git commit -m"First commit"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    *   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
    |\            Merge branch 'features/bar' - Christophe
    | * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
    | |           Sixth commit - Christophe
    | * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
    | |           Second commit - Christophe
    | * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
    | |           Fifth commit - Christophe
    | * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
    | |           Fourth commit - Christophe
    * |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
    |\ \            Merge branch 'features/foo' - Christophe
    | |/
    |/|
    | * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
    | |           Third commit - Christophe
    | * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
    |/            Second commit - Christophe
    * 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
                First commit - Christophe

    号江户十一〔21〕号

    我不确定我能比德里克·古雷更好地解释这件事…基本上,使用git pull --rebase而不是git pull:但是,本文中缺少的是,您可以默认启用它:

    1
    git config --global pull.rebase true

    号埃多克斯1〔24〕

    再一次,这里解释得很好。但简单地说,如果启用它,就不必多次解决相同的冲突。


    作为一个很好的解释在重新平衡页上的pro-git书。

    基本上,合并需要2次提交并合并它们。

    一个REBASE将转到2上的公共祖先,并逐渐将更改应用于彼此之上。这使得历史更加清晰和线性。

    但是,当您重新平衡时,您将放弃以前的提交并创建新的提交。因此,你不应该再对公开回购进行再抵押。做回购的其他人会讨厌你。

    仅仅因为这个原因,我几乎完全合并了。99%的时间我的分支机构没有太多的不同,所以如果有冲突,它只在一个或两个地方。


    Git-Rebase用于使历史中的分支路径更清晰,存储库结构更具线性。

    它还用于保持您创建的分支是私有的,就像在重新平衡并将更改推送到服务器之后,如果删除您的分支,就不会有您处理过的分支的证据。所以你们的分支机构现在是你们当地的关注点。

    在执行了rebase之后,我们还摆脱了一个额外的提交,我们以前查看是否执行了正常的合并。

    是的,在成功执行了REBASE之后仍然需要进行合并,因为REBASE命令将您的工作放在您在REBASE中提到的分支的顶部,即say master,并将分支的第一个提交作为主分支的直接后代。这意味着我们现在可以进行一个快速向前的合并,将更改从这个分支带到主分支。


    一些实际例子,有些与大型开发有关,其中gerrit用于审查和交付集成。

    当我把我的功能分支提升到一个新的远程主机时,我会合并。这提供了最小的提升工作,并且很容易跟踪功能开发的历史,例如Gitk。

    1
    2
    3
    4
    5
    git fetch
    git checkout origin/my_feature
    git merge origin/master
    git commit
    git push origin HEAD:refs/for/my_feature

    我在准备交付提交时合并。

    1
    2
    3
    4
    5
    git fetch
    git checkout origin/master
    git merge --squash origin/my_feature
    git commit
    git push origin HEAD:refs/for/master

    当我的交付提交由于任何原因而未能集成时,我重新设置基值,我需要将其更新到一个新的远程主机。

    1
    2
    3
    4
    5
    git fetch
    git fetch <gerrit link>
    git checkout FETCH_HEAD
    git rebase origin/master
    git push origin HEAD:refs/for/master


    很多次解释了什么是rebase和什么是merge,但是什么时候使用什么?

    何时使用REBASE?

    • 当你没有推动分支机构/没有其他人在努力。
    • 你想要完整的历史
    • 您希望避免所有自动生成的"合并.."提交消息

    因为Git Rebase改变了历史。因此,当其他人在同一个分支上工作时/如果您推动了它,则不应使用它。但是,如果您有一个本地分支,您可以在将分支合并回master之前执行合并Rebase master,以保持更清晰的历史记录。这样做,合并到主分支后,您在主分支中使用的分支将不可见-历史记录是"干净的",因为您没有自动生成"合并的…"。但是在主分支中仍然有完整的历史记录,而没有自动生成"合并.."提交。不过,请确保在将特性合并回主特性时,使用git merge feature-branch --ff-only确保在创建单个提交时没有冲突。如果您在获取功能分支的历史记录时为您所处理的每个任务使用功能分支,但不是"合并的…",那么这很有趣。犯罪

    第二种情况是,如果您从一个分支分支分支,并且想知道在主分支中发生了什么变化。REBASE为您提供了信息,因为它包括每个提交。

    何时使用合并?

    • 当你推动分支机构时,其他人也在努力。
    • 你不需要完整的历史
    • 简单的合并对你来说已经足够好了

    当您不需要或不想在主分支中拥有某个功能分支的所有历史记录时,或者如果其他人正在同一分支上工作/您已经推了它。如果您仍然想拥有历史记录,只需将主功能合并到功能分支,然后再将功能分支合并到主功能。这将导致一个快速向前的合并,其中您在主控形状中具有功能分支的历史记录(包括因为将主控形状合并到功能分支中而在功能分支中进行的合并提交)。


    我什么时候使用git rebase?几乎从来没有,因为它改写了历史。git merge几乎总是最好的选择,因为它尊重项目中实际发生的事情。