gitversion-control

Git cherry pick vs rebase


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/v2/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 ?


Solution

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

    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 C4 and reapply it on top of C3. In Git, this is called rebasing. With the rebase command, you can take all the changes that were committed on one branch and replay them on a different branch.

    For this example, you would check out the experiment branch, and then rebase it onto the master branch as follows:

    $ 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 C4 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 divergence, in other words). This is C2.
    2. Saves away all the commits made since the diversion point; in our toy example, it's just C4.
    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, C4. Let's say its application will produce a commit C4'.
    5. If all went well, the "experiment" reference is updated to point to the commit resulted from applying the last saved commit (C4' 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.