From e0645b3ad7e70519437e3b7a9ae6dc29488d13f2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 04:22:31 -0700 Subject: [PATCH 01/16] Drop unused git-rebase--am.sh Since 21853626ea (built-in rebase: call `git am` directly, 2019-01-18), the built-in rebase already uses the built-in `git am` directly. Now that d03ebd411c (rebase: remove the rebase.useBuiltin setting, 2019-03-18) even removed the scripted rebase, there is no longer any user of `git-rebase--am.sh`, so let's just remove it. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- .gitignore | 1 - Makefile | 1 - builtin/rebase.c | 4 --- git-rebase--am.sh | 85 ----------------------------------------------- 4 files changed, 91 deletions(-) delete mode 100644 git-rebase--am.sh diff --git a/.gitignore b/.gitignore index 2374f77a1aae58..875f3fc6e8ac20 100644 --- a/.gitignore +++ b/.gitignore @@ -122,7 +122,6 @@ /git-range-diff /git-read-tree /git-rebase -/git-rebase--am /git-rebase--common /git-rebase--interactive /git-rebase--preserve-merges diff --git a/Makefile b/Makefile index 8a7e2353520ddd..63e1973333272a 100644 --- a/Makefile +++ b/Makefile @@ -624,7 +624,6 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote -SCRIPT_LIB += git-rebase--am SCRIPT_LIB += git-rebase--common SCRIPT_LIB += git-rebase--preserve-merges SCRIPT_LIB += git-sh-setup diff --git a/builtin/rebase.c b/builtin/rebase.c index 21681a551b1983..4dd76ee5761e24 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1153,10 +1153,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) } switch (opts->type) { - case REBASE_AM: - backend = "git-rebase--am"; - backend_func = "git_rebase__am"; - break; case REBASE_PRESERVE_MERGES: backend = "git-rebase--preserve-merges"; backend_func = "git_rebase__preserve_merges"; diff --git a/git-rebase--am.sh b/git-rebase--am.sh deleted file mode 100644 index 6416716ee620cf..00000000000000 --- a/git-rebase--am.sh +++ /dev/null @@ -1,85 +0,0 @@ -# This shell script fragment is sourced by git-rebase to implement -# its default, fast, patch-based, non-interactive mode. -# -# Copyright (c) 2010 Junio C Hamano. -# - -git_rebase__am () { - -case "$action" in -continue) - git am --resolved --resolvemsg="$resolvemsg" \ - ${gpg_sign_opt:+"$gpg_sign_opt"} && - move_to_original_branch - return - ;; -skip) - git am --skip --resolvemsg="$resolvemsg" && - move_to_original_branch - return - ;; -show-current-patch) - exec git am --show-current-patch - ;; -esac - -if test -z "$rebase_root" - # this is now equivalent to ! -z "$upstream" -then - revisions=$upstream...$orig_head -else - revisions=$onto...$orig_head -fi - -ret=0 -rm -f "$GIT_DIR/rebased-patches" - -git format-patch -k --stdout --full-index --cherry-pick --right-only \ - --src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \ - --pretty=mboxrd --topo-order \ - $git_format_patch_opt \ - "$revisions" ${restrict_revision+^$restrict_revision} \ - >"$GIT_DIR/rebased-patches" -ret=$? - -if test 0 != $ret -then - rm -f "$GIT_DIR/rebased-patches" - case "$head_name" in - refs/heads/*) - git checkout -q "$head_name" - ;; - *) - git checkout -q "$orig_head" - ;; - esac - - cat >&2 <<-EOF - - git encountered an error while preparing the patches to replay - these revisions: - - $revisions - - As a result, git cannot rebase them. - EOF - return $ret -fi - -git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \ - --patch-format=mboxrd \ - $allow_rerere_autoupdate \ - ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches" -ret=$? - -rm -f "$GIT_DIR/rebased-patches" - -if test 0 != $ret -then - test -d "$state_dir" && write_basic_state - return $ret -fi - -move_to_original_branch - -} From 6e8be7d87352cdd01d92cf4d608b4e32097db27d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 04:22:31 -0700 Subject: [PATCH 02/16] t3400: stop referring to the scripted rebase One test case's title mentioned the then-current implementation detail that the `--am` backend was implemented in `git-rebase--am.sh`. This is no longer the case, so let's update the title to reflect the current reality. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t3400-rebase.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 42f147858d7c2e..80b23fd3269c78 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -285,7 +285,7 @@ EOF test_cmp From_.msg out ' -test_expect_success 'rebase--am.sh and --show-current-patch' ' +test_expect_success 'rebase --am and --show-current-patch' ' test_create_repo conflict-apply && ( cd conflict-apply && From 9e00e66dc70cf6600180a63e44737456d335f7b9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 04:22:32 -0700 Subject: [PATCH 03/16] .gitignore: there is no longer a built-in `git-rebase--interactive` This went away in 0609b741a4 (rebase -i: combine rebase--interactive.c with rebase.c, 2019-04-17). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 875f3fc6e8ac20..bcee4fda81df96 100644 --- a/.gitignore +++ b/.gitignore @@ -123,7 +123,6 @@ /git-read-tree /git-rebase /git-rebase--common -/git-rebase--interactive /git-rebase--preserve-merges /git-receive-pack /git-reflog From db9ec248e1be7144602b7dfff7972249851d30e1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 04:22:33 -0700 Subject: [PATCH 04/16] sequencer: the `am` and `rebase--interactive` scripts are gone Update a code comment that referred to those files as if they were still there. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- sequencer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sequencer.c b/sequencer.c index f88a97fb10a322..334de145421c1f 100644 --- a/sequencer.c +++ b/sequencer.c @@ -767,7 +767,7 @@ static int parse_key_value_squoted(char *buf, struct string_list *list) * GIT_AUTHOR_DATE='$author_date' * * where $author_name, $author_email and $author_date are quoted. We are strict - * with our parsing, as the file was meant to be eval'd in the old + * with our parsing, as the file was meant to be eval'd in the now-removed * git-am.sh/git-rebase--interactive.sh scripts, and thus if the file differs * from what this function expects, it is better to bail out than to do * something that the user does not expect. From 38c8e3e284f1ac3574e157786fc0b1d76491fa17 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 04:22:34 -0700 Subject: [PATCH 05/16] rebase: fold git-rebase--common into the -p backend The only remaining scripted part of `git rebase` is the `--preserve-merges` backend. Meaning: there is little reason to keep the "library of common rebase functions" as a separate file. While moving the functions to `git-rebase--preserve-merges.sh`, we also drop the `move_to_original_branch` function that is no longer used. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- .gitignore | 1 - Makefile | 1 - builtin/rebase.c | 3 +- git-rebase--common.sh | 69 ---------------------------------- git-rebase--preserve-merges.sh | 55 +++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 73 deletions(-) delete mode 100644 git-rebase--common.sh diff --git a/.gitignore b/.gitignore index bcee4fda81df96..4470d7cfc0ae72 100644 --- a/.gitignore +++ b/.gitignore @@ -122,7 +122,6 @@ /git-range-diff /git-read-tree /git-rebase -/git-rebase--common /git-rebase--preserve-merges /git-receive-pack /git-reflog diff --git a/Makefile b/Makefile index 63e1973333272a..6c3bfb1733e2b2 100644 --- a/Makefile +++ b/Makefile @@ -624,7 +624,6 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote -SCRIPT_LIB += git-rebase--common SCRIPT_LIB += git-rebase--preserve-merges SCRIPT_LIB += git-sh-setup SCRIPT_LIB += git-sh-i18n diff --git a/builtin/rebase.c b/builtin/rebase.c index 4dd76ee5761e24..74a60e8c83f7b2 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1163,8 +1163,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) } strbuf_addf(&script_snippet, - ". git-sh-setup && . git-rebase--common &&" - " . %s && %s", backend, backend_func); + ". git-sh-setup && . %s && %s", backend, backend_func); argv[0] = script_snippet.buf; status = run_command_v_opt(argv, RUN_USING_SHELL); diff --git a/git-rebase--common.sh b/git-rebase--common.sh deleted file mode 100644 index f00e13e5d04df0..00000000000000 --- a/git-rebase--common.sh +++ /dev/null @@ -1,69 +0,0 @@ - -resolvemsg=" -$(gettext 'Resolve all conflicts manually, mark them as resolved with -"git add/rm ", then run "git rebase --continue". -You can instead skip this commit: run "git rebase --skip". -To abort and get back to the state before "git rebase", run "git rebase --abort".') -" - -write_basic_state () { - echo "$head_name" > "$state_dir"/head-name && - echo "$onto" > "$state_dir"/onto && - echo "$orig_head" > "$state_dir"/orig-head && - test t = "$GIT_QUIET" && : > "$state_dir"/quiet - test t = "$verbose" && : > "$state_dir"/verbose - test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy - test -n "$strategy_opts" && echo "$strategy_opts" > \ - "$state_dir"/strategy_opts - test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \ - "$state_dir"/allow_rerere_autoupdate - test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt - test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff - test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec -} - -apply_autostash () { - if test -f "$state_dir/autostash" - then - stash_sha1=$(cat "$state_dir/autostash") - if git stash apply $stash_sha1 >/dev/null 2>&1 - then - echo "$(gettext 'Applied autostash.')" >&2 - else - git stash store -m "autostash" -q $stash_sha1 || - die "$(eval_gettext "Cannot store \$stash_sha1")" - gettext 'Applying autostash resulted in conflicts. -Your changes are safe in the stash. -You can run "git stash pop" or "git stash drop" at any time. -' >&2 - fi - fi -} - -move_to_original_branch () { - case "$head_name" in - refs/*) - message="rebase finished: $head_name onto $onto" - git update-ref -m "$message" \ - $head_name $(git rev-parse HEAD) $orig_head && - git symbolic-ref \ - -m "rebase finished: returning to $head_name" \ - HEAD $head_name || - die "$(eval_gettext "Could not move back to \$head_name")" - ;; - esac -} - -output () { - case "$verbose" in - '') - output=$("$@" 2>&1 ) - status=$? - test $status != 0 && printf "%s\n" "$output" - return $status - ;; - *) - "$@" - ;; - esac -} diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh index afbb65765d4610..dec90e9af67785 100644 --- a/git-rebase--preserve-merges.sh +++ b/git-rebase--preserve-merges.sh @@ -77,6 +77,61 @@ rewritten_pending="$state_dir"/rewritten-pending # and leaves CR at the end instead. cr=$(printf "\015") +resolvemsg=" +$(gettext 'Resolve all conflicts manually, mark them as resolved with +"git add/rm ", then run "git rebase --continue". +You can instead skip this commit: run "git rebase --skip". +To abort and get back to the state before "git rebase", run "git rebase --abort".') +" + +write_basic_state () { + echo "$head_name" > "$state_dir"/head-name && + echo "$onto" > "$state_dir"/onto && + echo "$orig_head" > "$state_dir"/orig-head && + test t = "$GIT_QUIET" && : > "$state_dir"/quiet + test t = "$verbose" && : > "$state_dir"/verbose + test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy + test -n "$strategy_opts" && echo "$strategy_opts" > \ + "$state_dir"/strategy_opts + test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \ + "$state_dir"/allow_rerere_autoupdate + test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt + test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff + test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec +} + +apply_autostash () { + if test -f "$state_dir/autostash" + then + stash_sha1=$(cat "$state_dir/autostash") + if git stash apply $stash_sha1 >/dev/null 2>&1 + then + echo "$(gettext 'Applied autostash.')" >&2 + else + git stash store -m "autostash" -q $stash_sha1 || + die "$(eval_gettext "Cannot store \$stash_sha1")" + gettext 'Applying autostash resulted in conflicts. +Your changes are safe in the stash. +You can run "git stash pop" or "git stash drop" at any time. +' >&2 + fi + fi +} + +output () { + case "$verbose" in + '') + output=$("$@" 2>&1 ) + status=$? + test $status != 0 && printf "%s\n" "$output" + return $status + ;; + *) + "$@" + ;; + esac +} + strategy_args=${strategy:+--strategy=$strategy} test -n "$strategy_opts" && eval ' From b3daf078e8804dc7bb9099fa1a0b723c8357ac76 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 21:26:37 +0200 Subject: [PATCH 06/16] t3427: add a clarifying comment The flow of this test script is outright confusing, and to start the endeavor to address that, let's describe what this test is all about, and how it tries to do it. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index d8640522a08696..3a2ae7b55d5da1 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -11,6 +11,34 @@ commit_message() { git log --pretty=format:%s -1 "$1" } +# There are a few bugs in the rebase with regards to the subtree strategy, and +# this test script tries to document them. First, the following commit history +# is generated (the onelines are shown, time flows from left to right): +# +# master1 - master2 - master3 +# \ +# README ---------------------- Add subproject master - master4 - files_subtree/master5 +# +# Where the merge moves the files master[123].t into the subdirectory +# files_subtree/ and master4 as well as files_subtree/master5 add files to that +# directory directly. +# +# Then, in subsequent test cases, `git filter-branch` is used to distill just +# the commits that touch files_subtree/. To give it a final pre-rebase touch, +# an empty commit is added on top. The pre-rebase commit history looks like +# this: +# +# Add subproject master - master4 - files_subtree/master5 - Empty commit +# +# where the root commit adds three files: master1.t, master2.t and master3.t. +# +# This commit history is then rebased onto `master3` with the +# `-Xsubtree=files_subtree` option in three different ways: +# +# 1. using `--preserve-merges` +# 2. using `--preserve-merges` and --keep-empty +# 3. without specifying a rebase backend + test_expect_success 'setup' ' test_commit README && mkdir files && From c168c4499b674db3aefa4d0303c349a2464a3de1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 21:44:58 +0200 Subject: [PATCH 07/16] t3427: simplify the `setup` test case significantly It still does the very same thing as before, but expresses it in a much more succinct (and still quite readable) manner. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index 3a2ae7b55d5da1..36c4f92e061748 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -41,27 +41,21 @@ commit_message() { test_expect_success 'setup' ' test_commit README && - mkdir files && - ( - cd files && - git init && - test_commit master1 && - test_commit master2 && - test_commit master3 - ) && - git fetch files master && - git branch files-master FETCH_HEAD && - git read-tree --prefix=files_subtree files-master && - git checkout -- files_subtree && - tree=$(git write-tree) && - head=$(git rev-parse HEAD) && - rev=$(git rev-parse --verify files-master^0) && - commit=$(git commit-tree -p $head -p $rev -m "Add subproject master" $tree) && - git update-ref HEAD $commit && - ( - cd files_subtree && - test_commit master4 - ) && + + git init files && + test_commit -C files master1 && + test_commit -C files master2 && + test_commit -C files master3 && + + : perform subtree merge into files_subtree/ && + git fetch files refs/heads/master:refs/heads/files-master && + git merge -s ours --no-commit --allow-unrelated-histories \ + files-master && + git read-tree --prefix=files_subtree -u files-master && + git commit -m "Add subproject master" && + + : add two extra commits to rebase && + test_commit -C files_subtree master4 && test_commit files_subtree/master5 ' From 138ff362fbf3d948e431489841602d787e360c69 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 21:50:56 +0200 Subject: [PATCH 08/16] t3427: move the `filter-branch` invocation into the `setup` case The step to prepare a pre-rebase commit history is _identical_ in _all_ of the test cases (except of course the `setup` case). It should therefore clearly a part of the `setup` test case instead. As the `git filter-branch` command is quite costly on platforms where Unix shell scripting is simply slow (meaning: on Windows), this shaves off a noticeable part of the runtime: in this developer's setup, the time was reduced from ~1m25s to ~1m. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index 36c4f92e061748..64ba95f3e0dda0 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -56,16 +56,18 @@ test_expect_success 'setup' ' : add two extra commits to rebase && test_commit -C files_subtree master4 && - test_commit files_subtree/master5 + test_commit files_subtree/master5 && + + git checkout -b to-rebase && + git filter-branch --prune-empty -f --subdirectory-filter files_subtree && + git commit -m "Empty commit" --allow-empty ' # FAILURE: Does not preserve master4. test_expect_failure REBASE_P \ 'Rebase -Xsubtree --preserve-merges --onto commit 4' ' reset_rebase && - git checkout -b rebase-preserve-merges-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-preserve-merges-4 to-rebase && git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && verbose test "$(commit_message HEAD~)" = "files_subtree/master4" ' @@ -74,9 +76,7 @@ test_expect_failure REBASE_P \ test_expect_failure REBASE_P \ 'Rebase -Xsubtree --preserve-merges --onto commit 5' ' reset_rebase && - git checkout -b rebase-preserve-merges-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-preserve-merges-5 to-rebase && git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && verbose test "$(commit_message HEAD)" = "files_subtree/master5" ' @@ -85,9 +85,7 @@ test_expect_failure REBASE_P \ test_expect_failure REBASE_P \ 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' ' reset_rebase && - git checkout -b rebase-keep-empty-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-keep-empty-4 to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" ' @@ -96,9 +94,7 @@ test_expect_failure REBASE_P \ test_expect_failure REBASE_P \ 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' ' reset_rebase && - git checkout -b rebase-keep-empty-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-keep-empty-5 to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" ' @@ -107,9 +103,7 @@ test_expect_failure REBASE_P \ test_expect_failure REBASE_P \ 'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' ' reset_rebase && - git checkout -b rebase-keep-empty-empty master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-keep-empty-empty to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && verbose test "$(commit_message HEAD)" = "Empty commit" ' @@ -117,9 +111,7 @@ test_expect_failure REBASE_P \ # FAILURE: fatal: Could not parse object test_expect_failure 'Rebase -Xsubtree --onto commit 4' ' reset_rebase && - git checkout -b rebase-onto-4 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-onto-4 to-rebase && git rebase -Xsubtree=files_subtree --onto files-master master && verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" ' @@ -127,18 +119,14 @@ test_expect_failure 'Rebase -Xsubtree --onto commit 4' ' # FAILURE: fatal: Could not parse object test_expect_failure 'Rebase -Xsubtree --onto commit 5' ' reset_rebase && - git checkout -b rebase-onto-5 master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-onto-5 to-rebase && git rebase -Xsubtree=files_subtree --onto files-master master && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" ' # FAILURE: fatal: Could not parse object test_expect_failure 'Rebase -Xsubtree --onto empty commit' ' reset_rebase && - git checkout -b rebase-onto-empty master && - git filter-branch --prune-empty -f --subdirectory-filter files_subtree && - git commit -m "Empty commit" --allow-empty && + git checkout -b rebase-onto-empty to-rebase && git rebase -Xsubtree=files_subtree --onto files-master master && verbose test "$(commit_message HEAD)" = "Empty commit" ' From 46ef0053650d9bcc7c3218cfbf0cb729307c03f8 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 21:56:56 +0200 Subject: [PATCH 09/16] t3427: condense the unnecessarily repetitive test cases into three Previously, this test script performed essentially three rebases and verified breakages by testing the post-rebase commits' messages. To do so, the rebases were performed multiple times, though, once per commit message to test. This wastes electricity (and CO2) and time. Let's condense the test cases to the essential number: the number of different rebases to validate. On Windows, where the scripted nature of the `--preserve-merges` backend hurts performance rather badly, this reduces the overall runtime in this developer's setup from ~1m to ~28s while still performing the exact same testing as before. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 61 +++++++-------------------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index 64ba95f3e0dda0..b21925d279aaad 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -64,70 +64,31 @@ test_expect_success 'setup' ' ' # FAILURE: Does not preserve master4. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --preserve-merges --onto commit 4' ' +test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' ' reset_rebase && - git checkout -b rebase-preserve-merges-4 to-rebase && - git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master4" -' - -# FAILURE: Does not preserve master5. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --preserve-merges --onto commit 5' ' - reset_rebase && - git checkout -b rebase-preserve-merges-5 to-rebase && + git checkout -b rebase-preserve-merges to-rebase && git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && + verbose test "$(commit_message HEAD~)" = "files_subtree/master4" && verbose test "$(commit_message HEAD)" = "files_subtree/master5" ' # FAILURE: Does not preserve master4. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' ' +test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' ' reset_rebase && - git checkout -b rebase-keep-empty-4 to-rebase && - git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" -' - -# FAILURE: Does not preserve master5. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' ' - reset_rebase && - git checkout -b rebase-keep-empty-5 to-rebase && - git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master5" -' - -# FAILURE: Does not preserve Empty. -test_expect_failure REBASE_P \ - 'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' ' - reset_rebase && - git checkout -b rebase-keep-empty-empty to-rebase && + git checkout -b rebase-keep-empty to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && + verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' # FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto commit 4' ' - reset_rebase && - git checkout -b rebase-onto-4 to-rebase && - git rebase -Xsubtree=files_subtree --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" -' - -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto commit 5' ' - reset_rebase && - git checkout -b rebase-onto-5 to-rebase && - git rebase -Xsubtree=files_subtree --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master5" -' -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto empty commit' ' +test_expect_failure 'Rebase -Xsubtree --onto commit' ' reset_rebase && - git checkout -b rebase-onto-empty to-rebase && + git checkout -b rebase-onto to-rebase && git rebase -Xsubtree=files_subtree --onto files-master master && + verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' From 8afec74cfc9907fff7379cc2d7b664deac6f93aa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 22:03:29 +0200 Subject: [PATCH 10/16] t3427: fix erroneous assumption Apart from the `setup` test case, `t3427-rebase-subtree.sh` is made up exclusively of demonstrations of breakages. The tricky thing about such demonstrations is that they are often buggy themselves. In this instance, somewhere over the course of the six iterations of the patch that eventually made it into Git's `master` as 5f35900849e (contrib/subtree: Add a test for subtree rebase that loses commits, 2016-06-28), the commit message "files_subtree/master4" was changed to just "master4", but the test cases still expected the old commit message. Let's fix this, at long last. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index b21925d279aaad..94cc532e108798 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -68,7 +68,7 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' reset_rebase && git checkout -b rebase-preserve-merges to-rebase && git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~)" = "files_subtree/master4" && + verbose test "$(commit_message HEAD~)" = "master4" && verbose test "$(commit_message HEAD)" = "files_subtree/master5" ' @@ -77,7 +77,7 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges -- reset_rebase && git checkout -b rebase-keep-empty to-rebase && git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" && + verbose test "$(commit_message HEAD~2)" = "master4" && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' @@ -87,7 +87,7 @@ test_expect_failure 'Rebase -Xsubtree --onto commit' ' reset_rebase && git checkout -b rebase-onto to-rebase && git rebase -Xsubtree=files_subtree --onto files-master master && - verbose test "$(commit_message HEAD~2)" = "files_subtree/master4" && + verbose test "$(commit_message HEAD~2)" = "master4" && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" ' From d73b0a05d90237253d0793580fe285df3aba689a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 22:11:03 +0200 Subject: [PATCH 11/16] t3427: accommodate for the `rebase --merge` backend having been replaced Since 68aa495b590 (rebase: implement --merge via the interactive machinery, 2018-12-11), the job of the old `--merge` backend is now performed by the `--interactive` backend, too. One consequence is that empty commits are no longer rebased by default. Meaning that the test case that calls `git rebase -Xsubtree` (which used to be handled by the `--merge` backend) now needs to ask explicitly for the empty commit to be rebased. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index 94cc532e108798..a734716ea3e377 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -83,10 +83,10 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges -- ' # FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --onto commit' ' +test_expect_failure 'Rebase -Xsubtree --keep-empty --onto commit' ' reset_rebase && git checkout -b rebase-onto to-rebase && - git rebase -Xsubtree=files_subtree --onto files-master master && + git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master && verbose test "$(commit_message HEAD~2)" = "master4" && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" From 57c63309bfaf1198fbf70c9dd992f36d9323dfcf Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 22:21:29 +0200 Subject: [PATCH 12/16] t3427: fix another incorrect assumption The test case that concerns `git rebase -Xsubtree` (with the default rebase backend, not with `--preserve-merges`) starts out with a pre-rebase commit history that begins with a commit that introduces three files: master1.t, master2.t and master3.t. This commit was generated by passing a subtree merge commit through `git filter-branch --subdirectory-filter`, so it looks as if this commit really introduces all those files. The commit history onto which this commit is then rebased, however, introduced those files in individual commits. For that reason, the rebase will fail, it _must_ fail, because the first `pick` results in no changes to be committed. Let's fix the test case to expect exactly this situation. With this change, we can mark the original bug that this test case tried to demonstrate as fixed. Signed-off-by: Johannes Schindelin --- t/t3427-rebase-subtree.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index a734716ea3e377..7a37235768574d 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -82,11 +82,12 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges -- verbose test "$(commit_message HEAD)" = "Empty commit" ' -# FAILURE: fatal: Could not parse object -test_expect_failure 'Rebase -Xsubtree --keep-empty --onto commit' ' +test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' ' reset_rebase && git checkout -b rebase-onto to-rebase && - git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master && + test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master && + : first pick results in no changes && + git rebase --continue && verbose test "$(commit_message HEAD~2)" = "master4" && verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && verbose test "$(commit_message HEAD)" = "Empty commit" From 75b1395dae5f2fac28d3b200b12833821b7b1862 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 14 May 2019 12:56:08 +0200 Subject: [PATCH 13/16] rebase -r: support merge strategies other than `recursive` We already support merge strategies in the sequencer, but only for `pick` commands. With this commit, we now also support them in `merge` commands. The approach is simple: if any merge strategy option is specified, or if any merge strategy other than `recursive` is specified, we simply spawn the `git merge` command. Otherwise, we handle the merge in-process just as before. Signed-off-by: Johannes Schindelin --- Documentation/git-rebase.txt | 2 -- builtin/rebase.c | 9 --------- sequencer.c | 14 ++++++++++++-- t/t3422-rebase-incompatible-options.sh | 10 ---------- t/t3430-rebase-merges.sh | 21 +++++++++++++++++++++ 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 5e4e9276479c94..bc620c44e93916 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -543,8 +543,6 @@ In addition, the following pairs of options are incompatible: * --preserve-merges and --interactive * --preserve-merges and --signoff * --preserve-merges and --rebase-merges - * --rebase-merges and --strategy - * --rebase-merges and --strategy-option BEHAVIORAL DIFFERENCES ----------------------- diff --git a/builtin/rebase.c b/builtin/rebase.c index 74a60e8c83f7b2..625f50c63779af 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1811,15 +1811,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) "'--reschedule-failed-exec'")); } - if (options.rebase_merges) { - if (strategy_options.nr) - die(_("cannot combine '--rebase-merges' with " - "'--strategy-option'")); - if (options.strategy) - die(_("cannot combine '--rebase-merges' with " - "'--strategy'")); - } - if (!options.root) { if (argc < 1) { struct branch *branch; diff --git a/sequencer.c b/sequencer.c index 334de145421c1f..d228448cd8c54a 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3256,6 +3256,9 @@ static int do_merge(struct repository *r, struct commit *head_commit, *merge_commit, *i; struct commit_list *bases, *j, *reversed = NULL; struct commit_list *to_merge = NULL, **tail = &to_merge; + const char *strategy = !opts->xopts_nr && + (!opts->strategy || !strcmp(opts->strategy, "recursive")) ? + NULL : opts->strategy; struct merge_options o; int merge_arg_len, oneline_offset, can_fast_forward, ret, k; static struct lock_file lock; @@ -3404,7 +3407,7 @@ static int do_merge(struct repository *r, goto leave_merge; } - if (to_merge->next) { + if (strategy || to_merge->next) { /* Octopus merge */ struct child_process cmd = CHILD_PROCESS_INIT; @@ -3418,7 +3421,14 @@ static int do_merge(struct repository *r, cmd.git_cmd = 1; argv_array_push(&cmd.args, "merge"); argv_array_push(&cmd.args, "-s"); - argv_array_push(&cmd.args, "octopus"); + if (!strategy) + argv_array_push(&cmd.args, "octopus"); + else { + argv_array_push(&cmd.args, strategy); + for (k = 0; k < opts->xopts_nr; k++) + argv_array_pushf(&cmd.args, + "-X%s", opts->xopts[k]); + } argv_array_push(&cmd.args, "--no-edit"); argv_array_push(&cmd.args, "--no-ff"); argv_array_push(&cmd.args, "--no-log"); diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh index a5868ea152f941..50e7960702da4f 100755 --- a/t/t3422-rebase-incompatible-options.sh +++ b/t/t3422-rebase-incompatible-options.sh @@ -76,14 +76,4 @@ test_expect_success REBASE_P \ test_must_fail git rebase --preserve-merges --rebase-merges A ' -test_expect_success '--rebase-merges incompatible with --strategy' ' - git checkout B^0 && - test_must_fail git rebase --rebase-merges -s resolve A -' - -test_expect_success '--rebase-merges incompatible with --strategy-option' ' - git checkout B^0 && - test_must_fail git rebase --rebase-merges -Xignore-space-change A -' - test_done diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 42ba5b9f0981b2..8ea6ff3548444c 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -412,4 +412,25 @@ test_expect_success '--continue after resolving conflicts after a merge' ' test_path_is_missing .git/MERGE_HEAD ' +test_expect_success '--rebase-merges with strategies' ' + git checkout -b with-a-strategy F && + test_tick && + git merge -m "Merge conflicting-G" conflicting-G && + + : first, test with a merge strategy option && + git rebase -ir -Xtheirs G && + echo conflicting-G >expect && + test_cmp expect G.t && + + : now, try with a merge strategy other than recursive && + git reset --hard @{1} && + write_script git-merge-override <<-\EOF && + echo overridden$1 >>G.t + git add G.t + EOF + PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G && + test_write_lines G overridden--xopt >expect && + test_cmp expect G.t +' + test_done From 8f74bfbc534cc7a707462622ddf387c67c6de222 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 16:43:33 +0200 Subject: [PATCH 14/16] t/lib-rebase: prepare for testing `git rebase --rebase-merges` The format of the todo list is quite a bit different in the `--rebase-merges` mode; Let's prepare the fake editor to handle those todo lists properly, too. The original idea was that we keep the original command unless overridden, and because the original todo lists only had `pick` lines anyway, we could be sloppy and "override" the command by the same command (i.e. use the sed replacement pattern "pick" instead of "&"). This actually would not have worked with `fixup` and `squash` commands, but it would appear that we never tried to use the fake editor with `--autosquash`. However, in the next commit we want to use the fake editor in conjunction with `--rebase-merges`, so let's use the correct sed replacement pattern. Technically, it is not necessary to take care of the `fakesha` thing (where we reuse the sed replacement pattern to craft a new todo command), at least for now, as the only user of that thing overrides the `action` anyway. Nevertheless, for completeness' sake, we do take care of it. Helped-by: brian m. carlson Signed-off-by: Johannes Schindelin --- t/lib-rebase.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 7ea30e50068be8..6d87961e419e10 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -44,10 +44,10 @@ set_fake_editor () { rm -f "$1" echo 'rebase -i script before editing:' cat "$1".tmp - action=pick + action=\& for line in $FAKE_LINES; do case $line in - pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d) + pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m) action="$line";; exec_*|x_*|break|b) echo "$line" | sed 's/_/ /g' >> "$1";; @@ -58,11 +58,12 @@ set_fake_editor () { bad) action="badcmd";; fakesha) + test \& != "$action" || action=pick echo "$action XXXXXXX False commit" >> "$1" action=pick;; *) - sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" - action=pick;; + sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1" + action=\&;; esac done echo 'rebase -i script after editing:' From 0c938d02b303b995f75cd7dbaa77eac01a06dc4d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 16:01:27 +0200 Subject: [PATCH 15/16] t3418: test `rebase -r` with merge strategies There is a test case in this script that verifies that `git rebase --preserve-merges` works all right with non-default merge strategies or non-default merge strategy options. Now that `git rebase --rebase-merges` learned about merge strategies, let's copy-edit this test case to verify that that works as intended, too. Signed-off-by: Johannes Schindelin --- t/t3418-rebase-continue.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index bdaa511bb0ae10..fbf9addfd137b9 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -120,6 +120,20 @@ test_expect_success REBASE_P 'rebase passes merge strategy options correctly' ' git rebase --continue ' +test_expect_success 'rebase -r passes merge strategy options correctly' ' + rm -fr .git/rebase-* && + git reset --hard commit-new-file-F3-on-topic-branch && + test_commit merge-theirs && + git reset --hard HEAD^ && + test_commit some-other-commit && + test_tick && + git merge --no-ff merge-theirs && + FAKE_LINES="1 3 edit 4 5 7 8 9" git rebase -i -f -r -m \ + -s recursive --strategy-option=theirs HEAD~2 && + test_commit force-change-ours && + git rebase --continue +' + test_expect_success '--skip after failed fixup cleans commit message' ' test_when_finished "test_might_fail git rebase --abort" && git checkout -b with-conflicting-fixup && From be62d267cb688ee1ac3c308e7a2593f32bf8f49f Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 24 Jul 2019 22:32:48 +0200 Subject: [PATCH 16/16] rebase -r: do not (re-)generate root commits with `--root` *and* `--onto` When rebasing a complete commit history onto a given commit, it is pretty obvious that the root commits should be rebased on top of said given commit. To test this, let's kill two birds with one stone and add a test case to t3427-rebase-subtree.sh that not only demonstrates that this works, but also that `git rebase -r` works with merge strategies now. Signed-off-by: Johannes Schindelin --- builtin/rebase.c | 7 +++++-- sequencer.c | 4 +++- sequencer.h | 6 ++++++ t/t3427-rebase-subtree.sh | 11 +++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 625f50c63779af..ee2bc8b032a2fa 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -62,7 +62,7 @@ struct rebase_options { const char *onto_name; const char *revisions; const char *switch_to; - int root; + int root, root_with_onto; struct object_id *squash_onto; struct commit *restrict_revision; int dont_finish_rebase; @@ -374,6 +374,7 @@ static int run_rebase_interactive(struct rebase_options *opts, flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0; flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; + flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0; flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; switch (command) { @@ -1841,7 +1842,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.squash_onto = &squash_onto; options.onto_name = squash_onto_name = xstrdup(oid_to_hex(&squash_onto)); - } + } else + options.root_with_onto = 1; + options.upstream_name = NULL; options.upstream = NULL; if (argc > 1) diff --git a/sequencer.c b/sequencer.c index d228448cd8c54a..ca119c84e577c9 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4440,6 +4440,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, { int keep_empty = flags & TODO_LIST_KEEP_EMPTY; int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS; + int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO; struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT; struct strbuf label = STRBUF_INIT; struct commit_list *commits = NULL, **tail = &commits, *iter; @@ -4606,7 +4607,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, if (!commit) strbuf_addf(out, "%s %s\n", cmd_reset, - rebase_cousins ? "onto" : "[new root]"); + rebase_cousins || root_with_onto ? + "onto" : "[new root]"); else { const char *to = NULL; diff --git a/sequencer.h b/sequencer.h index 0c494b83d43e2c..d506081d3c8656 100644 --- a/sequencer.h +++ b/sequencer.h @@ -142,6 +142,12 @@ int sequencer_remove_state(struct replay_opts *opts); */ #define TODO_LIST_REBASE_COUSINS (1U << 4) #define TODO_LIST_APPEND_TODO_HELP (1U << 5) +/* + * When generating a script that rebases merges with `--root` *and* with + * `--onto`, we do not want to re-generate the root commits. + */ +#define TODO_LIST_ROOT_WITH_ONTO (1U << 6) + int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, const char **argv, unsigned flags); diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh index 7a37235768574d..39e348de163b72 100755 --- a/t/t3427-rebase-subtree.sh +++ b/t/t3427-rebase-subtree.sh @@ -93,4 +93,15 @@ test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' ' verbose test "$(commit_message HEAD)" = "Empty commit" ' +test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' ' + reset_rebase && + git checkout -b rebase-merges-onto to-rebase && + test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root && + : first pick results in no changes && + git rebase --continue && + verbose test "$(commit_message HEAD~2)" = "master4" && + verbose test "$(commit_message HEAD~)" = "files_subtree/master5" && + verbose test "$(commit_message HEAD)" = "Empty commit" +' + test_done