tags:

views:

1294

answers:

4

Hi,

I have the following repository layout:

  • master branch (production)
  • integration
  • working

What I want to achieve is to cherry pick a range of commits from the working branch and merge it into the integration branch. I pretty new to git and I can't figure out how to exactly do this (the cherry picking of commit ranges in one operation not the merging) without messing the repository up. Any pointers or thoughts on this? Thanks!

+12  A: 

When it comes to a range of commits, cherry-picking is not practical.

A rebase --onto would be better, where you replay the given range of commit on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a pratical example of git rebase --onto)

If your current branch is integration:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

That will replay everything between:

  • after the parent of first_SHA-1_of_working_branch_range (hence the ~1): the first commit you want to replay
  • up to "integration" (which points to the last commit you want to replay, from the working branch)

to "tmp" (which points to where integration was pointing before)

If there is any conflict when one of those commits is replayed:

  • either solve it and run "git rebase --continue".
  • or skip this patch, and instead run "git rebase --skip"
  • or cancel the all thing with a "git rebase --abort" (and put back the integration branch on the tmp branch)

After that rebase --onto, integration will be back at the last commit of the integration branch (that is "tmp" branch + all the replayed commits)

With cherry-picking or rebase --onto, do not forget it has consequences on subsequent merges, as described here.


A pure "cherry-pick" solution is discussed here, and would involve something like:

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D in git lingo, so

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

But anyway, when you need to "replay" a range of commits, that work "replay" should push you to use the "rebase" feature of Git.

VonC
This is a thing of beauty, thank you!
Patrick Klingemann
+1  A: 

Are you sure you don't want to actually merge the branches? If the working branch has some recent commits you don't want, you can just create a new branch with a HEAD at the point you want.

Now, if you really do want to cherry-pick a range of commits, for whatever reason, an elegant way to do this is to just pull of a patchset and apply it to your new integration branch:

git format-patch A..B
git checkout integration
git am *.patch

This is essentially what git-rebase is doing anyway, but without the need to play games. You can add --3way to git-am if you need to merge. Make sure there are no other *.patch files already in the directory where you do this, if you follow the instructions verbatim...

djs
+1  A: 

I wrapped VonC's code into a short bash script, git-multi-cherry-pick, for easy running:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

I'm currently using this as I rebuild the history of a project that had both 3rd-party code and customizations mixed together in the same svn trunk. I'm now splitting apart core 3rd party code, 3rd party modules, and customizations onto their own git branches for better understanding of customizations going forward. git-cherry-pick is helpful in this situation since I have two trees in the same repository, but without a shared ancestor.

Adam Franco
I didn't see your script right away. Interesting. +1
VonC
+2  A: 

As of git v1.7.2 cherry pick can accept a range of commits:

  • "git cherry-pick" learned to pick a range of commits (e.g. "cherry-pick A..B" and "cherry-pick --stdin"), so did "git revert"; these do not support the nicer sequencing control "rebase [-i]" has, though.
Keith Kim