148

我最近开始使用 Git。

在线浏览Git 书籍,我在“Git Rebase ”部分下找到了以下内容:

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

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

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

两者有什么区别 ?

4

6 回答 6

190

自从git cherry-pick学会了能够应用多个提交以来,这种区别确实变得有些没有意义,但这就是所谓的趋同进化;-)

真正的区别在于创建这两种工具的初衷:

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

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

  • git cherry-pick是为了将一个有趣的提交从一个开发线带到另一个开发线。一个典型的例子是将在不稳定的开发分支上进行的安全修复反向移植到稳定(维护)分支,这merge没有任何意义,因为它会带来大量不需要的更改。

    自从它第一次出现以来,git cherry-pick已经能够一次一个地选择多个提交。

因此,这两个命令之间最显着的区别可能是它们如何处理它们工作的分支:通常从其他地方git cherry-pick带来一个提交并将其应用到您当前分支的顶部,记录一个提交,同时获取您当前的分支并重写一系列自己的提示以一种或另一种方式提交。是的,这是对可以做什么的严重简化的描述,但它是故意的,试图让总体思路深入人心。git rebasegit rebase

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

鉴于这种情况,
rebase 之前的 repo 状态
The Book指出:

但是,还有另一种方法:您可以获取 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 提交——实际上,“实验”是“ master" 直到并包括 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确实将一系列提交从“实验”移植到“大师”的尖端,因此您可以正确地判断出该过程中确实存在“另一个分支”。但要点是“实验”中的提示提交最终成为“实验”中的新提示提交,它只是改变了它的基础:
合并后的状态

同样,从技术上讲,您可以看出git rebase这里包含了来自“master”的某些提交,这是绝对正确的。

于 2012-08-07T00:08:03.540 回答
120

使用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)

于 2012-12-28T16:43:08.573 回答
19

Cherry-picking 适用于个人提交

当你做 rebase 时,它​​会将历史记录中的所有提交应用到那里缺少的分支的 HEAD。

于 2012-08-06T21:18:51.183 回答
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现在植根于最新的主位置。完毕!

于 2016-03-30T10:12:16.233 回答
10

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

git rebase接受开始提交并将您的提交重播为在他们的提交之后(开始提交)。

git cherry-pick接受一组提交并在的(你的)之后重播他们的提交HEAD

换句话说,这两个命令的核心行为(忽略它们不同的性能特征、调用约定和增强选项)是对称的:check out branch barand runninggit rebase foo将分支设置为与 check out branch and running设置bar相同的历史记录到(从 的变化,然后是从 的变化)。foogit cherry-pick ..barfoofoobar

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

于 2017-11-19T18:55:41.167 回答
7

两者都做非常相似的事情。主要的概念区别是(简而言之):

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

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

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

给定这个初始状态:

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

...并假设您希望从当前topic分支上重放分支的提交master,您有两个选择:

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

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

    结果:您当前的分支topic被重新定位(移动)到master. 分支已更新,而
    分支仍然存在。topicmaster

  2. 使用cherry-pick:您首先master通过doing去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. 分支已更新,而
    分支仍然存在。mastertopic


当然,在这里你必须明确告诉cherry-pick 选择一个提交序列,使用范围表示法 foo..bar。如果您只是简单地传递了分支名称,如在 中git cherry-pick topic,它只会拾取分支顶端的提交,结果是:

A---B---C---D---G' master
     \
      E---F---G topic
于 2019-08-19T13:57:30.850 回答