Skip to content

Blog

Fix branch hierarchies in git

You use git, good on ya! You branched off of a branch that should've been left alone. How do you change the "parent" of the new branch?!

Posted on Monday, September 18, 2017

Fix branch hierarchies in git ​

Problem ​

In git, you branched off of a branch that has a failed build. You fixed the build in the child branch, but your CI server (e.g. Atlassian Bamboo) sees the failed build on the parent branch as a blocker for you to be able to merge back to develop.

Git Structure

develop
  | -- bad-branch
         | -- good-branch
         
# We want to merge good-branch to develop

Solution ​

Summary: Re-parent good-branch to develop 😃

How-To

Inspired from this SO Thread

  1. Get the shell script from here
#!/bin/sh
# Copyright 2013 Google Inc.
# Written by Mark Lodato <lodato@google.com>.
# Distributed under the GNU General Public License, version 2.0.

OPTIONS_SPEC="\
git reparent [OPTIONS] ((-p <parent>)... | --no-parent)
Recommit HEAD with a new set of parents.
--
h,help          show the help
e,edit          edit the commit message in an editor first
keep-committer  do not rewrite committer name and date
m,message=      use the given message instead of that of HEAD
p,parent=!      new parent to use; may be given multiple times
no-parent!      create a parentless commit
q,quiet         be quiet; only report errors
reset*          default behavior
no-reset!       print the new object id instead of updating HEAD
"
SUBDIRECTORY_OK=Yes
. "$(git --exec-path)/git-sh-setup" || exit $?
require_clean_work_tree reparent "Please commit or stash them first."

# Location of the temporary message.
msg_file="$GIT_DIR/reparent-msg"

die_with_usage() {
echo "error: $1" >&2
usage
}

get_committer_ident_from_commit () {
encoding=$(git config i18n.commitencoding || echo UTF-8)
git show -s --pretty=raw --encoding="$encoding" "$1" -- |
parse_ident_from_commit committer COMMITTER
}

edit=
message=
keep_committer=
no_parent=
no_reset=
parent_flags=
quiet=
while [ $# -gt 0 ]; do
case "$1" in
-p)
[ $# -eq 0 ] && die_with_usage "-p requires an argument"
shift
parent_flags="$parent_flags$(git rev-parse --sq-quote -p "$1")"
;;
--no-parent)         no_parent=1 ;;
-e)                  edit=1 ;;
--no-edit)           edit= ;;
-m)                  message="$2"; shift ;;
--no-message)        message= ;;
--keep-committer)    keep_committer=1 ;;
--no-keep-committer) keep_committer= ;;
-q)                  quiet=-q ;;
--no-quiet)          quiet= ;;
--reset)             no_reset= ;;
--no-reset)          no_reset=1 ;;
--)          shift; break ;;
*)           die "internal error: unknown flag $1" ;;
esac
shift
done
[ $# -gt 0 ] && \
die_with_usage "no positional arguments expected"
[ -z "$no_parent" -a -z "$parent_flags" ] && \
die_with_usage "either -p or --no-parent is required"
[ -n "$no_parent" -a -n "$parent_flags" ] && \
die_with_usage "-p and --no-parent are mutually exclusive"

# Create the commit.
if [ -n "$message" ]; then
echo "$message" > "$msg_file"
else
git cat-file commit HEAD | sed "1,/^$/d" > "$msg_file"
fi
if [ -n "$edit" ]; then
# TODO: use the normal `git commit` comment stripping stuff
git_editor "$msg_file" || die "no editor configured"
[ -s "$msg_file" ] || die "aborting due to empty commit message"
fi
eval "$(get_author_ident_from_commit HEAD)"
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
if [ -n "$keep_committer" ]; then
eval "$(get_committer_ident_from_commit HEAD)"
export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE
fi
old_head="$(git rev-parse --short HEAD)" || exit $?
new_head="$(eval "git commit-tree HEAD: $parent_flags" '< "$msg_file"')" || \
exit $?
rm "$msg_file"

# Print out the commit if --no-reset; otherwise update HEAD.
if [ -n "$no_reset" ]; then
echo "$new_head"
else
set_reflog_action reparent
git reset $quiet "$new_head" || exit $?
new_abbrev="$(git rev-parse --short HEAD)" || exit $?
[ -z "$quiet" ] && echo "Moved HEAD to $new_abbrev (was $old_head)"
fi
  1. To convert to Unix Format: download dos2Unix from here
dos2unix filename.extension
  1. Now open git bash or Ubuntu terminal, and issue this command:
chmod +x git-reparent.sh
  1. To reparent good-branch to develop:
git-reparent.sh -p develop
# Force push, because we know what we're doing :-)
git push -f