169

I have recently started working with Git.

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

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

(Quoted from: 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).

What is the difference between the two ?

waldyrious
  • 3,683
  • 4
  • 33
  • 41
lysergic-acid
  • 19,570
  • 21
  • 109
  • 218

7 Answers7

202

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

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 base of 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-pick is 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 merge makes no sense, as it would bring a whole lot of unwanted changes.

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

Hence, possibly the most striking difference between these two commands is how they treat the branch they work on: git cherry-pick usually brings a commit from somewhere else and applies it on top of your current branch, recording a new commit, while git rebase takes your current branch and rewrites a series of its own tip commits in one way or another. Yes, this is a heavily dumbed down description of what git rebase can do, but it's intentional, to try to make the general idea sink in.

Update to further explain an example of using git rebase being discussed.

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

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

"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 shares commits 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.)

Now git rebase is told to rebase "experiment" onto the current tip of "master", and git rebase goes like this:

  1. Runs git merge-base to 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).

Now back to your question. As you can see, here technically git rebase indeed 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

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

kostix
  • 51,517
  • 14
  • 93
  • 176
  • 2
    Thanks. I still didn't fully grasp what you mean here. In the book, the example is given that rebase applies a series of tip commits from another branch, while you say it's from "the same branch". Or perhaps there are a few cases of how it works? – lysergic-acid Aug 07 '12 at 06:42
  • 1
    Tried to explain the matter by updating my answer. – kostix Aug 07 '12 at 08:32
136

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.

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)

Joe
  • 16,328
  • 12
  • 61
  • 75
Kenny Ho
  • 3,247
  • 5
  • 19
  • 24
  • 6
    Well answered. It is also common that you might want to cherrypick just A and B commits but leave C hanging in those cases you might want to keep the branch and just cherry pick changes that colleagues might need to see. Git is made to work with people so if you don't see the benefits of something when working alone it is often more commonly used when working in larger groups. – Pablo Jomer Dec 28 '12 at 17:06
  • If an interactive rebase was done instead, leaving out one or more commits, what branches would you have at the end? if it were only `topic` rebased on top of `master`, it doesn't contain the left-out commits so what branch will they be part of? – Anthony Dec 20 '14 at 19:53
  • 1
    Just one more thing I want to add: if you `git checkout topic` and then `git reset --hard C'` after the cherry picking, then you have the very same result as after rebasing. I saved myself from a lot of merge conflicts using cherry picking over rebasing, because the common ancestor was way back. – sorrymissjackson Feb 15 '16 at 09:16
  • @anthony - http://stackoverflow.com/questions/11835948/git-cherry-pick-vs-rebase#comment43592006_14072763: as far as I understand they are lost. I'm no `git`-guru but this `rebase`/`cherry-pick` is on of all the details with `git` that I had a problem understanding. – thoni56 Jan 03 '17 at 12:34
  • In the face of `rebase --onto` this really hammers home the difference between cherry-pick and rebase. – dash-tom-bang May 10 '17 at 17:24
  • 1
    Your graphs do more harm than good, because they are functionally identical. The only difference is the branch created by `git checkout -b`, which has nothing to do with `git cherry-pick`. A better way to explain what you're trying to say would be “you run `git rebase` on the `topic` branch and pass it `master`; you run `git cherry-pick` on the `master` branch and pass it (commits from) `topic`.” – Rory O'Kane Sep 20 '17 at 21:58
  • 1
    @RoryO'Kane No they aren't functionally identical. `git rebase` changes ancestor of `A` from `E` to `G` and it is similar to `Cut & Paste` operation. `git cherry-pick` does not change ancestor of `A` but just copies content of it as well as content of `B` and `C` and applies them starting from `G` so `git cherry-pick` is similar to `Copy & Paste` operation that leaves source `A..C` untouched. Creating a new branch called `topic_new` makes no difference. – Jimmix Apr 07 '23 at 19:26
20

Cherry-picking works for individual commits.

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

iltempo
  • 15,718
  • 8
  • 61
  • 72
  • Thanks. Do you know if these operate the same under the covers? (store their intermediate outputs to "patch" files, etc). – lysergic-acid Aug 06 '12 at 21:20
  • Afaik yes. It applies all patches one by one. That's the reason why you sometimes have to resolve merge conflicts in the middle of a rebase before continuing. – iltempo Aug 06 '12 at 21:24
  • 9
    @iltempo, it worked for individual commits only in older versions of Git; at present time you can do something like `git cherry-pick foo~3..foo` and get the tree top commits from "foo" picked one by one. – kostix Aug 06 '12 at 23:29
  • 2
    git-rebase uses the same api as the cherry-picking does in the codebase, iirc – alternative Dec 28 '12 at 17:47
  • I don't think they actually work the same under the covers. I've tried rebasing thousands of commits and I think git creates a huge mailbox file and then runs `git am` on it. Whereas a cherry pick applies commit by commit (possibly by creating a single-message mailbox for each patch). My rebase was failing because the mailbox file it was creating ran out of space on the drive, but cherry-pick with the same revision range succeeded (and seems to run faster). – onlynone Mar 25 '15 at 18:19
