diff options
Diffstat (limited to 'git-rebase.sh')
-rwxr-xr-x | git-rebase.sh | 160 |
1 files changed, 121 insertions, 39 deletions
diff --git a/git-rebase.sh b/git-rebase.sh index 528b604cd5..0ade699228 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -34,6 +34,7 @@ set_reflog_action rebase require_work_tree cd_to_toplevel +OK_TO_SKIP_PRE_REBASE= RESOLVEMSG=" When you have resolved this problem run \"git rebase --continue\". If you would prefer to skip this patch, instead run \"git rebase --skip\". @@ -45,7 +46,10 @@ do_merge= dotest="$GIT_DIR"/rebase-merge prec=4 verbose= +diffstat=$(git config --bool rebase.stat) git_am_opt= +rebase_root= +force_rebase= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -138,10 +142,37 @@ finish_rb_merge () { } is_interactive () { - test -f "$dotest"/interactive || - while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac + while test $# != 0 + do + case "$1" in + -i|--interactive) + interactive_rebase=explicit + break + ;; + -p|--preserve-merges) + interactive_rebase=implied + ;; + esac shift - done && test -n "$1" + done + + if [ "$interactive_rebase" = implied ]; then + GIT_EDITOR=: + export GIT_EDITOR + fi + + test -n "$interactive_rebase" || test -f "$dotest"/interactive +} + +run_pre_rebase_hook () { + if test -z "$OK_TO_SKIP_PRE_REBASE" && + test -x "$GIT_DIR/hooks/pre-rebase" + then + "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { + echo >&2 "The pre-rebase hook refused to rebase." + exit 1 + } + fi } test -f "$GIT_DIR"/rebase-apply/applying && @@ -160,6 +191,9 @@ fi while test $# != 0 do case "$1" in + --no-verify) + OK_TO_SKIP_PRE_REBASE=yes + ;; --continue) test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || die "No rebase in progress?" @@ -257,15 +291,37 @@ do esac do_merge=t ;; + -n|--no-stat) + diffstat= + ;; + --stat) + diffstat=t + ;; -v|--verbose) verbose=t + diffstat=t ;; --whitespace=*) git_am_opt="$git_am_opt $1" + case "$1" in + --whitespace=fix|--whitespace=strip) + force_rebase=t + ;; + esac + ;; + --committer-date-is-author-date|--ignore-date) + git_am_opt="$git_am_opt $1" + force_rebase=t ;; -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; + -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force_rebas|--force-rebase) + force_rebase=t + ;; -*) usage ;; @@ -275,6 +331,7 @@ do esac shift done +test $# -gt 2 && usage # Make sure we do not have $GIT_DIR/rebase-apply if test -z "$do_merge" @@ -301,32 +358,41 @@ else fi # The tree must be really really clean. -git update-index --ignore-submodules --refresh || exit +if ! git update-index --ignore-submodules --refresh; then + echo >&2 "cannot rebase: you have unstaged changes" + exit 1 +fi diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) case "$diff" in -?*) echo "cannot rebase: your index is not up-to-date" - echo "$diff" +?*) echo >&2 "cannot rebase: your index contains uncommitted changes" + echo >&2 "$diff" exit 1 ;; esac -# The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" +if test -z "$rebase_root" +then + # The upstream head must be given. Make sure it is valid. + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" + unset root_flag + upstream_arg="$upstream_name" +else + test -z "$newbase" && die "--root must be used with --onto" + unset upstream_name + unset upstream + root_flag="--root" + upstream_arg="$root_flag" +fi # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -if test -x "$GIT_DIR/hooks/pre-rebase" -then - "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { - echo >&2 "The pre-rebase hook refused to rebase." - exit 1 - } -fi +run_pre_rebase_hook "$upstream_arg" "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -334,16 +400,16 @@ fi # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null) + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse --verify "$2" 2>/dev/null) + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -365,7 +431,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or with --root, +# everything leading up to $branch) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -374,17 +441,15 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null then - # Lazily switch to the target branch if needed... - test -z "$switch_to" || git checkout "$switch_to" - echo >&2 "Current branch $branch_name is up to date." - exit 0 -fi - -if test -n "$verbose" -then - echo "Changes from $mb to $onto:" - # We want color (if set), but no pager - GIT_PAGER='' git diff --stat --summary "$mb" "$onto" + if test -z "$force_rebase" + then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || git checkout "$switch_to" + echo >&2 "Current branch $branch_name is up to date." + exit 0 + else + echo "Current branch $branch_name is up to date, rebase forced." + fi fi # Detach HEAD and reset the tree @@ -392,6 +457,16 @@ echo "First, rewinding head to replay your work on top of it..." git checkout -q "$onto^0" || die "could not detach HEAD" git update-ref ORIG_HEAD $branch +if test -n "$diffstat" +then + if test -n "$verbose" + then + echo "Changes from $mb to $onto:" + fi + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary "$mb" "$onto" +fi + # If the $onto is a proper descendant of the tip of the branch, then # we just fast forwarded. if test "$mb" = "$branch" @@ -401,10 +476,17 @@ then exit 0 fi +if test -n "$rebase_root" +then + revisions="$onto..$orig_head" +else + revisions="$upstream..$orig_head" +fi + if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + $root_flag "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -427,7 +509,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" |