Git 樱桃选择与变基

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11835948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 14:19:45  来源:igfitidea点击:

Git cherry pick vs rebase

gitversion-control

提问by lysergic-acid

I have recently started working with Git.

我最近开始使用 Git。

Going over the Git bookonline I found the following under the "Git Rebase" section:

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

With the rebase command, you can take all the changes that were committed on one branch and replay them on another one.

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

(Quoted from: http://git-scm.com/book/en/Git-Branching-Rebasing)

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

I thought this is the exact definition of git cherry-pick(reapply a commit or a set of commit objects on the currently checked out branch).

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

What is the difference between the two ?

两者有什么区别 ?

回答by kostix

Since the time git cherry-picklearned to be able to apply multiple commits, the distinction indeed became somewhat moot, but this is something to be called convergent evolution ;-)

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

The true distinction lies in original intent to create both tools:

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

  • git rebase's task is to forward-port a series of changes a developer has in their private repository, created against version X of some upstream branch, to version Y of that same branch (Y > X). This effectively changes the baseof that series of commits, hence "rebasing".

    (It also allows the developer to transplant a series of commits onto any arbitrary commit, but this is of less obvious use.)

  • git cherry-pickis for bringing an interesting commit from one line of development to another. A classic example is backporting a security fix made on an unstable development branch to a stable (maintenance) branch, where a mergemakes no sense, as it would bring a whole lot of unwanted changes.

    Since its first appearance, git cherry-pickhas been able to pick several commits at once, one-by-one.

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

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

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

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

Hence, possibly the most striking difference between these two commands is how they treat the branch they work on: git cherry-pickusually brings a commit from somewhere elseand applies it on top of your current branch, recording a newcommit, while git rebasetakes your current branch and rewritesa series of its owntip commits in one way or another. Yes, this is a heavily dumbed down description of what git rebasecan do, but it's intentional, to try to make the general idea sink in.

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

Updateto further explain an example of using git rebasebeing discussed.

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

Given this situation,
a state of the repo before rebasing
The Bookstates:

鉴于这种情况,
变基前的 repo 状态
本书指出:

However, there is another way: you can take the patch of the change that was introduced in C3 and reapply it on top of C4. In Git, this is called rebasing. With the rebase command, you can take all the changes that were committed on one branch and apply them onto another one.

In this example, you'd run the following:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

但是,还有另一种方法:您可以将 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

"The catch" here is that in this example, the "experiment" branch (the subject for rebasing) was originally forked off the "master" branch, and hence it sharescommits C0 through C2 with it — effectively, "experiment" is "master" up to, and including, C2 plus commit C3 on top of it. (This is the simplest possible case; of course, "experiment" could contain several dozens of commits on top of its original base.)

这里的“问题”是在这个例子中,“实验”分支(rebase 的主题)最初是从“主”分支分叉出来的,因此它与它共享C0 到 C2 的提交——实际上,“实验”是“ master”直到并包括 C2 加上在其之上提交 C3。(这是最简单的可能情况;当然,“实验”可以在其原始基础之上包含几十个提交。)

Now git rebaseis told to rebase "experiment" onto the currenttip of "master", and git rebasegoes like this:

现在git rebase被告知将“experiment”重新设置为“master”的当前提示,并且git rebase如下所示:

  1. Runs git merge-baseto see what's the last commit shared by both "experiment" and "master" (what's the point of diversion, in other words). This is C2.
  2. Saves away all the commits made since the diversion point; in our toy example, it's just C3.
  3. Rewinds the HEAD (which points to the tip commit of "experiment" before the operation starts to run) to point to the tip of "master" — we're rebasing onto it.
  4. Tries to apply each of the saved commits (as if with git apply) in order. In our toy example it's just one commit, C3. Let's say its application will produce a commit C3'.
  5. If all went well, the "experiment" reference is updated to point to the commit resulted from applying the last saved commit (C3' in our case).
  1. 运行git merge-base以查看“experiment”和“master”共享的最后一次提交是什么(换句话说,转移的重点是什么)。这是C2。
  2. 保存自转移点以来所做的所有提交;在我们的玩具示例中,它只是 C3。
  3. 倒回 HEAD(在操作开始运行之前指向“实验”的提示提交)以指向“主”的提示——我们正在重新建立它。
  4. 尝试git apply按顺序应用每个保存的提交(就像使用)。在我们的玩具示例中,它只是一次提交,C3。假设它的应用程序将产生一个提交 C3'。
  5. 如果一切顺利,“实验”引用将更新为指向应用最后保存的提交(在我们的例子中为 C3')所产生的提交。

Now back to your question. As you can see, here technicallygit rebaseindeed transplants a series of commits from "experiment" to the tip of "master", so you can rightfully tell there indeed is "another branch" in the process. But the gist is that the tip commit from "experiment" ended up being the new tip commit in "experiment", it just changed its base:
state after merging

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

Again, technically you can tell that git rebasehere incorporated certain commits from "master", and this is absolutely correct.

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

回答by Kenny Ho

With cherry-pick, the original commits/branch sticks around and new commits are created. With rebase, the whole branch is moved with the branch pointing to the replayed commits.

使用cherry-pick,原始提交/分支仍然存在并创建新提交。使用 rebase,整个分支移动,分支指向重放的提交。

Let say you started with:

假设您开始于:

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

Rebase:

变基:

$ git rebase master topic

You get:

你得到:

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

Cherry-pick:

樱桃采摘:

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

You get:

你得到:

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

for more info about git this book has most of it (http://git-scm.com/book)

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

回答by iltempo

Cherry-picking works for individual commits.

樱桃采摘适用于个人提交

When you do rebasing it applies all commitsin the history to the HEAD of the branch that are missing there.

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

回答by raiks

A short answer:

简短的回答:

  • git cherry-pick is more "low level"
  • As such, it can emulate git rebase
  • git cherry-pick 更“低级”
  • 因此,它可以模拟 git rebase

Answers given above are good, I just wanted to give an example in an attempt to demonstrate their interrelation.

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

It is not recommended to replace "git rebase" with this sequence of actions, it's just "a proof of concept" which, I hope, helps to understand how things work.

不建议用这一系列操作替换“git rebase”,它只是“概念证明”,我希望它有助于理解事情是如何工作的。

Given the following toy repository:

鉴于以下玩具存储库:

$ 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

Say, we have some very important changes (commits #2 through #5) in master which we want to include into our test_branch_1. Usually we just switch to a branch and do "git rebase master". But as we are pretending we are only equipped with "git cherry-pick", we do:

比如说,我们在 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 "^")    

After all these operations our commit graph will look like this:

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

* 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

As we can see, commits #6 and #7 were applied against 7254931 (a tip commit of master). HEAD was moved and points a commit which is, essentially, a tip of a rebased branch. Now all we need to do is delete an old branch pointer and create a new one:

正如我们所看到的,提交 #6 和 #7 被应用于 7254931(master 的一个提示提交)。HEAD 被移动并指向一个提交,它本质上是一个重新定位的分支的尖端。现在我们需要做的就是删除一个旧的分支指针并创建一个新的:

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

test_branch_1is now rooted from the latest master position. Done!

test_branch_1现在源自最新的主位置。完毕!

回答by Stuart P. Bentley

They're both commands for rewriting the commits of one branch on top of another: the difference is in which branch - "yours" (the currently checked out HEAD) or "theirs" (the branch passed as an argument to the command) - is the basefor this rewrite.

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

git rebasetakes a starting commitand replays yourcommits as coming after theirs(the starting commit).

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

git cherry-picktakes a set of commitsand replays theircommits as coming after yours(your HEAD).

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

In other words, the two commands are, in their core behavior (ignoring their divergent performance characteristics, calling conventions, and enhancement options), symmetrical: checking out branch barand running git rebase foosets the barbranch to the same history as checking out branch fooand running git cherry-pick ..barwould set footo (the changes from foo, followed by the changes from bar).

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

Naming-wise, the difference between the two commands can be remembered in that each one describes what it does to the currentbranch: rebasemakes the other head the new basefor your changes, whereas cherry-pickpicks changes from the other branch and puts them on top of your HEAD(like cherries on top of a sundae).

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

回答by waldyrious

Both do very similar things; the main conceptual difference is (in simplified terms) that:

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

  • rebasemoves commits from the current branchto another branch.

  • cherry-pickcopies commits from another branchto the current branch.

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

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

Using diagrams similar to @Kenny Ho's answer:

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

Given this initial state:

鉴于此初始状态:

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

...and assuming that you want get the commits from the topicbranch replayed on top of the current masterbranch, you have two options:

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

  1. Using rebase:You'd first go to topicby doing git checkout topic, and then move the branch by running git rebase master, producing:

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

    Result:your current branch topicwas rebased (moved) onto master.
    The topicbranch was updated, while the masterbranch remained in place.

  2. Using cherry-pick: you'd first go to masterby doing git checkout master, and then copy the branch by running git cherry-pick topic~3..topic(or, equivalently, git cherry-pick B..G), producing:

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

    Result:the commits from topicwere copiedinto master.
    The masterbranch was updated, while the topicbranch remained in place.

  1. 使用变基:您首先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分支留在地方。



Of course, here you had to explicitly tell cherry-pick to pick a sequence of commits, using the range notationfoo..bar. If you had simply passed the branch name, as in git cherry-pick topic, it would have picked up only the commit at the tip of the branch, resulting in:

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

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