Git cherry pick vs rebase

GitVersion Control

Git Problem Overview


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 ?

Git Solutions


Solution 1 - Git

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.

Solution 2 - Git

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)

Solution 3 - Git

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.

Solution 4 - Git

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!

Solution 5 - Git

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).

Solution 6 - Git

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.

  1. 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionlysergic-acidView Question on Stackoverflow
Solution 1 - GitkostixView Answer on Stackoverflow
Solution 2 - GitKenny HoView Answer on Stackoverflow
Solution 3 - GitiltempoView Answer on Stackoverflow
Solution 4 - GitraiksView Answer on Stackoverflow
Solution 5 - GitStuart P. BentleyView Answer on Stackoverflow
Solution 6 - GitwaldyriousView Answer on Stackoverflow