blob: 02a04e517dde1748ee1c6f04022aae35ff40b61b [file] [log] [blame]
#!/bin/sh
#
# Rebase 'master' on top of an upstream branch (defaults to 'junio/next'),
# retaining "fast-forwardability" by "merging" (with the "ours" strategy) the
# previous state on top of the current upstream state.
#
# options:
# --dry-run
# do not perform the rebase but only display the revisions selected
#
# --graph
# as for dry-run but display the commits as a graph
#
# --cherry
# as for --dry-run but display the commits using cherry notation to mark
# commits that are suitable for upstream consideration.
force=
dryrun=
graph=
cherry=
while test $# -gt 0
do
case "$1" in
-f|--force)
force=--force
;;
-s|--show|-d|--dry-run)
dryrun=t
;;
-g|--graph)
dryrun=t
graph=--graph
;;
-c|--cherry)
dryrun=t
cherry=--cherry
;;
-h|--help)
cat >&2 << EOF
Usage: $0 [options] [<upstream>]
Options:
-s|--show show which commits would be cherry-picked
-g|--graph like --show, but with the commit graph
-c|--cherry try to leave out commits that were applied upstream
EOF
exit 1
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
break
;;
esac
shift
done
TO=${1:-junio/next}
if test -z "$force" && test -z "$(git rev-list .."$TO")"
then
echo "Nothing new in $TO; To force a rebase, use the --force, Luke!" >&2
exit 1
fi
HEAD_NAME="$(git rev-parse --symbolic-full-name HEAD)"
case "$HEAD_NAME" in
refs/heads/*)
UPSTREAM=$(git rev-parse --symbolic-full-name HEAD@{u}) || {
echo "Not tracking any remote branch!" >&2
exit 1
}
test "$(git rev-parse HEAD)" = "$(git rev-parse $UPSTREAM)" ||
test "$(git rev-parse $HEAD_NAME@{1})" = "$(git rev-parse $UPSTREAM)" || {
echo "Your '$HEAD_NAME' is not up-to-date!" >&2
exit 1
}
;; # okay
HEAD) ;; # okay
*)
echo "Not on any branch!" >&2
exit 1
;;
esac
FROM_SHA1=$(git rev-parse --short HEAD)
TO_SHA1=$(git rev-parse --short $TO)
is_ours_merge () {
test "$(git rev-parse $1:)" = "$(git rev-parse "$1^:")"
}
list_merges () {
git rev-list --parents "$@" | sed -n 's/ .* .*//p'
}
# Find old merging rebase, if any
REBASING_BASE=
for commit in $(list_merges $TO..)
do
if is_ours_merge $commit
then
subject="$(git show -s --format=%s $commit)"
case "$subject" in
*merging-rebase*) ;;
*)
printf "%s\n\n%s\n%s\n\n(y/n) " \
"Is this the latest merging rebase?" \
$commit "$subject"
read answer
case "$answer" in
y*|Y*) ;;
*) continue;;
esac;;
esac
REBASING_BASE=$commit
break
fi
done
BASE_MESSAGE=
if test -n "$REBASING_BASE"
then
BASE_MESSAGE="using $REBASING_BASE as base."
MESSAGE="$(git cat-file commit $REBASING_BASE |
sed '1,/^$/d')"
# old style rebasing merge?
case "$MESSAGE" in
"Rebasing merge to "*)
BASE_MESSAGE="using the rebasing merge $REBASING_BASE."
# Fake a commit such that 'git log <commit>..' shows all the
# commits we want to rebase
PREVIOUS_REBASING="$(git rev-parse $REBASING_BASE^2)"
PREVIOUS_ONTO="$(echo "$MESSAGE" |
sed -n '1s/Rebasing merge .*(\(.*\))/\1/p')"
# To avoid checking out unneeded files/file versions, the
# throw-away base points to the tree we want to rebase onto
REBASING_BASE="$(echo Dummy |
git commit-tree $TO_SHA1: \
-p $PREVIOUS_ONTO -p $PREVIOUS_REBASING)"
;;
esac
fi
RANGE=$REBASING_BASE..
if test -n "$dryrun"
then
git log --oneline $graph $cherry --boundary $RANGE
exit
fi
exec "$(dirname "$0")"/shears.sh $force --merging --onto=$TO ${REBASING_BASE:-$TO}