Git Cherry Pick vs Rebase


119

我最近开始与Git合作。

在网上查阅Git书,我在“ Git Rebase”部分找到了以下内容:

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

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

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

两者有什么区别 ?

Answers:


165

自从git cherry-pick学习了能够应用多个提交的时间以来,这种区分确实变得有些争议了,但这就是所谓的收敛进化;-)

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

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

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

  • git cherry-pick是为了将有趣的承诺从一个开发线带到另一个开发线。一个典型的示例是将在不稳定的开发分支上进行的安全修复程序反向移植到稳定的(维护)分支,在该分支上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

这里的“陷阱”是,在此示例中,“实验”分支(用于重新定标的主题)最初是从“主”分支分支而来的,因此它与之共享提交C0到C2,实际上,“实验”为“主”,直到C2并在其顶部提交C3。(这是最简单的情况;当然,“实验”可以在其原始基础上包含数十个提交。)

现在git rebase被告知将“实验”重新建立到“大师” 的当前提示上,git rebase如下所示:

  1. 运行git merge-base以查看“实验”和“大师”共享的最后一次提交是什么(换句话说,这是转移的重点)。这是C2。
  2. 节省了从转移点以来所做的所有提交;在我们的玩具示例中,它只是C3。
  3. 倒退HEAD(在操作开始之前指向“ experiment”的尖端提交)以指向“ master”的尖端-我们正在此基础上。
  4. 尝试git apply按顺序应用每个已保存的提交(就像使用一样)。在我们的玩具示例中,这只是一次提交,即C3。假设其应用程序将生成提交C3'。
  5. 如果一切顺利,则将“实验”引用更新为指向应用最后保存的提交(在我们的例子中为C3')所导致的提交。

现在回到您的问题。如您所见,从技术上讲 ,这里git rebase确实是将一系列提交从“实验”移植到“ master”的尖端,因此您可以正确地告诉我们在此过程中确实存在“另一个分支”。但是要点是,来自“实验”的技巧提交最终成为“实验”中的新技巧提交,它只是改变了其基础:
合并后的状态

再次,从技术上讲,您可以说git rebase这里合并了“ master”的某些提交,这是绝对正确的。


2
谢谢。我仍然没有完全理解你在这里的意思。在本书中,示例给出了rebase应用来自另一个分支的一系列提示提交,而您说它来自“同一分支”的情况。也许有几种情况如何起作用?
麦角酸

1
试图通过更新我的答案来解释问题。
kostix 2012年

98

使用cherry-pick,原始提交/分支停留在周围,并创建新的提交。使用rebase,整个分支将移动,并且该分支指向重播的提交。

假设您开始使用:

      A---B---C topic
     /
D---E---F---G master

变基:

$ git rebase master topic

你得到:

              A'--B'--C' topic
             /
D---E---F---G master

樱桃采摘:

$ git checkout master -b topic_new
$ git cherry-pick A^..C

你得到:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new

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


3
好回答。同样常见的是,您可能只希望选择A和B提交,而将C挂起,在这种情况下,您可能希望保留分支,而只选择同事可能需要查看的更改。Git可以与人一起工作,因此,如果您在单独工作时看不到某些东西的好处,那么在较大的团队中工作时通常会更常用。
巴勃罗·乔默

如果改为进行交互式基础更改,而忽略一个或多个提交,那么最后您将拥有哪些分支?如果仅topic基于进行了重新基础master,则其中不包含遗漏的提交,那么它们将属于哪个分支?
安东尼

我还要补充一件事:如果您选择樱桃,然后git checkout topicgit reset --hard C'进行樱桃采摘,那么您得到的结果与重新定基后的结果完全相同。我使用樱桃挑选而不是重新定级,从而避免了很多合并冲突,因为共同祖先早已退后。
对不起,missmissjackson,2016年

@anthony - stackoverflow.com/questions/11835948/...:据我了解,他们都将丢失。我不是git-guru,但是这个rebase/ cherry-pick在所有细节上git都是我有一个理解上的问题。
thoni56

1
您的图表弊大于利,因为它们在功能上是相同的。唯一的区别是由创建的分支git checkout -b,与无关git cherry-pick。解释您要说的话的一种更好的方法是“您git rebasetopic分支上运行并通过它master;您git cherry-pickmaster分支上运行并将其传递(从提交)topic。”
罗里·奥肯

14

挑选个人作品的赏心悦目

当您进行基础调整时,它将历史记录中的所有提交应用于分支中缺少的HEAD。


谢谢。你知道这些在幕后是否一样吗?(将其中间输出存储到“补丁”文件等)。
麦角酸

阿法克是的 它一一应用所有补丁。这就是为什么您有时必须在重新设置基准之前解决合并冲突,然后再继续的原因。
iltempo,2012年

6
@iltempo,它仅适用于旧版本的Git;目前,您可以执行类似操作,git cherry-pick foo~3..foo并从“ foo”中逐一选择树顶提交。
kostix 2012年

1
git-rebase使用与在代码库iirc中的Cherry-picking相同的api
替代

我认为它们实际上并没有在后台进行相同的工作。我已经尝试重新整理成千上万的提交,我认为git创建了一个巨大的邮箱文件,然后git am在其上运行。樱桃选择适用于逐次提交的提交(可能通过为每个补丁创建一个单消息邮箱)。我的rebase失败了,因为它正在创建的邮箱文件在驱动器上空间不足,但是具有相同修订范围的cherry-pick成功了(并且运行速度似乎更快)。
onlynone 2015年

11

一个简短的答案:

  • 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

假设我们要对master进行一些非常重要的更改(提交#2至#5),并将其包含在test_branch_1中。通常我们只是切换到一个分支并执行“ 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 "^")    

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

* 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

如我们所见,提交#6和#7是针对7254931(master的一个尖端提交)。HEAD被移动并指向一个提交,该提交从本质上讲是一个重新创建分支的尖端。现在我们需要做的就是删除一个旧的分支指针并创建一个新的分支指针:

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4

现在,test_branch_1从最新的主控职位开始扎根。做完了!


但是rebase也可以模拟git cherry-pick吗?
Number945 '19

既然cherry-pick能够应用一系列提交,我想是的。尽管这样做有点怪异,但没有什么可以阻止您在功能分支的顶部挑选所有提交master,然后删除功能分支并重新创建,使其指向的尖端master。你可以认为git rebase作为一个序列git cherry-pick feature_branchgit branch -d feature_branchgit branch feature_branch master
袭击

7

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

git rebase接受一个起始提交,它们提交之后重播您的提交(起始提交)。

git cherry-pick接受一组提交(您的)之后重播它们的提交HEAD

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

在命名方面,可以记住这两个命令之间的区别,因为每个命令都描述了它对当前分支的作用:rebase使另一个成为更改的新基础,而cherry-pick从另一个分支中选择更改并将其放在顶部您的HEAD(像圣代冰淇淋上的樱桃)。


1
除了你的答案,我听不懂!它简洁明了,完全有意义,无需多余的措辞。
neoxic

4

两者都做非常相似的事情。主要的概念差异(以简化的方式)是:

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

  • Cherry-pick副本从另一个分支提交到当前分支

使用类似于@Kenny Ho的答案的图

给定此初始状态:

A---B---C---D master
     \
      E---F---G topic

...并且假设您想从topic当前master分支的顶部重放分支的提交,则有两个选择:

  1. 使用rebase:首先,请topic执行git checkout topic,然后通过运行来移动分支git rebase master,产生:

    A---B---C---D master
                 \
                  E'---F'---G' topic
    

    结果:您当前的分支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
    

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


当然,在这里您必须使用range表示法明确地告诉cherry-pick选择一系列提交。如果您只是简单地传递了分支名称(如中的),则它将只选择分支顶端的提交,结果是: foo..bargit cherry-pick topic

A---B---C---D---G' master
     \
      E---F---G topic
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.