| #!/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} |