【问题标题】:Git cherry pick vs rebaseGit 樱桃挑选与变基
【发布时间】:2020-05-06 18:25:05
【问题描述】:

我最近开始使用 Git。

在线查看Git book,我在“Git Rebase”部分下找到了以下内容:

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

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

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

这两者有什么区别?

【问题讨论】:

    标签: git version-control


    【解决方案1】:

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

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

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

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

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

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

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

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

    鉴于这种情况,

    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有了它——实际上,“实验”在 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”的某些提交,这是绝对正确的。

    【讨论】:

    • 谢谢。我仍然没有完全理解你在这里的意思。在本书中,给出的示例是 rebase 应用了来自另一个分支的一系列提示提交,而您说它来自“同一个分支”。或者也许有几个例子说明它是如何工作的?
    • 试图通过更新我的答案来解释此事。
    【解决方案2】:

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

    【讨论】:

    • 回答得很好。同样常见的是,您可能只想挑选 A 和 B 提交,但在这些情况下让 C 挂起,您可能希望保留分支并挑选同事可能需要查看的更改。 Git 是为与人一起工作而设计的,因此如果您在单独工作时看不到某件事的好处,那么在较大的团队中工作时通常会更常用。
    • 如果改为使用交互式变基,省略一个或多个提交,最后你会有哪些分支?如果它只是topic 基于master 的基础,它不包含遗漏的提交,那么它们将属于哪个分支?
    • 我还想补充一件事:如果你在采摘樱桃后先git checkout topic 然后git reset --hard C',那么你得到的结果与变基后的结果完全相同。我使用樱桃采摘而不是变基使自己免于许多合并冲突,因为共同的祖先已经很久了。
    • @anthony - stackoverflow.com/questions/11835948/…:据我所知,他们迷路了。我不是git-guru,但这个rebase/cherry-pick 是我无法理解的git 的所有细节。
    • 您的图表弊大于利,因为它们在功能上是相同的。唯一不同的是git checkout -b创建的分支,与git cherry-pick无关。解释你想说的更好的方法是“你在topic 分支上运行git rebase 并通过master;你在master 分支上运行git cherry-pick 并传递它(来自)topic。”
    【解决方案3】:

    樱桃采摘适用于个人提交

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

    【讨论】:

    • 谢谢。你知道这些在幕后操作是否相同吗? (将它们的中间输出存储到“补丁”文件等)。
    • Afaik 是的。它一一应用所有补丁。这就是为什么有时必须在变基过程中解决合并冲突才能继续的原因。
    • @iltempo,它仅适用于旧版本的 Git 中的单个提交;目前,您可以执行git cherry-pick foo~3..foo 之类的操作,并从“foo”中获取树顶提交。
    • git-rebase 使用与代码库 iirc 中的樱桃采摘相同的 api
    • 我不认为它们在幕后实际上是一样的。我尝试过重新调整数千次提交,我认为 git 创建了一个巨大的邮箱文件,然后在其上运行 git am。而樱桃选择通过提交应用提交(可能通过为每个补丁创建单个消息邮箱)。我的 rebase 失败了,因为它正在创建的邮箱文件用完了驱动器上的空间,但是具有相同修订范围的cherry-pick 成功(并且似乎运行得更快)。
    【解决方案4】:

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

    git rebase 接受一个开始提交重放你的提交在他们的之后/strong>(开始提交)。

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

    换句话说,这两个命令的核心行为(忽略它们不同的性能特征、调用约定和增强选项)是对称的:签出分支bar 并运行@987654326 @ 将bar 分支设置为与签出分支foo 相同的历史记录,运行git cherry-pick ..bar 会将foo 设置为(从foo 更改,然后是从bar 更改)。

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

    【讨论】:

    • 除了你的答案,我什么都听不懂!它简洁明了,没有多余的措辞。
    【解决方案5】:

    简短回答:

    • git cherry-pick 更“低级”
    • 因此,它可以模拟 git 变基

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

    不建议用这一系列操作替换“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 吗?
    • 因为cherry-pick 能够应用一系列提交,我认为是的。虽然这是一种有点奇怪的做事方式,但没有什么能阻止您在 master 之上的特性分支中挑选所有提交,然后删除特性分支并重新创建它,使其指向 @ 的尖端987654327@。您可以将git rebase 视为git cherry-pick feature_branchgit branch -d feature_branchgit branch feature_branch master 的序列。
    【解决方案6】:

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

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

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

    使用类似于@Kenny Ho's answer的图表:

    鉴于此初始状态:

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

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

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

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

      结果:您当前的分支 topic 已重新定位(移动)到 master
      topic 分支已更新,而 master 分支仍保留。

    2. 使用cherry-pick:您首先通过git checkout master 转到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 分支仍保留。


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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 2018-07-23
      • 2014-11-21
      • 1970-01-01
      • 1970-01-01
      • 2013-12-30
      相关资源
      最近更新 更多