]> git.ipfire.org Git - thirdparty/git.git/commitdiff
rebase --keep-base: imply --reapply-cherry-picks
authorPhillip Wood <phillip.wood@dunelm.org.uk>
Mon, 17 Oct 2022 13:17:44 +0000 (13:17 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2022 18:53:03 +0000 (11:53 -0700)
As --keep-base does not rebase the branch it is confusing if it
removes commits that have been cherry-picked to the upstream branch.
As --reapply-cherry-picks is not supported by the "apply" backend this
commit ensures that cherry-picks are reapplied by forcing the upstream
commit to match the onto commit unless --no-reapply-cherry-picks is
given.

Reported-by: Philippe Blain <levraiphilippeblain@gmail.com>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-rebase.txt
builtin/rebase.c
t/t3416-rebase-onto-threedots.sh

index a872ab0fbd1a9a615dd8d370bb5cdd74f7bab918..092a3550718e2861e7626a396e20907ca19c73ec 100644 (file)
@@ -218,12 +218,14 @@ leave out at most one of A and B, in which case it defaults to HEAD.
        merge base of `<upstream>` and `<branch>`. Running
        `git rebase --keep-base <upstream> <branch>` is equivalent to
        running
-       `git rebase --onto <upstream>...<branch> <upstream> <branch>`.
+       `git rebase --reapply-cherry-picks --onto <upstream>...<branch> <upstream> <branch>`.
 +
 This option is useful in the case where one is developing a feature on
 top of an upstream branch. While the feature is being worked on, the
 upstream branch may advance and it may not be the best idea to keep
-rebasing on top of the upstream but to keep the base commit as-is.
+rebasing on top of the upstream but to keep the base commit as-is. As
+the base commit is unchanged this option implies `--reapply-cherry-picks`
+to avoid losing commits.
 +
 Although both this option and `--fork-point` find the merge base between
 `<upstream>` and `<branch>`, this option uses the merge base as the _starting
@@ -278,7 +280,8 @@ See also INCOMPATIBLE OPTIONS below.
 Note that commits which start empty are kept (unless `--no-keep-empty`
 is specified), and commits which are clean cherry-picks (as determined
 by `git log --cherry-mark ...`) are detected and dropped as a
-preliminary step (unless `--reapply-cherry-picks` is passed).
+preliminary step (unless `--reapply-cherry-picks` or `--keep-base` is
+passed).
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -311,13 +314,16 @@ See also INCOMPATIBLE OPTIONS below.
        upstream changes, the behavior towards them is controlled by
        the `--empty` flag.)
 +
-By default (or if `--no-reapply-cherry-picks` is given), these commits
-will be automatically dropped.  Because this necessitates reading all
-upstream commits, this can be expensive in repos with a large number
-of upstream commits that need to be read.  When using the 'merge'
-backend, warnings will be issued for each dropped commit (unless
-`--quiet` is given). Advice will also be issued unless
-`advice.skippedCherryPicks` is set to false (see linkgit:git-config[1]).
+
+In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is
+given), these commits will be automatically dropped.  Because this
+necessitates reading all upstream commits, this can be expensive in
+repositories with a large number of upstream commits that need to be
+read. When using the 'merge' backend, warnings will be issued for each
+dropped commit (unless `--quiet` is given). Advice will also be issued
+unless `advice.skippedCherryPicks` is set to false (see
+linkgit:git-config[1]).
+
 +
 `--reapply-cherry-picks` allows rebase to forgo reading all upstream
 commits, potentially improving performance.
index 9ee3ab62407c29c41dfbd32e45e89599a964a774..0287f3e8171745fc6abbb8bf587ea4a92e8db98a 100644 (file)
@@ -1171,6 +1171,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
 
+       options.reapply_cherry_picks = -1;
        options.allow_empty_message = 1;
        git_config(rebase_config, &options);
        /* options.gpg_sign_opt will be either "-S" or NULL */
@@ -1230,6 +1231,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                if (options.root)
                        die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
        }
+       /*
+        * --keep-base defaults to --reapply-cherry-picks to avoid losing
+        * commits when using this option.
+        */
+       if (options.reapply_cherry_picks < 0)
+               options.reapply_cherry_picks = keep_base;
 
        if (options.root && options.fork_point > 0)
                die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
@@ -1406,7 +1413,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.empty != EMPTY_UNSPECIFIED)
                imply_merge(&options, "--empty");
 
-       if (options.reapply_cherry_picks)
+       /*
+        * --keep-base implements --reapply-cherry-picks by altering upstream so
+        * it works with both backends.
+        */
+       if (options.reapply_cherry_picks && !keep_base)
                imply_merge(&options, "--reapply-cherry-picks");
 
        if (gpg_sign)
@@ -1672,6 +1683,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                fill_branch_base(&options, &branch_base);
        }
 
+       if (keep_base && options.reapply_cherry_picks)
+               options.upstream = options.onto;
+
        if (options.fork_point > 0)
                options.restrict_revision =
                        get_fork_point(options.upstream_name, options.orig_head);
index 01eb9513d6c2d7c142e71bfb54d4d9454be180e7..ea501f2b42b8cc43394192d11051992605bfb619 100755 (executable)
@@ -199,6 +199,27 @@ test_expect_success 'rebase --keep-base requires a single merge base' '
        grep "need exactly one merge base with branch" err
 '
 
+test_expect_success 'rebase --keep-base keeps cherry picks' '
+       git checkout -f -B main E &&
+       git cherry-pick F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=2 git rebase -i --keep-base HEAD G
+       ) &&
+       test_cmp_rev HEAD G
+'
+
+test_expect_success 'rebase --keep-base --no-reapply-cherry-picks' '
+       git checkout -f -B main E &&
+       git cherry-pick F &&
+       (
+               set_fake_editor &&
+               EXPECT_COUNT=1 git rebase -i --keep-base \
+                                       --no-reapply-cherry-picks HEAD G
+       ) &&
+       test_cmp_rev HEAD^ C
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
        test_editor_unchanged