* "Not possible to fast-forward" when pull.ff=only and new commits on remote @ 2021-10-19 17:23 Kenneth Arnold 2021-10-19 21:22 ` Jeff King 0 siblings, 1 reply; 9+ messages in thread From: Kenneth Arnold @ 2021-10-19 17:23 UTC (permalink / raw) To: git@vger.kernel.org After upgrade to 2.33.1, the behavior of `pull.ff=only` has changed in a way that breaks some workflows, notably the default used in VSCode. Example (specific repo doesn't matter) ``` $ git clone git@github.com:kcarnold/util.git ... $ cd util $ echo "" > test $ git add test $ git commit -m "Test" $ git -c pull.ff=only pull fatal: Not possible to fast-forward, aborting. $ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) ``` Previously this pull succeeded without error, which was as expected because no merge was necessary. VS Code users have reported this problem because that editor has a "sync" option that seems to run something like `git pull && git push`. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: "Not possible to fast-forward" when pull.ff=only and new commits on remote 2021-10-19 17:23 "Not possible to fast-forward" when pull.ff=only and new commits on remote Kenneth Arnold @ 2021-10-19 21:22 ` Jeff King 2021-10-20 16:28 ` Junio C Hamano 0 siblings, 1 reply; 9+ messages in thread From: Jeff King @ 2021-10-19 21:22 UTC (permalink / raw) To: Kenneth Arnold; +Cc: Alex Henrie, git@vger.kernel.org On Tue, Oct 19, 2021 at 05:23:06PM +0000, Kenneth Arnold wrote: > After upgrade to 2.33.1, the behavior of `pull.ff=only` has changed in a way that breaks some workflows, notably the default used in VSCode. > > Example (specific repo doesn't matter) > > ``` > $ git clone git@github.com:kcarnold/util.git > ... > $ cd util > $ echo "" > test > $ git add test > $ git commit -m "Test" > $ git -c pull.ff=only pull > fatal: Not possible to fast-forward, aborting. > $ git status > On branch master > Your branch is ahead of 'origin/master' by 1 commit. > (use "git push" to publish your local commits) > ``` > > Previously this pull succeeded without error, which was as expected because no merge was necessary. Thanks for reporting, this is an interesting case. I agree that it probably ought to continue to be a noop. There is nothing to pull, and so the question of ff-versus-merge should not even enter into it. It bisects to Alex's (cc'd) 3d5fc24dae (pull: abort if --ff-only is given and fast-forwarding is impossible, 2021-07-21). I'd guess it's probably the hunk in pull.cc which checks "can_ff". The solution doesn't seem entirely obvious to me though (at least in a way that retains what that commit was trying to do). -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: "Not possible to fast-forward" when pull.ff=only and new commits on remote 2021-10-19 21:22 ` Jeff King @ 2021-10-20 16:28 ` Junio C Hamano 2021-10-20 17:09 ` Jeff King 2021-10-20 19:02 ` [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date Junio C Hamano 0 siblings, 2 replies; 9+ messages in thread From: Junio C Hamano @ 2021-10-20 16:28 UTC (permalink / raw) To: Jeff King; +Cc: Kenneth Arnold, Alex Henrie, git@vger.kernel.org Jeff King <peff@peff.net> writes: > Thanks for reporting, this is an interesting case. I agree that it > probably ought to continue to be a noop. There is nothing to pull, and > so the question of ff-versus-merge should not even enter into it. Probably something along this line? Hasn't been tested beyond compiling and passing $ git checkout master && ./git pull --ff-only -v . maint but that should be sufficient, I hope. builtin/pull.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git c/builtin/pull.c w/builtin/pull.c index ae9f5bd7cc..d925999543 100644 --- c/builtin/pull.c +++ w/builtin/pull.c @@ -931,6 +931,33 @@ static int get_can_ff(struct object_id *orig_head, return ret; } +/* + * Is orig_head is 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) { advise(_("You have divergent branches and need to specify how to reconcile them.\n" @@ -1072,7 +1099,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) /* ff-only takes precedence over rebase */ 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(); opt_rebase = REBASE_FALSE; } ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: "Not possible to fast-forward" when pull.ff=only and new commits on remote 2021-10-20 16:28 ` Junio C Hamano @ 2021-10-20 17:09 ` Jeff King 2021-10-20 19:19 ` Junio C Hamano 2021-10-20 19:02 ` [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date Junio C Hamano 1 sibling, 1 reply; 9+ messages in thread From: Jeff King @ 2021-10-20 17:09 UTC (permalink / raw) To: Junio C Hamano; +Cc: Kenneth Arnold, Alex Henrie, git@vger.kernel.org On Wed, Oct 20, 2021 at 09:28:08AM -0700, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > Thanks for reporting, this is an interesting case. I agree that it > > probably ought to continue to be a noop. There is nothing to pull, and > > so the question of ff-versus-merge should not even enter into it. > > Probably something along this line? Hasn't been tested beyond > compiling and passing > > $ git checkout master && ./git pull --ff-only -v . maint > > but that should be sufficient, I hope. Yeah, this direction makes sense to me. Just looking over the patch... > +/* > + * Is orig_head is 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); I think orig_head can be the null oid if we're on an unborn HEAD. I guess you'd want to return "1" in that case (but I could be wrong; it looks like get_can_ff() assumes it's valid, so perhaps that case is handled earlier). I'd expect that merge_heads can never be empty here, or we'd bail earlier in the command, but I didn't check (though again, get_can_ff() seems to assume there's at least one). > + 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; > + } Running a sequence of traversals like this can be slow, because we may walk over the same history again and again. But I think in the usual non-octopus cases we'd only have one entry, so we'd only be adding a single extra merge-base traversal in most cases. It does feel like this could be combined with get_can_ff() somehow so that we're not adding even that single traversal. But I expect that may be hard to do because of the multiple heads (e.g., we cannot use the usual ahead/behind code). -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: "Not possible to fast-forward" when pull.ff=only and new commits on remote 2021-10-20 17:09 ` Jeff King @ 2021-10-20 19:19 ` Junio C Hamano 2021-10-20 20:36 ` Jeff King 0 siblings, 1 reply; 9+ messages in thread From: Junio C Hamano @ 2021-10-20 19:19 UTC (permalink / raw) To: Jeff King; +Cc: Kenneth Arnold, Alex Henrie, git@vger.kernel.org Jeff King <peff@peff.net> writes: >> + ours = lookup_commit_reference(the_repository, orig_head); > > I think orig_head can be the null oid if we're on an unborn HEAD. I > guess you'd want to return "1" in that case (but I could be wrong; it > looks like get_can_ff() assumes it's valid, so perhaps that case is > handled earlier). It is a good point; the main codeflow already special cases the unborn HEAD to delegate to pull_into_void() before it gets to the point to call get_can_ff(). > I'd expect that merge_heads can never be empty here, or we'd bail > earlier in the command Yes, that happens even before that "are we unborn" check. > Running a sequence of traversals like this can be slow, because we may > walk over the same history again and again. But I think in the usual > non-octopus cases we'd only have one entry, so we'd only be adding a > single extra merge-base traversal in most cases. > > It does feel like this could be combined with get_can_ff() somehow so > that we're not adding even that single traversal. But I expect that may > be hard to do because of the multiple heads (e.g., we cannot use the > usual ahead/behind code). I'd leave such an optimization as a separate topic. This was meant to be a regression fix. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: "Not possible to fast-forward" when pull.ff=only and new commits on remote 2021-10-20 19:19 ` Junio C Hamano @ 2021-10-20 20:36 ` Jeff King 0 siblings, 0 replies; 9+ messages in thread From: Jeff King @ 2021-10-20 20:36 UTC (permalink / raw) To: Junio C Hamano; +Cc: Kenneth Arnold, Alex Henrie, git@vger.kernel.org On Wed, Oct 20, 2021 at 12:19:19PM -0700, Junio C Hamano wrote: > > Running a sequence of traversals like this can be slow, because we may > > walk over the same history again and again. But I think in the usual > > non-octopus cases we'd only have one entry, so we'd only be adding a > > single extra merge-base traversal in most cases. > > > > It does feel like this could be combined with get_can_ff() somehow so > > that we're not adding even that single traversal. But I expect that may > > be hard to do because of the multiple heads (e.g., we cannot use the > > usual ahead/behind code). > > I'd leave such an optimization as a separate topic. This was meant > to be a regression fix. Yeah, I'm definitely OK with that. You might be making performance a little worse with your patch, but getting correctness right is much more important. -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date 2021-10-20 16:28 ` Junio C Hamano 2021-10-20 17:09 ` Jeff King @ 2021-10-20 19:02 ` Junio C Hamano 2021-10-20 20:45 ` Jeff King 1 sibling, 1 reply; 9+ messages in thread From: Junio C Hamano @ 2021-10-20 19:02 UTC (permalink / raw) To: git; +Cc: Jeff King, Kenneth Arnold, Alex Henrie Earlier, we made sure that "git pull --ff-only" (and "git -c pull.ff=only pull") errors out when our current HEAD is not an ancestor of the tip of the history we are merging, but the condition to trigger the error was implemented incorrectly. Imagine you forked from a remote branch, built your history on top of it, and then attempted to pull from them again. If they have not made any update in the meantime, our current HEAD is obviously not their ancestor, and this new error triggers. Without the --ff-only option, we just report that there is no need to pull; we did the same historically with --ff-only, too. Make sure we do not fail with the recently added check to restore the historycal behaviour. Reported-by: Kenneth Arnold <ka37@calvin.edu> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- * With tests and a proposed log message. builtin/pull.c | 29 ++++++++++++++++++++++++++++- t/t7601-merge-pull-config.sh | 16 +++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/builtin/pull.c b/builtin/pull.c index b311ea6b9d..05f1f0e446 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -933,6 +933,33 @@ static int get_can_ff(struct object_id *orig_head, return ret; } +/* + * Is orig_head is a descendant of _all_ merge_heads? + * unfortunately is_descendant_of() cannot be used as it is to + * ask 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) { advise(_("You have divergent branches and need to specify how to reconcile them.\n" @@ -1074,7 +1101,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix) /* ff-only takes precedence over rebase */ 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(); opt_rebase = REBASE_FALSE; } diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh index 1f652f433e..6275641b9c 100755 --- a/t/t7601-merge-pull-config.sh +++ b/t/t7601-merge-pull-config.sh @@ -2,7 +2,7 @@ test_description='git merge -Testing pull.* configuration parsing.' +Testing pull.* configuration parsing and other things.' . ./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_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)' ' git reset --hard c1 && git config pull.twohead ours && -- 2.33.1-904-gfba30156dd ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date 2021-10-20 19:02 ` [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date Junio C Hamano @ 2021-10-20 20:45 ` Jeff King 2021-10-21 6:38 ` Alex Henrie 0 siblings, 1 reply; 9+ messages in thread From: Jeff King @ 2021-10-20 20:45 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Kenneth Arnold, Alex Henrie On Wed, Oct 20, 2021 at 12:02:09PM -0700, Junio C Hamano wrote: > Earlier, we made sure that "git pull --ff-only" (and "git -c > pull.ff=only pull") errors out when our current HEAD is not an > ancestor of the tip of the history we are merging, but the condition > to trigger the error was implemented incorrectly. > > Imagine you forked from a remote branch, built your history on top > of it, and then attempted to pull from them again. If they have not > made any update in the meantime, our current HEAD is obviously not > their ancestor, and this new error triggers. > > Without the --ff-only option, we just report that there is no need > to pull; we did the same historically with --ff-only, too. Thanks, this looks good to me overall, and I agree this is a regression we should try to fix promptly (so thank you for jumping on it). > Make sure we do not fail with the recently added check to restore > the historycal behaviour. Not sure if "historycal" is a typo or some clever pun. :) > +/* > + * Is orig_head is a descendant of _all_ merge_heads? s/is a/a/ > +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; > +} You answered all of my "what about..." questions from before elsewhere in the thread, so this looks correct. > +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)" > +' And these tests cover the cases I'd expect. The use of "test" with process substitution looks a bit funny to me these days, but it does match the surrounding code (and losing the exit codes isn't a big deal here, as we are not testing rev-parse). The combo of "test_config" and "git -c" is unusual, but I don't see anything wrong with it. -Peff ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date 2021-10-20 20:45 ` Jeff King @ 2021-10-21 6:38 ` Alex Henrie 0 siblings, 0 replies; 9+ messages in thread From: Alex Henrie @ 2021-10-21 6:38 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Git mailing list, Kenneth Arnold On Wed, Oct 20, 2021 at 2:45 PM Jeff King <peff@peff.net> wrote: > > On Wed, Oct 20, 2021 at 12:02:09PM -0700, Junio C Hamano wrote: > > > Earlier, we made sure that "git pull --ff-only" (and "git -c > > pull.ff=only pull") errors out when our current HEAD is not an > > ancestor of the tip of the history we are merging, but the condition > > to trigger the error was implemented incorrectly. > > > > Imagine you forked from a remote branch, built your history on top > > of it, and then attempted to pull from them again. If they have not > > made any update in the meantime, our current HEAD is obviously not > > their ancestor, and this new error triggers. > > > > Without the --ff-only option, we just report that there is no need > > to pull; we did the same historically with --ff-only, too. > > Thanks, this looks good to me overall, and I agree this is a regression > we should try to fix promptly (so thank you for jumping on it). It looks like you guys got this under control in no time. Thank you so much! -Alex ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2021-10-21 6:38 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-10-19 17:23 "Not possible to fast-forward" when pull.ff=only and new commits on remote Kenneth Arnold 2021-10-19 21:22 ` Jeff King 2021-10-20 16:28 ` Junio C Hamano 2021-10-20 17:09 ` Jeff King 2021-10-20 19:19 ` Junio C Hamano 2021-10-20 20:36 ` Jeff King 2021-10-20 19:02 ` [PATCH v2] pull: --ff-only should make it a noop when already-up-to-date Junio C Hamano 2021-10-20 20:45 ` Jeff King 2021-10-21 6:38 ` Alex Henrie
Code repositories for project(s) associated with this public inbox https://80x24.org/mirrors/git.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).