16

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 base for this rewrite.

git rebase takes a starting commit and replays your commits as coming after theirs (the starting commit).

git cherry-pick takes a set of commits and replays their commits as coming after yours (your 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 bar and running git rebase foo sets the bar branch to the same history as checking out branch foo and running git cherry-pick ..bar would set foo to (the changes from foo, followed by the changes from bar).

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

Stuart P. Bentley
  • 10,195
  • 10
  • 55
  • 84
11

A short answer:

  • git cherry-pick is more "low level"
  • As such, it can emulate 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.

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:

$ 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:

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

test_branch_1 is now rooted from the latest master position. Done!

Andy
  • 4,783
  • 2
  • 26
  • 51
raiks
  • 1,270
  • 1
  • 15
  • 12
  • But rebase can simulate git cherry-pick also ? – Number945 Feb 02 '19 at 19:28
  • Since `cherry-pick` is able to apply a range of commits, I think, yes. Although this is a bit weird way of doing things, nothing prevents you from cherry-picking all commits in your feature branch on top of `master`, then delete the feature branch and re-create it such that it points to the tip of `master`. You can think of `git rebase` as of a sequence of `git cherry-pick feature_branch`, `git branch -d feature_branch` and `git branch feature_branch master`. – raiks Jul 30 '20 at 11:18
11

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

  • rebase moves commits from the current branch to another branch.

  • cherry-pick copies commits from another branch to the current branch.

Using diagrams similar to @Kenny Ho's answer:

Given this initial state:

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

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

  1. Using rebase: You'd first go to topic by 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 topic was rebased (moved) onto master.
    The topic branch was updated, while the master branch remained in place.

  2. Using cherry-pick: you'd first go to master by 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 topic were copied into master.
    The master branch was updated, while the topic branch remained in place.


Of course, here you had to explicitly tell cherry-pick to pick a sequence of commits, using the range notation foo..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:

A---B---C---D---G' master
     \
      E---F---G topic
waldyrious
  • 3,683
  • 4
  • 33
  • 41
5

They are very different:

  • A rebase is a "magical" operation that modifies branches to update them.
  • A cherry-pick by contrast is a fine-grained operation which copies commits.

In fact, cherry picks can even be used to replace rebases.

The outdated branch problem:

An outdated branch is a daily scenario.

  1. You branched of from a "master" branch,
  2. Committed some changes,
  3. While in parallel also the initial "master" branch changed.

problem case

As illustrated above, there are several ways to depict this initial state:

  • Even though we do tend to show a kind of "branched" diagram when several branches have the same commits. In reality each branch has its own line of commits, some of which have the same hash code.
  • It's important to realize that there isn't such a thing as "master" or "child" relation between branches in git. All of that is just orchestrated by naming conventions.

On top of that, usually you would have 2 versions of most branches: a local and remote version. So, if we want to be as verbose as possible, we would show it as follows:

A--B--D      (remote) origin/master
     
A--B--C--E   (remote) origin/feature/foo
A--B--C--E   (local) feature/foo

The challenge now, is to update the history of our 2 feature branches, to add the missing changes of commit D. And there are in fact 2 ways to accomplish this.

Rebase solution

Rebase really is a magical command that updates a feature branch for you. It does so by adding the missing commits (i.e. commit D of the master) to a branch that "runs behind" a couple of commits. And it does so by adding the commits IN FRONT of the new commits.

rebase

After a rebase, all we need to do, is push the result, to update the remote branch with the updated version.

A--B--D         (remote) origin/master
     
A--B--D--C--E   (remote) origin/feature/foo
A--B--D--C--E   (local) feature/foo

Or as it would often be visualized.

look it moved

And that kind of gives the impression that our feature branch "moved", while in reality, it just got a bit longer.

Cherry-pick solution

You can in fact accomplish the same by combining a "hard reset" with multiple "cherry-picks". Let's start from the same problem, and perform a "manual rebase" by using cherry-picks.

cherry pick rebase

Which would have the same result as a rebase

A--B--D         master
       \
        C--E    feature/foo
bvdb
  • 22,839
  • 10
  • 110
  • 123