Last active
August 29, 2015 14:14
-
-
Save Glutexo/1c8b839f5c06603bb22d to your computer and use it in GitHub Desktop.
GIT: Incorporate changes made in an already merged branch to its merge commit.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Takes changes done in a branch that was already merged into another and | |
# incorporates them into the already existing merge commit. Leaves in a | |
# detached HEAD state, the current HEAD being the patched original commit. | |
# If the patch cannot be applied cleanly, it is saved into ./patch. | |
if [ -z $1 -o -z $2 ] | |
then | |
echo "Usage: remerge.sh <original merge commit> <commit to merge>" | |
exit 1 | |
fi | |
git rev-parse --verify --quiet "$1" > /dev/null | |
if [ $? -ne 0 ] | |
then | |
echo "$1 is not a valid commit." | |
exit 2 | |
fi | |
git rev-parse --verify --quiet "$1^2" > /dev/null | |
if [ $? -ne 0 ] | |
then | |
echo "$1 is not a merge commit." | |
exit 2 | |
fi | |
git rev-parse --verify --quiet "$1^3" > /dev/null | |
if [ $? -eq 0 ] | |
then | |
echo "$1 is an octopus merge commit." | |
exit 2 | |
fi | |
git rev-parse --verify --quiet "$2" > /dev/null | |
if [ $? -ne 0 ] | |
then | |
echo "$1 is not a valid commit." | |
exit 2 | |
fi | |
REBASE_DIR=.git/rebase-apply | |
if [ -e "$REBASE_DIR" ] | |
then | |
echo "$REBASE_DIR exists." | |
exit 3 | |
fi | |
TMP_BRANCH=_remerge_tmp_branch | |
git rev-parse --verify --quiet "$TMP_BRANCH" > /dev/null | |
if [ $? -eq 0 ] | |
then | |
echo "A branch _remerge_tmp_branch exists." | |
exit 3 | |
fi | |
# Rev-parse so that checkouting results in a detached HEAD state. | |
ORIGINAL_MERGE=`git rev-parse "$1"` | |
MERGED_PARENT="$ORIGINAL_MERGE^2" | |
BRANCH_TO_MERGE=$2 | |
# Always use the default commit message. | |
GIT_EDITOR=true | |
export GIT_EDITOR | |
amend_previous_head() { | |
# Turn the previous head into the current state. | |
git reset --soft HEAD@{1} | |
git commit --amend | |
} | |
# Format patch that turns the state of the merged branch in the time when merged | |
# into its current state. A throwaway branch is used for that. | |
git checkout "$MERGED_PARENT" | |
git checkout -b "$TMP_BRANCH" | |
git merge --squash "$BRANCH_TO_MERGE" | |
git commit --allow-empty | |
PATCH=`git format-patch --stdout HEAD^` | |
git checkout "$ORIGINAL_MERGE" | |
git branch -D "$TMP_BRANCH" | |
# Create a new commit whose main parent is the same as of the original merge, | |
# but the merged parent is the provided new one from the changed branch. | |
git checkout "$ORIGINAL_MERGE^1" | |
# The branches are merged using the “ours” strategy to ensure there are no | |
# conflicts. The merge is done just for the commit to have the correct parents, | |
# but the changes are applied as a patch. | |
git merge --strategy=ours "$BRANCH_TO_MERGE" | |
git reset --hard "$ORIGINAL_MERGE" | |
amend_previous_head | |
echo "$PATCH" | git am | |
if [ $? -eq 0 ]; then | |
# The patch applied cleanly, make it a part of the new merge. | |
amend_previous_head | |
echo "The patch has been applied cleanly." | |
else | |
echo "The patch could not be applied cleanly. Stored in \c" | |
PATCH_FILE_NAME="patch" | |
SRC_PATCH_PATH="$REBASE_DIR/$PATCH_FILE_NAME" | |
TARGET_PATCH_PATH="./$PATCH_FILE_NAME" | |
if [ -e "$TARGET_PATCH_PATH" ] | |
then | |
echo "$SRC_PATCH_PATH. Run git am --abort after applying." | |
else | |
cp "$SRC_PATCH_PATH" "$TARGET_PATCH_PATH" | |
git am --abort | |
echo "$TARGET_PATCH_PATH." | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment