Automating to copy a set of commits from one branch to another
Muhammad Wasi Naseer
Posted on October 26, 2020
This article will describe how to copy a set of contiguous commits from one branch to another branch in a repository
Photo by Yancy Min on Unsplash
Final Script
The final version of our bash script copy_commitswould be like this
copy_commits.sh -p <project_path> -s <source-branch> -t <target-branch> -f <commit-start> -e <commit-end>
Let’s assume that we have two branches dev1 and dev2, and we want to move a set of contiguous commits from dev1 to dev2
dev1 branch has 5 commits and the dev2 branch has 1 commit. And, we want to copy the 4 extra commits of dev1 to the dev2 branch by specifying the start commit and end commit.
Steps
We will do the following steps to automate the task.
- First, we’ll parse the parameters passed at the time of the script execution. The values will be store in the following five variables:
$PROJECT_PATH
$SOURCE_BRANCH
$TARGET_BRANCH
$FROM_COMMIT
$TO_COMMIT
We will first check out to dev1 branch (source branch) and get all the commits in between the start and end commit (inclusive) in an array.
We’ll then check out to dev2 branch, and we’ll take each of the commits from the array and cherry-pick the commit on dev2 (target branch)
Step 1: Parsing parameters and storing parameters into variables
while getopts ":p:s:t:f:e:" o; do
case "${o}" in
p)
PROJECT_PATH="${OPTARG}"
;;
s)
SOURCE_BRANCH="${OPTARG}"
;;
t)
TARGET_BRANCH="${OPTARG}"
;;
f)
FROM_COMMIT="${OPTARG}"
;;
e)
TO_COMMIT="${OPTARG}"
;;
*)
abnormal_exit
;;
esac
done
Following is the code for the abnormal_exit()
function
# Prints usage
usage() {
echo "Usage: $0 -p <project_path> -s <source-branch> -t <target-branch> -f <commit-start> -e <commit-target> " 1>&2
}
# Prints usage and exit
abnormal_exit() {
usage
exit 1
}
Step 2: Getting commits’ hash from the source branch and storing it into an array
commits=($(git rev-list "$FROM_COMMIT^..$TO_COMMIT"))
git rev-list
is used to list the git commits in reverse chronological order. Therefore, the latest commit will be at the start of the array.<start_commit_hash>..<end_commit_hash>
is a notation to specify that start from the start_commit_hash (not included) to the end_commit_hash (included). If we want to get start_commit_hash in the result then we have to specify the parent of the start commit. The parent of the start commit can be specified using caret ^ at the end of the commit hash. That is why a caret has been used at the end of the start commit.$FROM_COMMIT^
: It means that start from the parent of the$FROM_COMMIT
. The caret (^) is used to specify the parent of the commit otherwise the$FROM_COMMIT
will not be included in the final result.$(some_command_here)
: This notation is used when we want to run some command but want to store the output of the command into a variable. Hence, we can assign its output to a variable on the left.($(some_command_here))
: If we encapsulate some text into parentheses, then bash will convert them into an array based on the IFS(Internal Field Separator). It is by default whitespace. Hence the output of the$(some_command_here)
will be converted into an array.
Step 3: Applying commits on the target branch by picking them from the commits array
We have got all the commits into the commits array. Now, we have to check out the target branch.
git checkout $TARGET_BRANCH
if ! [[ $? -eq 0 ]]
then
echo $'\u274c' "ERROR: Failed to checkout to $TARGET_BRANCH"
exit 2
fi
We have to pick each commit one by one and run the cherry-pick
command. But, remember that we have to pick the commits in reverse order because the latest commit is at the start of the array. That is why we will start picking commits from length-1 index of the commits array.
# For each commit
for ((i=$((${#commits[@]}-1));i>=0;i--))
do
echo "Cherry Picking ${commits[i]} ..."
git cherry-pick "${commits[i]}"
# If the above command fails, then exit with proper message
if ! [[ $? -eq 0 ]]
then
echo "ERROR: Cherry pick commit with ${commits[i]} failed, exiting..."
exit 2
fi
# Show success message for the copied commit
echo "DONE: Commit ${commits[i]} copied"
done
#
is used to find the length of the array.The loop starts at length-1, and we will decrementing i until it is less than 0.
$?
is used to get the exit status code of the most recent command executed.
Final Script Screenshot
Posted on October 26, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.