Skip to content

Instantly share code, notes, and snippets.

@dkirrane
Forked from Dyrcona/quickpick
Created May 21, 2017 19:38
Show Gist options
  • Save dkirrane/92bb0751153f321e1610b63af18ef072 to your computer and use it in GitHub Desktop.
Save dkirrane/92bb0751153f321e1610b63af18ef072 to your computer and use it in GitHub Desktop.
A bash script to batch git cherry-pick of many commits from a single source branch. It can become a new git command if you save it in your path with a name like git-quickpick. Then, you can run it like so `git quickpick foo/bar'. Very handy, that.
#!/bin/bash
# Author: Thomas Berezansky <[email protected]>
# Author: Jason Stephenson <[email protected]>
#
# Feel free to use and to share this script in anyway you like.
# This script is intended as a shortcut for the git cherry-pick
# command when you have several commits that you want to cherry-pick
# into your local branch from another branch. It often results in a
# cleaner commit history than simply doing a git merge.
# It can be run in two ways and either method takes an optional
# parameter, -s. If provided the -s parameter will add your git
# signature to each incoming commit as they are cherry-picked. This
# is a handy way to sign off on someone else's commits in bulk when
# your project has a policy of requiring sign offs on anything going
# into the main git repository. If you supply the -s parameter, it
# should come before any branch or commit arguments on the command
# line.
# The simpler way to run this script is to simply supply the branch
# name whose commits you want to cherry-pick into your current branch.
# For instance, if you want to cherry-pick the commits from branch
# `bar' of remote `foo' that do not exist in your current branch you
# would use the following:
#
# quickpick foo/bar
#
# or
#
# quickpick -s foo/bar
#
# to include your sign off.
# The other way to use this script is to supply start and end git
# commit hashes. This is useful if there are commits on the external
# branch that you don't want, and you only want those from start to
# end. For example, if you want all commits between aef2247 and
# c6e1234, then you would use the following:
#
# quickpick aef2247 c6e1234
#
# When used in this way, the script will retrieve the commits via the
# git log command in reverse order so that commits are applied in the
# proper order, if later commits depend on earlier commits.
# Should you run into a conflict while cherry-picking, you would
# resolve in the usual way. Should you still have outstanding commits
# left to pull in, you may pickup where you left off by using the
# special argument `--continue.' Even if you believe that your
# conflict occurred in the final cherry-pick, it won't hurt to use the
# --continue argument as a precaution against missing any commits.
# The script keeps track of the commits that have been cherry-picked
# as it runs, and so it would be safe to run --continue when there are
# none left.
if [ "$1" == "-s" ]; then
SIGN="-s"
shift
else
SIGN=""
fi
START=${1:-'FAIL'}
END=${2:-'CHERRY'}
if [ "$1" == 'FAIL' ]; then
echo "NEED SOURCE BRANCH"
exit 1;
fi
if [ "$START" == '--continue' ]; then
COMMITS=`cat ~/.git-cherrypick-resume-commits`
SIGN=`cat ~/.git-cherrypick-resume-sign`
elif [ "$END" == 'CHERRY' ]; then
COMMITS=`git cherry HEAD $START | grep ^+ | cut -f2 -d' '`
else
COMMITS=`git log --pretty=oneline --reverse $START...$END | cut -f1 -d' '`
fi
if [ "$SIGN" != "-s" ]; then
SIGN=''
fi
for commit in $COMMITS
do
COMMITS=`echo $COMMITS | sed -e "s/.*$commit\s*//"`
git cherry-pick $SIGN $commit
if [ $? -ne 0 ]; then
echo $COMMITS > ~/.git-cherrypick-resume-commits
echo "$SIGN" > ~/.git-cherrypick-resume-sign
exit 1
fi
done
rm -f ~/.git-cherrypick-resume-commits ~/.git-cherrypick-resume-sign
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment