From: Elijah Newren via GitGitGadget Date: Thu, 15 Jul 2021 02:40:47 GMT Subject: [PATCH 3/5] pull: handle conflicting rebase/merge options via last option wins Message-ID: <3c07ce978caa832b08c6bef1c48c061e41a6fd0b.1626316849.git.gitgitgadget@gmail.com> In-Reply-To: From: Elijah Newren The --rebase[=...] flags and the various ff flags are incompatible, except that --no-rebase (or --rebase=false) work with any of the ff flags, and --ff works with any of the rebase flags. Both sets of these flags could also be passed via configuration options, namely pull.rebase and pull.ff. As with elsewhere in git: * Make the last flag specified win * Treat command line flags as coming after any configuration options * Do not imply an order between different configuration options; if they conflict, just report an error. Signed-off-by: Elijah Newren --- Documentation/config/pull.txt | 3 +- Documentation/git-pull.txt | 3 ++ builtin/pull.c | 12 +++++++ t/t7601-merge-pull-config.sh | 67 +++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/Documentation/config/pull.txt b/Documentation/config/pull.txt index 54048306095..e70ed99e408 100644 --- a/Documentation/config/pull.txt +++ b/Documentation/config/pull.txt @@ -7,12 +7,13 @@ pull.ff:: line). When set to `only`, only such fast-forward merges are allowed (equivalent to giving the `--ff-only` option from the command line). This setting overrides `merge.ff` when pulling. + Incompatible with pull.rebase. pull.rebase:: When true, rebase branches on top of the fetched branch, instead of merging the default branch from the default remote when "git pull" is run. See "branch..rebase" for setting this on a - per-branch basis. + per-branch basis. Incompatible with pull.ff. + When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase' so that the local merge commits are included in the rebase (see diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 5c3fb67c014..03e8694e146 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -121,6 +121,9 @@ When false, merge the current branch into the upstream branch. + When `interactive`, enable the interactive mode of rebase. + +Note that these flags are incompatible with --no-ff and --ff-only; if +such incompatible flags are given, the last one will take precedence. ++ See `pull.rebase`, `branch..rebase` and `branch.autoSetupRebase` in linkgit:git-config[1] if you want to make `git pull` always use `--rebase` instead of merging. diff --git a/builtin/pull.c b/builtin/pull.c index d99719403d0..b355fd38794 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -109,6 +109,11 @@ static int parse_opt_rebase(const struct option *opt, const char *arg, int unset *value = parse_config_rebase("--rebase", arg, 0); else *value = unset ? REBASE_FALSE : REBASE_TRUE; + + /* --rebase overrides earlier --ff-only and --no-ff */ + if (*value != REBASE_FALSE) + opt_ff = "--ff"; + return *value == REBASE_INVALID ? -1 : 0; } @@ -119,6 +124,10 @@ static int parse_opt_ff(const struct option *opt, const char *arg, int unset) else opt_ff = xstrfmt("--%s", opt->long_name); + /* --ff-only and --no-ff override earlier --rebase */ + if (strcmp(opt_ff, "--ff")) + opt_rebase = REBASE_FALSE; + return 0; } @@ -984,6 +993,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix) if (opt_rebase < 0) opt_rebase = config_get_rebase(&rebase_unspecified); + if (opt_rebase != REBASE_FALSE && opt_ff && strcmp(opt_ff, "--ff")) + die(_("pull.rebase and pull.ff are incompatible; please unset one")); + if (read_cache_unmerged()) die_resolve_conflict("pull"); diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh index 52e8ccc933a..73a0dbdf25a 100755 --- a/t/t7601-merge-pull-config.sh +++ b/t/t7601-merge-pull-config.sh @@ -143,6 +143,73 @@ test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' test_i18ngrep ! "Pulling without specifying how to reconcile" err ' +test_does_rebase() { + git reset --hard c2 && + git "$@" . c1 && + # Check that we actually did a rebase + git rev-list --count HEAD >actual && + git rev-list --merges --count HEAD >>actual && + test_write_lines 3 0 >expect && + test_cmp expect actual && + rm actual expect +} + +test_does_merge() { + git reset --hard c2 && + git "$@" . c1 && + # Check that we actually did a merge + git rev-list --count HEAD >actual && + git rev-list --merges --count HEAD >>actual && + test_write_lines 4 1 >expect && + test_cmp expect actual && + rm actual expect +} + +test_attempts_fast_forward() { + git reset --hard c2 && + test_must_fail git "$@" . c1 2>err && + test_i18ngrep "Not possible to fast-forward, aborting" err +} + +test_expect_success 'conflicting options: --ff-only --rebase' ' + test_does_rebase pull --ff-only --rebase +' + +test_expect_success 'conflicting options: --no-ff --rebase' ' + test_does_rebase pull --no-ff --rebase +' + +test_expect_success 'conflicting options: -c pull.ff=false --rebase' ' + test_does_rebase -c pull.ff=false pull --rebase +' + +test_expect_success 'conflicting options: -c pull.ff=only --rebase' ' + test_does_rebase -c pull.ff=only pull --rebase +' + +test_expect_success 'conflicting options: --rebase --ff-only' ' + test_attempts_fast_forward pull --rebase --ff-only +' + +test_expect_success 'conflicting options: --rebase --no-ff' ' + test_does_merge pull --rebase --no-ff +' + +test_expect_success 'conflicting options: -c pull.rebase=true --no-ff' ' + test_does_merge -c pull.rebase=true pull --no-ff +' + +test_expect_success 'conflicting options: -c pull.rebase=true --ff-only' ' + test_attempts_fast_forward -c pull.rebase=true pull --ff-only +' + +test_expect_success 'report conflicting configuration' ' + git reset --hard c2 && + test_must_fail git -c pull.ff=false -c pull.rebase=true pull . c1 2>err && + test_i18ngrep "pull.rebase and pull.ff are incompatible; please unset one" err + +' + test_expect_success 'merge c1 with c2' ' git reset --hard c1 && test -f c0.c && -- gitgitgadget