Merge branch 'jc/fix-pull-ff-only-when-already-up-to-date'

"git pull --ff-only" and "git pull --rebase --ff-only" should make
it a no-op to attempt pulling from a remote that is behind us, but
instead the command errored out by saying it was impossible to
fast-forward, which may technically be true, but not a useful thing
to diagnose as an error.  This has been corrected.

* jc/fix-pull-ff-only-when-already-up-to-date:
  pull: --ff-only should make it a noop when already-up-to-date
This commit is contained in:
Junio C Hamano 2021-11-10 15:01:19 -08:00
commit 7c7cf62c48
2 changed files with 43 additions and 2 deletions

View File

@ -937,6 +937,33 @@ static int get_can_ff(struct object_id *orig_head,
return ret; return ret;
} }
/*
* Is orig_head a descendant of _all_ merge_heads?
* Unfortunately is_descendant_of() cannot be used as it asks
* if orig_head is a descendant of at least one of them.
*/
static int already_up_to_date(struct object_id *orig_head,
struct oid_array *merge_heads)
{
int i;
struct commit *ours;
ours = lookup_commit_reference(the_repository, orig_head);
for (i = 0; i < merge_heads->nr; i++) {
struct commit_list *list = NULL;
struct commit *theirs;
int ok;
theirs = lookup_commit_reference(the_repository, &merge_heads->oid[i]);
commit_list_insert(theirs, &list);
ok = repo_is_descendant_of(the_repository, ours, list);
free_commit_list(list);
if (!ok)
return 0;
}
return 1;
}
static void show_advice_pull_non_ff(void) static void show_advice_pull_non_ff(void)
{ {
advise(_("You have divergent branches and need to specify how to reconcile them.\n" advise(_("You have divergent branches and need to specify how to reconcile them.\n"
@ -1078,7 +1105,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
/* ff-only takes precedence over rebase */ /* ff-only takes precedence over rebase */
if (opt_ff && !strcmp(opt_ff, "--ff-only")) { if (opt_ff && !strcmp(opt_ff, "--ff-only")) {
if (!can_ff) if (!can_ff && !already_up_to_date(&orig_head, &merge_heads))
die_ff_impossible(); die_ff_impossible();
opt_rebase = REBASE_FALSE; opt_rebase = REBASE_FALSE;
} }

View File

@ -2,7 +2,7 @@
test_description='git merge test_description='git merge
Testing pull.* configuration parsing.' Testing pull.* configuration parsing and other things.'
. ./test-lib.sh . ./test-lib.sh
@ -387,6 +387,20 @@ test_expect_success 'pull prevents non-fast-forward with "only" in pull.ff' '
test_must_fail git pull . c3 test_must_fail git pull . c3
' '
test_expect_success 'already-up-to-date pull succeeds with "only" in pull.ff' '
git reset --hard c1 &&
test_config pull.ff only &&
git pull . c0 &&
test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
'
test_expect_success 'already-up-to-date pull/rebase succeeds with "only" in pull.ff' '
git reset --hard c1 &&
test_config pull.ff only &&
git -c pull.rebase=true pull . c0 &&
test "$(git rev-parse HEAD)" = "$(git rev-parse c1)"
'
test_expect_success 'merge c1 with c2 (ours in pull.twohead)' ' test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
git reset --hard c1 && git reset --hard c1 &&
git config pull.twohead ours && git config pull.twohead ours &&