Skip to content

Instantly share code, notes, and snippets.

@GuyPaddock
Created February 12, 2021 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GuyPaddock/6433ed4432aa457bcadb407812c3c751 to your computer and use it in GitHub Desktop.
Save GuyPaddock/6433ed4432aa457bcadb407812c3c751 to your computer and use it in GitHub Desktop.
Transplant (rebase) a range of commits from one branch onto another
#!/usr/bin/env bash
# Stop on undefined variables and errors
set -u
set -e
get_commit_parent() {
local target_commit="$1";
local _parent_commit=`git log --pretty=%P -n 1 "${target_commit}"`;
local parent_count=`echo "${_parent_commit}" | wc | awk '{ print $2; }'`;
if [ "${parent_count}" -ne "1" ]; then
echo "'${target_commit}' must have only one parent (parents are '${parent_commit}')." >&2;
exit 1;
else
export parent_commit="${_parent_commit}";
fi
}
do_transplant() {
local target_branch="$1";
local parent_of_first_commit="$2";
local last_commit="$3";
set -x;
git checkout "${target_branch}";
# Checkout a new temporary branch at the current location
git checkout -b tmp;
# Move the target branch to the head of the new patchset
git branch -f "${target_branch}" "${last_commit}";
# Rebase the patchset onto tmp, the old location of the target branch
git rebase \
--onto tmp "${parent_of_first_commit}" "${target_branch}" \
--committer-date-is-author-date;
set +x;
cleanup_transplant;
}
function abort_transplant() {
local target_branch="$1";
set -x;
git rebase --abort;
git checkout tmp;
git branch -f "$target_branch" tmp;
git checkout "$target_branch";
set +x;
cleanup_transplant;
}
function cleanup_transplant() {
set -x;
# Delete temporary branch
git branch -D tmp;
set +x;
}
function echo_error() {
message="$1";
echo "$message" 1>&2;
}
function print_usage() {
script_name=`basename "${0}"`;
echo_error "GIT Transplant -- Used to transplant a range of commits from one branch (for";
echo_error "example, 'develop') onto another (for example, a hotfix).";
echo_error "";
echo_error "This script requires GIT and can only be run inside a non-bare GIT repo.";
echo_error "";
echo_error "Pass --abort as the fourth parameter to abort a transplant that is ";
echo_error "in-progress, rolling branches back to their original state.";
echo_error "";
echo_error "Pass --cleanup as the fourth parameter to cleanup after a transplant ";
echo_error "that you manually completed successfully after being interrupted by ";
echo_error "merge conflicts.";
echo_error "";
echo_error "Usage: ${script_name} <target branch> <first commit hash (inclusive)> \\";
echo_error " <last commit hash (inclusive)> [--abort|cleanup]";
}
if [[ ($# -ne 3) && (($# -ne 4) || (($# -eq 4) && ($4 != '--abort') && \
($4 != '--cleanup'))) ]]; then
print_usage;
exit 1;
else
if [[ $# -eq 4 ]]; then
if [ $4 == '--abort' ]; then
abort_transplant "$1";
elif [[ $4 == '--cleanup' ]]; then
cleanup_transplant;
fi;
else
export parent_commit="";
get_commit_parent "$2" && do_transplant "$1" "${parent_commit}" "$3";
fi;
fi;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment