From: Junio C Hamano Date: Fri, 2 Nov 2018 02:04:53 +0000 (+0900) Subject: Merge branch 'ag/rebase-i-in-c' X-Git-Tag: v2.20.0-rc0~94 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b49ef560ed66449d24a3fdfe25972c390bb44951 Merge branch 'ag/rebase-i-in-c' Rewrite of the remaining "rebase -i" machinery in C. * ag/rebase-i-in-c: rebase -i: move rebase--helper modes to rebase--interactive rebase -i: remove git-rebase--interactive.sh rebase--interactive2: rewrite the submodes of interactive rebase in C rebase -i: implement the main part of interactive rebase as a builtin rebase -i: rewrite init_basic_state() in C rebase -i: rewrite write_basic_state() in C rebase -i: rewrite the rest of init_revisions_and_shortrevisions() in C rebase -i: implement the logic to initialize $revisions in C rebase -i: remove unused modes and functions rebase -i: rewrite complete_action() in C t3404: todo list with commented-out commands only aborts sequencer: change the way skip_unnecessary_picks() returns its result sequencer: refactor append_todo_help() to write its message to a buffer rebase -i: rewrite checkout_onto() in C rebase -i: rewrite setup_reflog_action() in C sequencer: add a new function to silence a command, except if it fails rebase -i: rewrite the edit-todo functionality in C editor: add a function to launch the sequence editor rebase -i: rewrite append_todo_help() in C sequencer: make three functions and an enum from sequencer.c public --- b49ef560ed66449d24a3fdfe25972c390bb44951 diff --cc .gitignore index 4d5de166e8,406f26d050..0d77ea5894 --- a/.gitignore +++ b/.gitignore @@@ -122,8 -116,6 +122,7 @@@ /git-read-tree /git-rebase /git-rebase--am +/git-rebase--common - /git-rebase--helper /git-rebase--interactive /git-rebase--merge /git-rebase--preserve-merges diff --cc Makefile index 95b93c709d,ca3a0888dd..bbfbb4292d --- a/Makefile +++ b/Makefile @@@ -626,8 -619,6 +626,7 @@@ SCRIPT_SH += git-web--browse.s SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--am +SCRIPT_LIB += git-rebase--common - SCRIPT_LIB += git-rebase--interactive SCRIPT_LIB += git-rebase--preserve-merges SCRIPT_LIB += git-rebase--merge SCRIPT_LIB += git-sh-setup @@@ -952,9 -919,9 +951,10 @@@ LIB_OBJS += progress. LIB_OBJS += prompt.o LIB_OBJS += protocol.o LIB_OBJS += quote.o +LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o + LIB_OBJS += rebase-interactive.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o @@@ -1092,10 -1058,8 +1092,10 @@@ BUILTIN_OBJS += builtin/prune-packed. BUILTIN_OBJS += builtin/prune.o BUILTIN_OBJS += builtin/pull.o BUILTIN_OBJS += builtin/push.o +BUILTIN_OBJS += builtin/range-diff.o BUILTIN_OBJS += builtin/read-tree.o +BUILTIN_OBJS += builtin/rebase.o - BUILTIN_OBJS += builtin/rebase--helper.o + BUILTIN_OBJS += builtin/rebase--interactive.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o BUILTIN_OBJS += builtin/remote.o diff --cc builtin.h index 6fb66f5ba4,7feb689d87..6538932e99 --- a/builtin.h +++ b/builtin.h @@@ -202,10 -201,8 +202,10 @@@ extern int cmd_prune(int argc, const ch extern int cmd_prune_packed(int argc, const char **argv, const char *prefix); extern int cmd_pull(int argc, const char **argv, const char *prefix); extern int cmd_push(int argc, const char **argv, const char *prefix); +extern int cmd_range_diff(int argc, const char **argv, const char *prefix); extern int cmd_read_tree(int argc, const char **argv, const char *prefix); +extern int cmd_rebase(int argc, const char **argv, const char *prefix); - extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix); + extern int cmd_rebase__interactive(int argc, const char **argv, const char *prefix); extern int cmd_receive_pack(int argc, const char **argv, const char *prefix); extern int cmd_reflog(int argc, const char **argv, const char *prefix); extern int cmd_remote(int argc, const char **argv, const char *prefix); diff --cc git-legacy-rebase.sh index 5d92648014,0000000000..75a08b2683 mode 100755,000000..100755 --- a/git-legacy-rebase.sh +++ b/git-legacy-rebase.sh @@@ -1,708 -1,0 +1,745 @@@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano. +# + +SUBDIRECTORY_OK=Yes +OPTIONS_KEEPDASHDASH= +OPTIONS_STUCKLONG=t +OPTIONS_SPEC="\ +git rebase [-i] [options] [--exec ] [--onto ] [] [] +git rebase [-i] [options] [--exec ] [--onto ] --root [] +git rebase --continue | --abort | --skip | --edit-todo +-- + Available options are +v,verbose! display a diffstat of what changed upstream +q,quiet! be quiet. implies --no-stat +autostash automatically stash/stash pop before and after +fork-point use 'merge-base --fork-point' to refine upstream +onto=! rebase onto given branch instead of upstream +r,rebase-merges? try to rebase merges instead of skipping them +p,preserve-merges! try to recreate merges instead of ignoring them +s,strategy=! use the given merge strategy +X,strategy-option=! pass the argument through to the merge strategy +no-ff! cherry-pick all commits, even if unchanged +f,force-rebase! cherry-pick all commits, even if unchanged +m,merge! use merging strategies to rebase +i,interactive! let the user edit the list of commits to rebase +x,exec=! add exec lines after each commit of the editable list +k,keep-empty preserve empty commits during rebase +allow-empty-message allow rebasing commits with empty messages +stat! display a diffstat of what changed upstream +n,no-stat! do not show diffstat of what changed upstream +verify allow pre-rebase hook to run +rerere-autoupdate allow rerere to update index with resolved conflicts +root! rebase all reachable commits up to the root(s) +autosquash move commits that begin with squash!/fixup! under -i +signoff add a Signed-off-by: line to each commit +committer-date-is-author-date! passed to 'git am' +ignore-date! passed to 'git am' +whitespace=! passed to 'git apply' +ignore-whitespace! passed to 'git apply' +C=! passed to 'git apply' +S,gpg-sign? GPG-sign commits + Actions: +continue! continue +abort! abort and check out the original branch +skip! skip current patch and continue +edit-todo! edit the todo list during an interactive rebase +quit! abort but keep HEAD where it is +show-current-patch! show the patch file being applied or merged +" +. git-sh-setup +set_reflog_action rebase +require_work_tree_exists +cd_to_toplevel + +LF=' +' +ok_to_skip_pre_rebase= + +squash_onto= +unset onto +unset restrict_revision +cmd= +strategy= +strategy_opts= +do_merge= +merge_dir="$GIT_DIR"/rebase-merge +apply_dir="$GIT_DIR"/rebase-apply +verbose= +diffstat= +test "$(git config --bool rebase.stat)" = true && diffstat=t +autostash="$(git config --bool rebase.autostash || echo false)" +fork_point=auto +git_am_opt= +git_format_patch_opt= +rebase_root= +force_rebase= +allow_rerere_autoupdate= +# Non-empty if a rebase was in progress when 'git rebase' was invoked +in_progress= +# One of {am, merge, interactive} +type= +# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge} +state_dir= +# One of {'', continue, skip, abort}, as parsed from command line +action= +rebase_merges= +rebase_cousins= +preserve_merges= +autosquash= +keep_empty= +allow_empty_message=--allow-empty-message +signoff= +test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t +case "$(git config --bool commit.gpgsign)" in +true) gpg_sign_opt=-S ;; +*) gpg_sign_opt= ;; +esac +. git-rebase--common + +read_basic_state () { + test -f "$state_dir/head-name" && + test -f "$state_dir/onto" && + head_name=$(cat "$state_dir"/head-name) && + onto=$(cat "$state_dir"/onto) && + # We always write to orig-head, but interactive rebase used to write to + # head. Fall back to reading from head to cover for the case that the + # user upgraded git with an ongoing interactive rebase. + if test -f "$state_dir"/orig-head + then + orig_head=$(cat "$state_dir"/orig-head) + else + orig_head=$(cat "$state_dir"/head) + fi && + GIT_QUIET=$(cat "$state_dir"/quiet) && + test -f "$state_dir"/verbose && verbose=t + test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)" + test -f "$state_dir"/strategy_opts && + strategy_opts="$(cat "$state_dir"/strategy_opts)" + test -f "$state_dir"/allow_rerere_autoupdate && + allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)" + test -f "$state_dir"/gpg_sign_opt && + gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)" + test -f "$state_dir"/signoff && { + signoff="$(cat "$state_dir"/signoff)" + force_rebase=t + } +} + +finish_rebase () { + rm -f "$(git rev-parse --git-path REBASE_HEAD)" + apply_autostash && + { git gc --auto || true; } && + rm -rf "$state_dir" +} + ++run_interactive () { ++ GIT_CHERRY_PICK_HELP="$resolvemsg" ++ export GIT_CHERRY_PICK_HELP ++ ++ test -n "$keep_empty" && keep_empty="--keep-empty" ++ test -n "$rebase_merges" && rebase_merges="--rebase-merges" ++ test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins" ++ test -n "$autosquash" && autosquash="--autosquash" ++ test -n "$verbose" && verbose="--verbose" ++ test -n "$force_rebase" && force_rebase="--no-ff" ++ test -n "$restrict_revision" && \ ++ restrict_revision="--restrict-revision=^$restrict_revision" ++ test -n "$upstream" && upstream="--upstream=$upstream" ++ test -n "$onto" && onto="--onto=$onto" ++ test -n "$squash_onto" && squash_onto="--squash-onto=$squash_onto" ++ test -n "$onto_name" && onto_name="--onto-name=$onto_name" ++ test -n "$head_name" && head_name="--head-name=$head_name" ++ test -n "$strategy" && strategy="--strategy=$strategy" ++ test -n "$strategy_opts" && strategy_opts="--strategy-opts=$strategy_opts" ++ test -n "$switch_to" && switch_to="--switch-to=$switch_to" ++ test -n "$cmd" && cmd="--cmd=$cmd" ++ test -n "$action" && action="--$action" ++ ++ exec git rebase--interactive "$action" "$keep_empty" "$rebase_merges" "$rebase_cousins" \ ++ "$upstream" "$onto" "$squash_onto" "$restrict_revision" \ ++ "$allow_empty_message" "$autosquash" "$verbose" \ ++ "$force_rebase" "$onto_name" "$head_name" "$strategy" \ ++ "$strategy_opts" "$cmd" "$switch_to" \ ++ "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" ++} ++ +run_specific_rebase () { + if [ "$interactive_rebase" = implied ]; then + GIT_EDITOR=: + export GIT_EDITOR + autosquash= + fi - . git-rebase--$type + - if test -z "$preserve_merges" ++ if test -n "$interactive_rebase" -a -z "$preserve_merges" + then - git_rebase__$type ++ run_interactive + else - git_rebase__preserve_merges ++ . git-rebase--$type ++ ++ if test -z "$preserve_merges" ++ then ++ git_rebase__$type ++ else ++ git_rebase__preserve_merges ++ fi + fi + + ret=$? + if test $ret -eq 0 + then + finish_rebase - elif test $ret -eq 2 # special exit status for rebase -i ++ elif test $ret -eq 2 # special exit status for rebase -p + then + apply_autostash && + rm -rf "$state_dir" && + die "Nothing to do" + fi + exit $ret +} + +run_pre_rebase_hook () { + if test -z "$ok_to_skip_pre_rebase" && + test -x "$(git rev-parse --git-path hooks/pre-rebase)" + then + "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} || + die "$(gettext "The pre-rebase hook refused to rebase.")" + fi +} + +test -f "$apply_dir"/applying && + die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")" + +if test -d "$apply_dir" +then + type=am + state_dir="$apply_dir" +elif test -d "$merge_dir" +then + if test -d "$merge_dir"/rewritten + then + type=preserve-merges + interactive_rebase=explicit + preserve_merges=t + elif test -f "$merge_dir"/interactive + then + type=interactive + interactive_rebase=explicit + else + type=merge + fi + state_dir="$merge_dir" +fi +test -n "$type" && in_progress=t + +total_argc=$# +while test $# != 0 +do + case "$1" in + --no-verify) + ok_to_skip_pre_rebase=yes + ;; + --verify) + ok_to_skip_pre_rebase= + ;; + --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch) + test $total_argc -eq 2 || usage + action=${1##--} + ;; + --onto=*) + onto="${1#--onto=}" + ;; + --exec=*) + cmd="${cmd}exec ${1#--exec=}${LF}" + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --interactive) + interactive_rebase=explicit + ;; + --keep-empty) + keep_empty=yes + ;; + --allow-empty-message) + allow_empty_message=--allow-empty-message + ;; + --no-keep-empty) + keep_empty= + ;; + --rebase-merges) + rebase_merges=t + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --rebase-merges=*) + rebase_merges=t + case "${1#*=}" in + rebase-cousins) rebase_cousins=t;; + no-rebase-cousins) rebase_cousins=;; + *) die "Unknown mode: $1";; + esac + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --preserve-merges) + preserve_merges=t + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --autosquash) + autosquash=t + ;; + --no-autosquash) + autosquash= + ;; + --fork-point) + fork_point=t + ;; + --no-fork-point) + fork_point= + ;; + --merge) + do_merge=t + ;; + --strategy-option=*) + strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)" + do_merge=t + test -z "$strategy" && strategy=recursive + ;; + --strategy=*) + strategy="${1#--strategy=}" + do_merge=t + ;; + --no-stat) + diffstat= + ;; + --stat) + diffstat=t + ;; + --autostash) + autostash=true + ;; + --no-autostash) + autostash=false + ;; + --verbose) + verbose=t + diffstat=t + GIT_QUIET= + ;; + --quiet) + GIT_QUIET=t + git_am_opt="$git_am_opt -q" + verbose= + diffstat= + ;; + --whitespace=*) + git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}" + case "${1#--whitespace=}" in + fix|strip) + force_rebase=t + ;; + esac + ;; + --ignore-whitespace) + git_am_opt="$git_am_opt $1" + ;; + --signoff) + signoff=--signoff + ;; + --no-signoff) + signoff= + ;; + --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 + ;; + --force-rebase|--no-ff) + force_rebase=t + ;; + --rerere-autoupdate|--no-rerere-autoupdate) + allow_rerere_autoupdate="$1" + ;; + --gpg-sign) + gpg_sign_opt=-S + ;; + --gpg-sign=*) + gpg_sign_opt="-S${1#--gpg-sign=}" + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done +test $# -gt 2 && usage + +if test -n "$action" +then + test -z "$in_progress" && die "$(gettext "No rebase in progress?")" + # Only interactive rebase uses detailed reflog messages + if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase + then + GIT_REFLOG_ACTION="rebase -i ($action)" + export GIT_REFLOG_ACTION + fi +fi + +if test "$action" = "edit-todo" && test -z "$interactive_rebase" +then + die "$(gettext "The --edit-todo action can only be used during interactive rebase.")" +fi + +case "$action" in +continue) + # Sanity check + git rev-parse --verify HEAD >/dev/null || + die "$(gettext "Cannot read HEAD")" + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules || { + echo "$(gettext "You must edit all merge conflicts and then +mark them as resolved using git add")" + exit 1 + } + read_basic_state + run_specific_rebase + ;; +skip) + output git reset --hard HEAD || exit $? + read_basic_state + run_specific_rebase + ;; +abort) + git rerere clear + read_basic_state + case "$head_name" in + refs/*) + git symbolic-ref -m "rebase: aborting" HEAD $head_name || + die "$(eval_gettext "Could not move back to \$head_name")" + ;; + esac + output git reset --hard $orig_head + finish_rebase + exit + ;; +quit) + exec rm -rf "$state_dir" + ;; +edit-todo) + run_specific_rebase + ;; +show-current-patch) + run_specific_rebase + die "BUG: run_specific_rebase is not supposed to return here" + ;; +esac + +# Make sure no rebase is in progress +if test -n "$in_progress" +then + state_dir_base=${state_dir##*/} + cmd_live_rebase="git rebase (--continue | --abort | --skip)" + cmd_clear_stale_rebase="rm -fr \"$state_dir\"" + die " +$(eval_gettext 'It seems that there is already a $state_dir_base directory, and +I wonder if you are in the middle of another rebase. If that is the +case, please try + $cmd_live_rebase +If that is not the case, please + $cmd_clear_stale_rebase +and run me again. I am stopping in case you still have something +valuable there.')" +fi + +if test -n "$rebase_root" && test -z "$onto" +then + test -z "$interactive_rebase" && interactive_rebase=implied +fi + +if test -n "$keep_empty" +then + test -z "$interactive_rebase" && interactive_rebase=implied +fi + +if test -n "$interactive_rebase" +then + if test -z "$preserve_merges" + then + type=interactive + else + type=preserve-merges + fi + + state_dir="$merge_dir" +elif test -n "$do_merge" +then + type=merge + state_dir="$merge_dir" +else + type=am + state_dir="$apply_dir" +fi + +if test -t 2 && test -z "$GIT_QUIET" +then + git_format_patch_opt="$git_format_patch_opt --progress" +fi + +if test -n "$git_am_opt"; then + incompatible_opts=$(echo " $git_am_opt " | \ + sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/') + if test -n "$interactive_rebase" + then + if test -n "$incompatible_opts" + then + die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")" + fi + fi + if test -n "$do_merge"; then + if test -n "$incompatible_opts" + then + die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")" + fi + fi +fi + +if test -n "$signoff" +then + test -n "$preserve_merges" && + die "$(gettext "error: cannot combine '--signoff' with '--preserve-merges'")" + git_am_opt="$git_am_opt $signoff" + force_rebase=t +fi + +if test -n "$preserve_merges" +then + # Note: incompatibility with --signoff handled in signoff block above + # Note: incompatibility with --interactive is just a strong warning; + # git-rebase.txt caveats with "unless you know what you are doing" + test -n "$rebase_merges" && + die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")" +fi + +if test -n "$rebase_merges" +then + test -n "$strategy_opts" && + die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")" + test -n "$strategy" && + die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")" +fi + +if test -z "$rebase_root" +then + case "$#" in + 0) + if ! upstream_name=$(git rev-parse --symbolic-full-name \ + --verify -q @{upstream} 2>/dev/null) + then + . git-parse-remote + error_on_missing_default_upstream "rebase" "rebase" \ + "against" "git rebase $(gettext '')" + fi + + test "$fork_point" = auto && fork_point=t + ;; + *) upstream_name="$1" + if test "$upstream_name" = "-" + then + upstream_name="@{-1}" + fi + shift + ;; + esac + upstream=$(peel_committish "${upstream_name}") || + die "$(eval_gettext "invalid upstream '\$upstream_name'")" + upstream_arg="$upstream_name" +else + if test -z "$onto" + then + empty_tree=$(git hash-object -t tree /dev/null) + onto=$(git commit-tree $empty_tree or "detached HEAD" +switch_to= +case "$#" in +1) + # Is it "rebase other $branchname" or "rebase other $commit"? + branch_name="$1" + switch_to="$1" + + # Is it a local branch? + if git show-ref --verify --quiet -- "refs/heads/$branch_name" && + orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name") + then + head_name="refs/heads/$branch_name" + # If not is it a valid ref (branch or commit)? + elif orig_head=$(git rev-parse -q --verify "$branch_name") + then + head_name="detached HEAD" + + else + die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")" + fi + ;; +0) + # Do not need to switch branches, we are already on it. + if branch_name=$(git symbolic-ref -q HEAD) + then + head_name=$branch_name + branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)') + else + head_name="detached HEAD" + branch_name=HEAD + fi + orig_head=$(git rev-parse --verify HEAD) || exit + ;; +*) + die "BUG: unexpected number of arguments left to parse" + ;; +esac + +if test "$fork_point" = t +then + new_upstream=$(git merge-base --fork-point "$upstream_name" \ + "${switch_to:-HEAD}") + if test -n "$new_upstream" + then + restrict_revision=$new_upstream + fi +fi + +if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null +then + stash_sha1=$(git stash create "autostash") || + die "$(gettext 'Cannot autostash')" + + mkdir -p "$state_dir" && + echo $stash_sha1 >"$state_dir/autostash" && + stash_abbrev=$(git rev-parse --short $stash_sha1) && + echo "$(eval_gettext 'Created autostash: $stash_abbrev')" && + git reset --hard +fi + +require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")" + +# Now we are rebasing commits $upstream..$orig_head (or with --root, +# everything leading up to $orig_head) 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 +# and if this is not an interactive rebase. +mb=$(git merge-base "$onto" "$orig_head") +if test -z "$interactive_rebase" && test "$upstream" = "$onto" && + test "$mb" = "$onto" && test -z "$restrict_revision" && + # linear history? + ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null +then + if test -z "$force_rebase" + then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \ + git checkout -q "$switch_to" -- + if test "$branch_name" = "HEAD" && + ! git symbolic-ref -q HEAD + then + say "$(eval_gettext "HEAD is up to date.")" + else + say "$(eval_gettext "Current branch \$branch_name is up to date.")" + fi + finish_rebase + exit 0 + else + if test "$branch_name" = "HEAD" && + ! git symbolic-ref -q HEAD + then + say "$(eval_gettext "HEAD is up to date, rebase forced.")" + else + say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")" + fi + fi +fi + +# If a hook exists, give it a chance to interrupt +run_pre_rebase_hook "$upstream_arg" "$@" + +if test -n "$diffstat" +then + if test -n "$verbose" + then + echo "$(eval_gettext "Changes from \$mb to \$onto:")" + fi + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary "$mb" "$onto" +fi + +test -n "$interactive_rebase" && run_specific_rebase + +# Detach HEAD and reset the tree +say "$(gettext "First, rewinding head to replay your work on top of it...")" + +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \ + git checkout -q "$onto^0" || die "could not detach HEAD" +git update-ref ORIG_HEAD $orig_head + +# If the $onto is a proper descendant of the tip of the branch, then +# we just fast-forwarded. +if test "$mb" = "$orig_head" +then + say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")" + move_to_original_branch + finish_rebase + exit 0 +fi + +if test -n "$rebase_root" +then + revisions="$onto..$orig_head" +else + revisions="${restrict_revision-$upstream}..$orig_head" +fi + +run_specific_rebase diff --cc git.c index 8e52276831,81aabd1423..2f604a41ea --- a/git.c +++ b/git.c @@@ -524,15 -517,8 +524,15 @@@ static struct cmd_struct commands[] = { "prune-packed", cmd_prune_packed, RUN_SETUP }, { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE }, { "push", cmd_push, RUN_SETUP }, + { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER }, { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX}, + /* + * NEEDSWORK: Until the rebase is independent and needs no redirection + * to rebase shell script this is kept as is, then should be changed to + * RUN_SETUP | NEED_WORK_TREE + */ + { "rebase", cmd_rebase }, - { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE }, + { "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE }, { "receive-pack", cmd_receive_pack }, { "reflog", cmd_reflog, RUN_SETUP }, { "remote", cmd_remote, RUN_SETUP }, diff --cc sequencer.c index 0c164d5f98,8dd6db5a01..3c86c7694b --- a/sequencer.c +++ b/sequencer.c @@@ -30,7 -30,7 +30,8 @@@ #include "oidset.h" #include "commit-slab.h" #include "alias.h" +#include "commit-reach.h" + #include "rebase-interactive.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@@ -903,24 -876,13 +918,13 @@@ static int run_git_commit(const char *d if ((flags & ALLOW_EMPTY)) argv_array_push(&cmd.args, "--allow-empty"); - if (opts->allow_empty_message) + if (!(flags & EDIT_MSG)) argv_array_push(&cmd.args, "--allow-empty-message"); - if (cmd.err == -1) { - /* hide stderr on success */ - struct strbuf buf = STRBUF_INIT; - int rc = pipe_command(&cmd, - NULL, 0, - /* stdout is already redirected */ - NULL, 0, - &buf, 0); - if (rc) - fputs(buf.buf, stderr); - strbuf_release(&buf); - return rc; - } - - return run_command(&cmd); + if (is_rebase_i(opts) && !(flags & EDIT_MSG)) + return run_command_silent_on_success(&cmd); + else + return run_command(&cmd); } static int rest_is_empty(const struct strbuf *sb, int start) @@@ -4587,9 -4515,9 +4690,9 @@@ static int skip_unnecessary_picks(struc if (item->commit->parents->next) break; /* merge commit */ parent_oid = &item->commit->parents->item->object.oid; - if (!oideq(parent_oid, oid)) - if (hashcmp(parent_oid->hash, output_oid->hash)) ++ if (!oideq(parent_oid, output_oid)) break; - oid = &item->commit->object.oid; + oidcpy(output_oid, &item->commit->object.oid); } if (i > 0) { int offset = get_item_line_offset(&todo_list, i); diff --cc sequencer.h index c986bc8251,aab280f276..660cff5050 --- a/sequencer.h +++ b/sequencer.h @@@ -1,13 -1,9 +1,14 @@@ #ifndef SEQUENCER_H #define SEQUENCER_H +#include "cache.h" +#include "strbuf.h" + +struct commit; + const char *git_path_commit_editmsg(void); const char *git_path_seq_dir(void); + const char *rebase_path_todo(void); #define APPEND_SIGNOFF_DEDUP (1u << 0) diff --cc strbuf.h index bf18fddb5b,66da9822fd..9981f782b2 --- a/strbuf.h +++ b/strbuf.h @@@ -582,14 -574,11 +582,16 @@@ void strbuf_add_unique_abbrev(struct st * run in. If the buffer is NULL the editor is launched as usual but the * file's contents are not read into the buffer upon completion. */ - int launch_editor(const char *path, - struct strbuf *buffer, -extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); -extern int launch_sequence_editor(const char *path, struct strbuf *buffer, - const char *const *env); ++int launch_editor(const char *path, struct strbuf *buffer, + const char *const *env); + ++int launch_sequence_editor(const char *path, struct strbuf *buffer, ++ const char *const *env); + -extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size); +void strbuf_add_lines(struct strbuf *sb, + const char *prefix, + const char *buf, + size_t size); /** * Append s to sb, with the characters '<', '>', '&' and '"' converted