summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/pull.c70
-rwxr-xr-xt/t7601-merge-pull-config.sh66
2 files changed, 104 insertions, 32 deletions
diff --git a/builtin/pull.c b/builtin/pull.c
index aa56ebcdd0..e8927fc2ff 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -324,7 +324,7 @@ static const char *config_get_ff(void)
* looks for the value of "pull.rebase". If both configuration keys do not
* exist, returns REBASE_FALSE.
*/
-static enum rebase_type config_get_rebase(void)
+static enum rebase_type config_get_rebase(int *rebase_unspecified)
{
struct branch *curr_branch = branch_get("HEAD");
const char *value;
@@ -344,20 +344,7 @@ static enum rebase_type config_get_rebase(void)
if (!git_config_get_value("pull.rebase", &value))
return parse_config_rebase("pull.rebase", value, 1);
- if (opt_verbosity >= 0 && !opt_ff) {
- advise(_("Pulling without specifying how to reconcile divergent branches is\n"
- "discouraged. You can squelch this message by running one of the following\n"
- "commands sometime before your next pull:\n"
- "\n"
- " git config pull.rebase false # merge (the default strategy)\n"
- " git config pull.rebase true # rebase\n"
- " git config pull.ff only # fast-forward only\n"
- "\n"
- "You can replace \"git config\" with \"git config --global\" to set a default\n"
- "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
- "or --ff-only on the command line to override the configured default per\n"
- "invocation.\n"));
- }
+ *rebase_unspecified = 1;
return REBASE_FALSE;
}
@@ -924,6 +911,36 @@ static int run_rebase(const struct object_id *newbase,
return ret;
}
+static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_head)
+{
+ int ret;
+ struct commit_list *list = NULL;
+ struct commit *merge_head, *head;
+
+ head = lookup_commit_reference(the_repository, orig_head);
+ commit_list_insert(head, &list);
+ merge_head = lookup_commit_reference(the_repository, orig_merge_head);
+ ret = repo_is_descendant_of(the_repository, merge_head, list);
+ free_commit_list(list);
+ return ret;
+}
+
+static void show_advice_pull_non_ff(void)
+{
+ advise(_("Pulling without specifying how to reconcile divergent branches is\n"
+ "discouraged. You can squelch this message by running one of the following\n"
+ "commands sometime before your next pull:\n"
+ "\n"
+ " git config pull.rebase false # merge (the default strategy)\n"
+ " git config pull.rebase true # rebase\n"
+ " git config pull.ff only # fast-forward only\n"
+ "\n"
+ "You can replace \"git config\" with \"git config --global\" to set a default\n"
+ "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
+ "or --ff-only on the command line to override the configured default per\n"
+ "invocation.\n"));
+}
+
int cmd_pull(int argc, const char **argv, const char *prefix)
{
const char *repo, **refspecs;
@@ -931,6 +948,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
struct object_id orig_head, curr_head;
struct object_id rebase_fork_point;
int autostash;
+ int rebase_unspecified = 0;
+ int can_ff;
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
@@ -952,7 +971,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
opt_ff = xstrdup_or_null(config_get_ff());
if (opt_rebase < 0)
- opt_rebase = config_get_rebase();
+ opt_rebase = config_get_rebase(&rebase_unspecified);
if (read_cache_unmerged())
die_resolve_conflict("pull");
@@ -1026,6 +1045,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (opt_rebase && merge_heads.nr > 1)
die(_("Cannot rebase onto multiple branches."));
+ can_ff = get_can_ff(&orig_head, &merge_heads.oid[0]);
+
+ if (rebase_unspecified && !opt_ff && !can_ff) {
+ if (opt_verbosity >= 0)
+ show_advice_pull_non_ff();
+ }
+
if (opt_rebase) {
int ret = 0;
int ran_ff = 0;
@@ -1040,22 +1066,12 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
submodule_touches_in_range(the_repository, &upstream, &curr_head))
die(_("cannot rebase with locally recorded submodule modifications"));
if (!autostash) {
- struct commit_list *list = NULL;
- struct commit *merge_head, *head;
-
- head = lookup_commit_reference(the_repository,
- &orig_head);
- commit_list_insert(head, &list);
- merge_head = lookup_commit_reference(the_repository,
- &merge_heads.oid[0]);
- if (repo_is_descendant_of(the_repository,
- merge_head, list)) {
+ if (can_ff) {
/* we can fast-forward this without invoking rebase */
opt_ff = "--ff-only";
ran_ff = 1;
ret = run_merge();
}
- free_commit_list(list);
}
if (!ran_ff)
ret = run_rebase(&newbase, &upstream);
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 6774e9d86f..52e8ccc933 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -29,11 +29,8 @@ test_expect_success 'setup' '
test_expect_success 'pull.rebase not set' '
git reset --hard c0 &&
- git -c color.advice=always pull . c1 2>err &&
- test_decode_color <err >decoded &&
- test_i18ngrep "<YELLOW>hint: " decoded &&
- test_i18ngrep "Pulling without specifying how to reconcile" decoded
-
+ git pull . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
'
test_expect_success 'pull.rebase not set and pull.ff=true' '
@@ -87,6 +84,65 @@ test_expect_success 'pull.rebase not set and --ff-only given' '
test_i18ngrep ! "Pulling without specifying how to reconcile" err
'
+test_expect_success 'pull.rebase not set (not-fast-forward)' '
+ git reset --hard c2 &&
+ git -c color.advice=always pull . c1 2>err &&
+ test_decode_color <err >decoded &&
+ test_i18ngrep "<YELLOW>hint: " decoded &&
+ test_i18ngrep "Pulling without specifying how to reconcile" decoded
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
+ git reset --hard c2 &&
+ test_config pull.ff true &&
+ git pull . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
+ git reset --hard c2 &&
+ test_config pull.ff false &&
+ git pull . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
+ git reset --hard c2 &&
+ test_config pull.ff only &&
+ test_must_fail git pull . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
+ git reset --hard c2 &&
+ git pull --rebase . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
+ git reset --hard c2 &&
+ git pull --no-rebase . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
+ git reset --hard c2 &&
+ git pull --ff . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
+ git reset --hard c2 &&
+ git pull --no-ff . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
+ git reset --hard c2 &&
+ test_must_fail git pull --ff-only . c1 2>err &&
+ test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
test_expect_success 'merge c1 with c2' '
git reset --hard c1 &&
test -f c0.c &&