Git cherry pick vs rebase

lys*_*cid 103 git version-control

我最近开始与Git合作.

在线阅读 git书(Git书)我在"Git Rebase"部分找到了以下内容:

使用rebase命令,您可以获取在一个分支上提交的所有更改,并在另一个分支上重播它们.

(引用自:http://git-scm.com/book/en/Git-Branching-Rebasing)

我认为这是git cherry-pick的确切定义(在当前签出的分支上重新应用提交或一组提交对象.

两者有什么区别 ?

kos*_*tix 150

由于git cherry-pick学会了能够应用多次提交的时间,这种区别确实变得有点没有实际意义,但这有点被称为收敛进化;-)

真正的区别在于创建两种工具的原始意图:

  • git rebase的任务是将开发人员在其私有存储库中进行的一系列更改转发到同一分支的版本Y(Y> X),这些更改是针对某个上游分支的版本X创建的.这有效地改变了该系列提交的基础,因此"重新定位".

    (它还允许开发人员将一系列提交移植到任意提交中,但这种用法不太明显.)

  • git cherry-pick是为了从一个开发线到另一个开发线带来一个有趣的提交.一个典型的例子是将不稳定开发分支上的安全修复程序向后移植到稳定(维护)分支,其中a merge没有意义,因为它会带来大量不必要的更改.

    自首次亮相以来,git cherry-pick已经能够一次性选择几个提交.

因此,这两个命令之间可能最显着的区别在于它们如何处理它们所处理的分支:git cherry-pick通常从其他地方提交提交并将其应用于当前分支,记录提交,同时git rebase获取当前分支并重写一系列自己的提示以这种或那种方式提交.是的,这是一个严重愚蠢的描述,git rebase可以做什么,但它是故意的,试图让一般的想法沉沦.

更新以进一步解释使用git rebase正在讨论的示例.

鉴于这种情况,
在重新定位之前的回购状态
该书指出:

但是,还有另一种方法:您可以获取C3中引入的更改补丁并在C4之上重新应用它.在Git中,这称为变基.使用rebase命令,您可以获取在一个分支上提交的所有更改并将其应用到另一个分支上.

在此示例中,您将运行以下命令:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Run Code Online (Sandbox Code Playgroud)

这里的"捕获"是在这个例子中,"实验"分支(重新定位的主题)最初是从"主"分支分离出来的,因此它与它共享提交C0到C2 - 实际上,"实验"是"掌握"最多,包括C2加上提交C3." (这是最简单的情况;当然,"实验"可能在其原始基础之上包含几十个提交.)

现在git rebase被告知要将"实验"重新定位到"主人" 的当前提示上,并且git rebase像这样:

  1. 运行git merge-base以查看"实验"和"主人"共享的最后一次提交是什么(换句话说,转移点是什么).这是C2.
  2. 保存自转移点以来所做的所有提交; 在我们的玩具示例中,它只是C3.
  3. 将HEAD(指向操作开始运行之前的"实验"提示)转向指向"主"的提示 - 我们正在重新定位它.
  4. 尝试git apply按顺序应用每个已保存的提交(如同).在我们的玩具示例中,它只是一个提交,C3.假设它的应用程序将生成一个提交C3'.
  5. 如果一切顺利,"实验"引用将更新为指向应用上次保存的提交(在我们的示例中为C3')的结果.

现在回到你的问题.正如您所看到的,这里技术上 git rebase确实将一系列提交从"实验"移植到"主"的提示,因此您可以正确地告诉其过程中确实存在"另一个分支".但要点是,"实验"中的提示最终成为"实验"中的新提示,它只是改变了它的基础:
合并后的状态

同样,从技术上讲,你可以说git rebase这里包含了来自"master"的某些提交,这绝对是正确的.

  • 谢谢.我仍然没有完全掌握你的意思.在本书中,给出了一个例子,rebase应用来自另一个分支的一系列提示,而你说它来自"同一个分支".或者也许有一些案例如何运作? (2认同)

Ken*_* Ho 94

使用cherry-pick,原始提交/分支会一直存在并创建新提交.使用rebase,整个分支随着分支指向重放的提交而移动.

让我们开始说:

      A---B---C topic
     /
D---E---F---G master
Run Code Online (Sandbox Code Playgroud)

变基:

$ git rebase master topic
Run Code Online (Sandbox Code Playgroud)

你得到:

              A'--B'--C' topic
             /
D---E---F---G master
Run Code Online (Sandbox Code Playgroud)

摘樱桃:

$ git checkout master -b topic_new
$ git cherry-pick A^..C
Run Code Online (Sandbox Code Playgroud)

你得到:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new
Run Code Online (Sandbox Code Playgroud)

有关git的更多信息,这本书有大部分内容(http://git-scm.com/book)

  • 好的回答.你也可能只想点赞A和B提交,但是在这些情况下你可能想要保留分支并且只是挑选同事可能需要看到的变化.Git可以与人合作,所以如果你在单独工作时看不到某些东西的好处,那么在大型团队工作时通常会更常用. (3认同)
  • 我还想补充一件事:如果你在挑选之后“git checkout topic”然后“git reset --hard C”,那么你会得到与 rebase 后完全相同的结果。我使用樱桃选择而不是变基使自己避免了许多合并冲突,因为共同的祖先很早以前。 (2认同)

ilt*_*mpo 13

樱桃采摘适用于个人提交.

当你进行变基时,它会将历史记录中的所有提交应用到那里缺少的分支的HEAD.

  • @iltempo,它只适用于旧版本的Git中的个人提交; 目前你可以做一些像`git cherry-pick foo~3..foo`这样的事情,并从"foo"逐一挑选出来的树顶提交. (5认同)
  • git-rebase 使用与代码库 iirc 中的cherry-picking 相同的 api (2认同)

rai*_*iks 10

一个简短的回答:

  • git cherry-pick更"低级别"
  • 因此,它可以模拟git rebase

上面给出的答案很好,我只是想举例说明它们之间的相互关系.

不建议将"git rebase"替换为这一系列操作,它只是"概念证明",我希望这有助于理解事物的运作方式.

鉴于以下玩具存储库:

$ git log --graph --decorate --all --oneline
* 558be99 (test_branch_1) Test commit #7
* 21883bb Test commit #6
| * 7254931 (HEAD -> master) Test commit #5
| * 79fd6cb Test commit #4
| * 48c9b78 Test commit #3
| * da8a50f Test commit #2
|/
* f2fa606 Test commit #1
Run Code Online (Sandbox Code Playgroud)

比如说,我们在trunk中有一些非常重要的更改(提交#2到#5),我们想要包含在我们的分支中.通常我们只是切换到一个分支并执行"git rebase master".但是,由于我们假装我们只配备了"git cherry-pick",我们这样做:

$ git checkout 7254931                # Switch to master (7254931 <-- master <-- HEAD)
$ git cherry-pick 21883bb^..558be99   # Apply a range of commits (first commit is included, hence "^")    
Run Code Online (Sandbox Code Playgroud)

完成所有这些操作后,我们的提交图将如下所示:

* dd0d3b4 (HEAD) Test commit #7
* 8ccc132 Test commit #6
* 7254931 (master) Test commit #5
* 79fd6cb Test commit #4
* 48c9b78 Test commit #3
* da8a50f Test commit #2
| * 558be99 (test_branch_1) Test commit #7
| * 21883bb Test commit #6
|/
* f2fa606 Test commit #1
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,提交#6和#7适用于7254931(主提示).HEAD被移动并指出一个提交,这实际上是一个重新分支的一个提示.现在我们需要做的就是删除一个旧的分支指针并创建一个新指针:

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4
Run Code Online (Sandbox Code Playgroud)

test_branch_1现在来自最新的主要职位.完成!


Stu*_*ley 9

它们都是用于在另一个分支之上重写一个分支的提交的命令:区别在于哪个分支 - “你的”(当前签出的HEAD)或“他们的”(作为参数传递给命令的分支) - 是这次重写的基础

git rebase进行开始提交并将您的提交重放为在他们的提交之后(开始提交)。

git cherry-pick接受一组提交并将它们的提交重放为在您的(您的HEAD)之后。

换句话说,这两个命令,在其核心行为(忽略了不同的性能特点,调用约定,并增强选项),对称:检查出的分支bar和运行git rebase foobar如检查出的分支的分支在同一历史foo和运行git cherry-pick ..bar将设置foo到(从 的变化foo,然后是从 的变化bar)。

在命名方面,这两个命令之间的区别可以记住,因为每个命令都描述了它对当前分支所做的事情:rebase使另一个 head 成为您更改的新基础,而cherry-pick从另一个分支中挑选更改并将它们放在上面你的HEAD(就像圣代上的樱桃)。

  • 除了你的答案之外,我无法理解任何答案!它简洁明了,没有多余的措辞。 (2认同)

bvd*_*vdb 7

它们有很大不同:

  • 变基是一种“神奇”的操作,它可以修改分支来更新它们。
  • 相比之下,cherry-pick 是一种复制提交的细粒度操作

事实上,cherry pick 甚至可以用来替代 rebase。

过时的分支问题:

过时的分支是日常场景。

  1. 你从“主”分支分支出来,
  2. 做出了一些改变,
  3. 与此同时,最初的“主”分支也发生了变化。

问题案例

如上图所示,有几种方法可以描述这种初始状态:

  • 尽管当多个分支具有相同的提交时,我们确实倾向于显示一种“分支”图。实际上,每个分支都有自己的提交行,其中一些具有相同的哈希码。
  • 重要的是要认识到 git 中的分支之间不存在“主”或“子”关系。所有这一切都是通过命名约定来安排的。

最重要的是,大多数分支通常有两个版本:本地版本和远程版本。因此,如果我们想尽可能详细,我们将显示如下:

A--B--D      (remote) origin/master
     
A--B--C--E   (remote) origin/feature/foo
A--B--C--E   (local) feature/foo
Run Code Online (Sandbox Code Playgroud)

现在的挑战是更新我们的 2 个功能分支的历史记录,添加缺少的提交更改D。事实上有两种方法可以实现这一点。

变基解决方案

Rebase 确实是一个神奇的命令,可以为您更新功能分支。它通过将缺少的提交(即D主控的提交)添加到“运行在”几个提交之后的分支来实现这一点。它通过在新提交的前面添加提交来实现这一点。

变基

变基后,我们需要做的就是推送结果,用更新的版本更新远程分支。

A--B--D         (remote) origin/master
     
A--B--D--C--E   (remote) origin/feature/foo
A--B--D--C--E   (local) feature/foo
Run Code Online (Sandbox Code Playgroud)

或者正如人们经常想象的那样。

看它动了

这给人的印象是我们的功能分支“移动”了,而实际上,它只是变得更长了一点。

精选解决方案

事实上,您可以通过将“硬重置”与多个“樱桃选择”相结合来实现相同的目的。让我们从同样的问题开始,并使用cherry-picks 执行“手动变基”。

樱桃选择变基

这会产生与 rebase 相同的结果

A--B--D         master
       \
        C--E    feature/foo
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的视觉解释并为其添加细节。 (3认同)
  • @Pygirl 很高兴有人成功了。:-) 这个答案比我预期的要长得多。 (2认同)

wal*_*ous 5

两者都做非常相似的事情;主要的概念差异是(简单来说):

  • rebase将提交从当前分支移动到另一个分支

  • cherry-pick将提交从另一个分支复制到当前分支

使用类似于@Kenny Ho 的回答的图表:

鉴于此初始状态:

A---B---C---D master
     \
      E---F---G topic
Run Code Online (Sandbox Code Playgroud)

...并且假设您希望topic在当前master分支的顶部重放分支中的提交,您有两个选择:

  1. 使用变基:您首先topic通过执行转到git checkout topic,然后通过运行移动分支git rebase master,产生:

    A---B---C---D master
                 \
                  E'---F'---G' topic
    
    Run Code Online (Sandbox Code Playgroud)

    结果:您当前的分支topic被重新定位(移动)到master.
    topic分支进行了更新,而master分支留在地方。

  2. 使用cherry-pick:您首先master通过执行转到git checkout master,然后通过运行git cherry-pick topic~3..topic(或等效地,git cherry-pick B..G)复制分支,产生:

    A---B---C---D---E'---F'---G' master
         \
          E---F---G topic
    
    Run Code Online (Sandbox Code Playgroud)

    结果:来自的提交topic复制master.
    master分支进行了更新,而topic分支留在地方。


当然,在这里您必须使用范围表示法明确告诉cherry-pick 选择一系列提交。如果您只是简单地传递了分支名称,如,它将仅获取分支尖端的提交,从而导致: foo..bargit cherry-pick topic

A---B---C---D---G' master
     \
      E---F---G topic
Run Code Online (Sandbox Code Playgroud)