{"thread":{"id":"62904","subject":"[PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","startedAt":"2025-02-05T03:11:41Z","lastAt":"2025-04-22T20:30:22Z","messageCount":13,"participants":["D. Ben Knoble","Junio C Hamano","Alex Henrie"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"511846","messageId":"20250205030642.95252-1-ben.knoble+github@gmail.com","threadId":"62904","inReplyTo":null,"subject":"[PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-02-05T03:06:28Z","receivedAt":"2025-02-05T03:11:41Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"When running \"git pull\" with the following configuration options, we\nfail to merge divergent branches:\n\n- pull.ff=only\n- pull.rebase (unset)\n- branch.<current_branch>.rebase=true\n\nYet it seems that the user intended to make rebase the default for the\ncurrent branch while using --ff-only for non-rebase pulls. Since this\ncase appears uncovered by existing tests, changing the behavior here\nmight be safe: it makes what was an error into a successful rebase.\n\nAdd a test for the behavior and make it pass: this requires knowing from\nwhere the rebase was requested. Previous commits (e4dc25ed49 (pull:\nsince --ff-only overrides, handle it first, 2021-07-22), adc27d6a93\n(pull: make --rebase and --no-rebase override pull.ff=only, 2021-07-22))\ntook care to differentiate that --rebase overrides pull.ff=only, but\ndon't distinguish which config setting requests the rebase. Split\nconfig_get_rebase into 2 parts so that we know where the rebase comes\nfrom, since we only want to allow branch-config to override pull.ff=only\n(like --rebase does); pull.rebase should still be overridden by\npull.ff=only or --ff-only.\n\nSigned-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>\n---\nNotes:\n    - I also looked at ea1954af77 (pull: should be noop when already-up-to-date,\n      2021-11-17) when trying to understand how some options override others,\n      but it didnt' seem germane to the final version.\n    - I think I've got the right test script, since it's the one that started\n      failing before I added the \"else\" branch to the new code (which also\n      confirms that it's necessary to preserve current behavior); the only new\n      behavior should be the one mentioned by the new test.\n    - A possible #leftoverbits: it would be good to document more clearly the\n      interplay of --ff[-only], --rebase, pull.ff, pull.rebase, and\n      branch.<name>.rebase, particularly when they override each other.\n      Confusingly, branch.<name>.merge has nothing to do with whether pull will\n      merge or rebase ;) lest you think I'd forgotten something that _looks_\n      parallel to pull.rebase.\n\n builtin/pull.c               | 39 ++++++++++++++++++++++++++++--------\n t/t7601-merge-pull-config.sh |  8 ++++++++\n 2 files changed, 39 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 9c4a00620a..c30f233dcc 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -326,13 +326,13 @@ static const char *config_get_ff(void)\n }\n \n /**\n- * Returns the default configured value for --rebase. It first looks for the\n+ * Returns the default configured value for --rebase. It looks for the\n  * value of \"branch.$curr_branch.rebase\", where $curr_branch is the current\n  * branch, and if HEAD is detached or the configuration key does not exist,\n- * looks for the value of \"pull.rebase\". If both configuration keys do not\n- * exist, returns REBASE_FALSE.\n+ * considers the result unspecified. Follow up by checking\n+ * config_get_rebase_pull.\n  */\n-static enum rebase_type config_get_rebase(int *rebase_unspecified)\n+static enum rebase_type config_get_rebase_branch(int *rebase_unspecified)\n {\n \tstruct branch *curr_branch = branch_get(\"HEAD\");\n \tconst char *value;\n@@ -349,11 +349,22 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified)\n \t\tfree(key);\n \t}\n \n+\t*rebase_unspecified = 1;\n+\treturn REBASE_INVALID;\n+}\n+\n+/*\n+ * Looks for the value of \"pull.rebase\". If it does not exist, returns\n+ * REBASE_FALSE.\n+ */\n+static enum rebase_type config_get_rebase_pull(int *rebase_unspecified)\n+{\n+\tconst char *value;\n+\n \tif (!git_config_get_value(\"pull.rebase\", &value))\n \t\treturn parse_config_rebase(\"pull.rebase\", value, 1);\n \n \t*rebase_unspecified = 1;\n-\n \treturn REBASE_FALSE;\n }\n \n@@ -1026,7 +1037,7 @@ int cmd_pull(int argc,\n \t\t * are relying on the next if-condition happening before\n \t\t * the config_get_rebase() call so that an explicit\n \t\t * \"--rebase\" can override a config setting of\n-\t\t * pull.ff=only.\n+\t\t * pull.ff=only. [continued…]\n \t\t */\n \t\tif (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, \"--ff-only\")) {\n \t\t\tfree(opt_ff);\n@@ -1034,8 +1045,20 @@ int cmd_pull(int argc,\n \t\t}\n \t}\n \n-\tif (opt_rebase < 0)\n-\t\topt_rebase = config_get_rebase(&rebase_unspecified);\n+\tif (opt_rebase < 0) {\n+\t\t/*\n+\t\t * […continued] But, if the config requests rebase *for this\n+\t\t * branch*, override --ff-only, which otherwise takes precedence\n+\t\t * over pull.rebase=true.\n+\t\t */\n+\t\topt_rebase = config_get_rebase_branch(&rebase_unspecified);\n+\t\tif (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, \"--ff-only\")) {\n+\t\t\tfree(opt_ff);\n+\t\t\topt_ff = xstrdup(\"--ff\");\n+\t\t} else {\n+\t\t    opt_rebase = config_get_rebase_pull(&rebase_unspecified);\n+\t\t}\n+\t}\n \n \tif (repo_read_index_unmerged(the_repository))\n \t\tdie_resolve_conflict(\"pull\");\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex 199a1d5db3..fd99f46aad 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -113,6 +113,14 @@\n \ttest_grep ! \"You have divergent branches\" err\n '\n \n+test_expect_success 'pull.rebase not set and pull.ff=only and branch.<name>.rebase=true (not-fast-forward)' '\n+\tgit reset --hard c2 &&\n+\ttest_config pull.ff only &&\n+\tgit switch -c bc2 &&\n+\ttest_config branch.bc2.rebase true &&\n+\tgit pull . c1\n+'\n+\n test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '\n \tgit reset --hard c2 &&\n \tgit pull --rebase . c1 2>err &&\n-- \n2.47.0\n\n"},{"id":"511869","messageId":"xmqqbjvgr11y.fsf@gitster.g","threadId":"62904","inReplyTo":"20250205030642.95252-1-ben.knoble+github@gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-02-05T13:08:57Z","receivedAt":"2025-02-05T13:09:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n\n> When running \"git pull\" with the following configuration options, we\n> fail to merge divergent branches:\n>\n> - pull.ff=only\n> - pull.rebase (unset)\n> - branch.<current_branch>.rebase=true\n>\n> Yet it seems that the user intended to make rebase the default for the\n> current branch while using --ff-only for non-rebase pulls. Since this\n> case appears uncovered by existing tests, changing the behavior here\n> might be safe: it makes what was an error into a successful rebase.\n\nHmph, to me it looks more like with pull.ff, the user, no matter\nwhat other variables say and which mode between merge and rebase a\npull consolidates the histories, wanted to make sure they will never\naccept anything other than fast-forwarding of the history, because\nthe end-user expects that they will pull only after they push out\neverything, i.e., the expectation is that the other side is a strict\nfast-forward or the user wants to examine the situation before\nmaking further damage to the local history.\n\nWith that understanding, I am not sure \"even though pull.ff tells\nus to stop unless the other side is a descendant of our history, if\nwe are rebasing, it is OK if they have something we have never seen\"\nis a good thing to do.\n\nSo, I dunno.\n"},{"id":"511877","messageId":"CALnO6CA_vF4huxMx6jSS4SVjS4+EO9K16Msco-vMUDzSoYRDOg@mail.gmail.com","threadId":"62904","inReplyTo":"xmqqbjvgr11y.fsf@gitster.g","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-02-05T16:36:10Z","receivedAt":"2025-02-05T16:36:24Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Wed, Feb 5, 2025 at 8:09 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n>\n> > When running \"git pull\" with the following configuration options, we\n> > fail to merge divergent branches:\n> >\n> > - pull.ff=only\n> > - pull.rebase (unset)\n> > - branch.<current_branch>.rebase=true\n> >\n> > Yet it seems that the user intended to make rebase the default for the\n> > current branch while using --ff-only for non-rebase pulls. Since this\n> > case appears uncovered by existing tests, changing the behavior here\n> > might be safe: it makes what was an error into a successful rebase.\n>\n> Hmph, to me it looks more like with pull.ff, the user, no matter\n> what other variables say and which mode between merge and rebase a\n> pull consolidates the histories, wanted to make sure they will never\n> accept anything other than fast-forwarding of the history, because\n> the end-user expects that they will pull only after they push out\n> everything, i.e., the expectation is that the other side is a strict\n> fast-forward or the user wants to examine the situation before\n> making further damage to the local history.\n\nThat's certainly one way to understand --ff-only, but I can't find it\nsupported by existing docs (though it's what current code says,\nexcepting lack of test for interaction with branch.name.merge). For\nexample, `git help pull`:\n\n        --ff-only\n           Only update to the new history if there is no divergent local\n           history. This is the default when no method for reconciling divergent\n           histories is provided (via the --rebase=* flags).\n\nand `git help config`:\n\n       pull.ff\n           By default, Git does not create an extra merge commit when merging a\n           commit that is a descendant of the current commit. Instead, the tip\n           of the current branch is fast-forwarded. When set to false, this\n           variable tells Git to create an extra merge commit in such a case\n           (equivalent to giving the --no-ff option from the command line). When\n           set to only, only such fast-forward merges are allowed (equivalent to\n           giving the --ff-only option from the command line). This setting\n           overrides merge.ff when pulling.\n\n[…]\n\n       branch.autoSetupRebase\n           When a new branch is created with git branch, git switch or git\n           checkout that tracks another branch, this variable tells Git to set\n           up pull to rebase instead of merge (see \"branch.<name>.rebase\"). When\n           never, rebase is never automatically set to true. When local, rebase\n           is set to true for tracked branches of other local branches. When\n           remote, rebase is set to true for tracked branches of remote-tracking\n           branches. When always, rebase will be set to true for all tracking\n           branches. See \"branch.autoSetupMerge\" for details on how to set up a\n           branch to track another branch. This option defaults to never.\n\n[…]\n\n       branch.<name>.rebase\n           When true, rebase the branch <name> on top of the fetched branch,\n           instead of merging the default branch from the default remote when\n           \"git pull\" is run. See \"pull.rebase\" for doing this in a non\n           branch-specific manner.\n\n           [snip]\n\n           NOTE: this is a possibly dangerous operation; do not use it unless\n           you understand the implications (see git-rebase(1) for details).\n\nSo I would tend to read branch.name.rebase as \"you opted in to this,\nyou know what you're doing\" and let it override --ff-only.\n\nGranted, it's not clear just from reading the various git-config files\nwhich sections and variables override which, so I'm perhaps\noverly-reliant on the documentation to understand when those overrides\nhappen (see \"notes\" in original post).\n\n>\n> With that understanding, I am not sure \"even though pull.ff tells\n> us to stop unless the other side is a descendant of our history, if\n> we are rebasing, it is OK if they have something we have never seen\"\n> is a good thing to do.\n>\n> So, I dunno.\n\nAgreed that if pull.ff=only is supposed to override all other options\n(except those on the command-line), this might be wrong. And `git pull\n--rebase` works in the scenario I described.\n\nI think that `pull.ff=only` + `branch.name.rebase=true` is a useful\ncombination to say \"unless I'm asking to rebase [via --rebase or\nbranch settings], only permit fast-forward pulls.\" For example, my\nmain or master branch is typically fast-forward only, while I want my\ntopic branches to be rebased; preferably, all of those things happen\nfor just \"git pull.\"\n\nBut maybe the intended way to accomplish what I want is pull.ff=true\n(the default?), which doesn't prevent accidental merges in the cases I\nwant it to without setting branch.name.mergeOptions for each branch I\nwant to protect from accidental pull-merges. (I'm in the habit of\nusing fetch + merge as needed and mostly use pull to shortcut things\nwhen I'm confident, and obviously I can undo the accidental merge… but\nnot having it in the first place is nice, too.)\n\nLMK if something in my position is not clear—my overreliance on\nparentheticals can be confusing.\n"},{"id":"511893","messageId":"xmqq34gsp9tr.fsf@gitster.g","threadId":"62904","inReplyTo":"CALnO6CA_vF4huxMx6jSS4SVjS4+EO9K16Msco-vMUDzSoYRDOg@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-02-05T17:42:24Z","receivedAt":"2025-02-05T17:42:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n\n>> So, I dunno.\n>\n> Agreed that if pull.ff=only is supposed to override all other options\n> (except those on the command-line), this might be wrong. And `git pull\n> --rebase` works in the scenario I described.\n\nYeah, I view --ff-only as a safety measure for the user to say \"my\nworkflow is to make sure I do not have anything locally cooking on\nmy branch when integrating with the other side, and stop me if I\nsomehow made a mistake\".  If rebase or other options override, the\nfolks in the rebasing camp, unlike in the merging camp, cannot\nbenefit from such safety measure, which worries me.\n"},{"id":"511907","messageId":"CALnO6CC71A_Bn+RhyXfmhiNCn2vFGJ+WCs8+dAnpQvGFyNZyfA@mail.gmail.com","threadId":"62904","inReplyTo":"xmqq34gsp9tr.fsf@gitster.g","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-02-05T21:14:21Z","receivedAt":"2025-02-05T21:14:34Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Wed, Feb 5, 2025 at 12:42 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n>\n> >> So, I dunno.\n> >\n> > Agreed that if pull.ff=only is supposed to override all other options\n> > (except those on the command-line), this might be wrong. And `git pull\n> > --rebase` works in the scenario I described.\n>\n> Yeah, I view --ff-only as a safety measure for the user to say \"my\n> workflow is to make sure I do not have anything locally cooking on\n> my branch when integrating with the other side, and stop me if I\n> somehow made a mistake\".  If rebase or other options override, the\n> folks in the rebasing camp, unlike in the merging camp, cannot\n> benefit from such safety measure, which worries me.\n\nIs there, then, an existing combination that means roughly to treat\n`git pull` with no other options like this:\n- if not rebasing, forbid merging and be equivalent to --ff-only\n- if rebasing is requested (because of branch.name.rebase or --rebase\nor …?), allow it\n\nIn other words, something like a pull.merge=ff (or ff-only) meaning to\napply the rules I've attempted to describe, in which case I would\nleave pull.ff unset?\n\nI suppose pull.rebase=true is close, but is not quite the same for me\n(I'd like to be warned when this would imply a non-fast-forward for a\nmain branch, though the \"rebasing\" logs might be sufficient)…\n"},{"id":"512022","messageId":"CAMMLpeQvJUZJuwvK-H=M_FFedpgazGOPH=7wvPCg3U8RrxEtkA@mail.gmail.com","threadId":"62904","inReplyTo":"CALnO6CC71A_Bn+RhyXfmhiNCn2vFGJ+WCs8+dAnpQvGFyNZyfA@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2025-02-07T02:35:23Z","receivedAt":"2025-02-07T02:36:01Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Tue, Feb 4, 2025 at 8:11 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n>\n> When running \"git pull\" with the following configuration options, we\n> fail to merge divergent branches:\n>\n> - pull.ff=only\n> - pull.rebase (unset)\n> - branch.<current_branch>.rebase=true\n>\n> Yet it seems that the user intended to make rebase the default for the\n> current branch while using --ff-only for non-rebase pulls.\n\nYou make an interesting point. The idea is that more specific options\noverride less specific options. In this case, \"fast-forward only\" is\nmore specific than \"rebase\" (because rebasing might or might not\nfast-forward), but \"my branch\" is also more specific than \"all\nbranches\". So which option should win? 🤔\n\nOn Wed, Feb 5, 2025 at 2:14 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n\n> Is there, then, an existing combination that means roughly to treat\n> `git pull` with no other options like this:\n> - if not rebasing, forbid merging and be equivalent to --ff-only\n> - if rebasing is requested (because of branch.name.rebase or --rebase\n> or …?), allow it\n\nI think what we're missing is a branch.<name>.ffOnly option to make a\nparticular branch fast-forward only. Such an option would be\nespecially useful for the master branch, but you could set it on all\nof your branches except the ones that you want to rebase. We could\neven have a branch.autoSetupFfOnly option to turn on ffOnly\nautomatically for new branches.\n\n-Alex\n"},{"id":"512198","messageId":"CALnO6CDZ=rq_eZESzi++VFk081ddosHMpKQV4QHNFJbnsOMAzg@mail.gmail.com","threadId":"62904","inReplyTo":"CAMMLpeQvJUZJuwvK-H=M_FFedpgazGOPH=7wvPCg3U8RrxEtkA@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-02-10T20:26:41Z","receivedAt":"2025-02-10T20:26:54Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Thu, Feb 6, 2025 at 9:36 PM Alex Henrie <alexhenrie24@gmail.com> wrote:\n>\n> On Tue, Feb 4, 2025 at 8:11 PM D. Ben Knoble\n> <ben.knoble+github@gmail.com> wrote:\n> >\n> > When running \"git pull\" with the following configuration options, we\n> > fail to merge divergent branches:\n> >\n> > - pull.ff=only\n> > - pull.rebase (unset)\n> > - branch.<current_branch>.rebase=true\n> >\n> > Yet it seems that the user intended to make rebase the default for the\n> > current branch while using --ff-only for non-rebase pulls.\n>\n> You make an interesting point. The idea is that more specific options\n> override less specific options. In this case, \"fast-forward only\" is\n> more specific than \"rebase\" (because rebasing might or might not\n> fast-forward), but \"my branch\" is also more specific than \"all\n> branches\". So which option should win? 🤔\n\nPrecisely! I think \"my branch\" is most specific here, but Junio's\nargument is (if I understand it) that pull.ff=only is _stronger_,\nregardless of specificity.\n\n>\n> On Wed, Feb 5, 2025 at 2:14 PM D. Ben Knoble\n> <ben.knoble+github@gmail.com> wrote:\n>\n> > Is there, then, an existing combination that means roughly to treat\n> > `git pull` with no other options like this:\n> > - if not rebasing, forbid merging and be equivalent to --ff-only\n> > - if rebasing is requested (because of branch.name.rebase or --rebase\n> > or …?), allow it\n>\n> I think what we're missing is a branch.<name>.ffOnly option to make a\n> particular branch fast-forward only. Such an option would be\n> especially useful for the master branch, but you could set it on all\n> of your branches except the ones that you want to rebase. We could\n> even have a branch.autoSetupFfOnly option to turn on ffOnly\n> automatically for new branches.\n\nThat is probably something that is missing, and might solve the\nproblem, but I don't know that these in particular are something I\nneed (read: want to implement).\n\nHow do you (and Junio, and others) feel about\npull.ff=onlyUnlessOverridden? The meaning would be \"like --ff-only\nexcept when branch.<name>.rebase says otherwise.\"\n\nThe name of the value can be workshopped (I initially thought of\n\"override\" as a short value, but it may be too short to convey its\nintended meaning). Perhaps \"onlyOr[Branch]Rebase\"?\n\nI think this would be a smaller change that meets my needs without\nchanging the meaning of ff=only.\n\n>\n> -Alex\n"},{"id":"512210","messageId":"CAMMLpeSgSTU+SVeU6A_9LJvjVbho+QC8HpNQtKJvFic98xKvJQ@mail.gmail.com","threadId":"62904","inReplyTo":"CALnO6CDZ=rq_eZESzi++VFk081ddosHMpKQV4QHNFJbnsOMAzg@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"Alex Henrie","fromEmail":"alexhenrie24@gmail.com","sentAt":"2025-02-11T06:55:00Z","receivedAt":"2025-02-11T06:56:41Z","isPatch":true,"sender":{"key":"alexhenrie24@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5951993?v=4"},"body":"On Mon, Feb 10, 2025 at 1:26 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n>\n> On Thu, Feb 6, 2025 at 9:36 PM Alex Henrie <alexhenrie24@gmail.com> wrote:\n> >\n> > On Tue, Feb 4, 2025 at 8:11 PM D. Ben Knoble\n> > <ben.knoble+github@gmail.com> wrote:\n> > >\n> > > When running \"git pull\" with the following configuration options, we\n> > > fail to merge divergent branches:\n> > >\n> > > - pull.ff=only\n> > > - pull.rebase (unset)\n> > > - branch.<current_branch>.rebase=true\n> > >\n> > > Yet it seems that the user intended to make rebase the default for the\n> > > current branch while using --ff-only for non-rebase pulls.\n> >\n> > You make an interesting point. The idea is that more specific options\n> > override less specific options. In this case, \"fast-forward only\" is\n> > more specific than \"rebase\" (because rebasing might or might not\n> > fast-forward), but \"my branch\" is also more specific than \"all\n> > branches\". So which option should win? 🤔\n>\n> Precisely! I think \"my branch\" is most specific here, but Junio's\n> argument is (if I understand it) that pull.ff=only is _stronger_,\n> regardless of specificity.\n\nI can see it both ways here, though in general when the user's intent\nis ambiguous, I think Git should default to the more conservative\noperation.\n\n> > On Wed, Feb 5, 2025 at 2:14 PM D. Ben Knoble\n> > <ben.knoble+github@gmail.com> wrote:\n> >\n> > > Is there, then, an existing combination that means roughly to treat\n> > > `git pull` with no other options like this:\n> > > - if not rebasing, forbid merging and be equivalent to --ff-only\n> > > - if rebasing is requested (because of branch.name.rebase or --rebase\n> > > or …?), allow it\n> >\n> > I think what we're missing is a branch.<name>.ffOnly option to make a\n> > particular branch fast-forward only. Such an option would be\n> > especially useful for the master branch, but you could set it on all\n> > of your branches except the ones that you want to rebase. We could\n> > even have a branch.autoSetupFfOnly option to turn on ffOnly\n> > automatically for new branches.\n>\n> That is probably something that is missing, and might solve the\n> problem, but I don't know that these in particular are something I\n> need (read: want to implement).\n>\n> How do you (and Junio, and others) feel about\n> pull.ff=onlyUnlessOverridden? The meaning would be \"like --ff-only\n> except when branch.<name>.rebase says otherwise.\"\n>\n> The name of the value can be workshopped (I initially thought of\n> \"override\" as a short value, but it may be too short to convey its\n> intended meaning). Perhaps \"onlyOr[Branch]Rebase\"?\n>\n> I think this would be a smaller change that meets my needs without\n> changing the meaning of ff=only.\n\nIn my opinion, the matrix of which pull options override which pull\noptions is already too hard to understand. Rather than add a new\ndimension to pull.ff, I would much prefer to fill in the gap that is\nthe lack of a per-branch fast-forward setting. It might be more work\nin the short term, but it's an investment:\npull.ff=onlyUnlessOverridden would only address your particular use\ncase, but a per-branch setting could address many others. For example,\nthe user could set branch.autoSetupRebase=true to make every branch\nrebase by default, but override it with branch.master.ff=only to make\nthe master branch fast-forward only. Or the user could have\nbranch.<name>.rebase set to either true or false as appropriate for\neach branch, but temporarily set branch.<name>.ff=only when they are\nin the middle of work on a branch and don't want to accidentally bring\nin upstream changes that would interrupt their work.\n\nIf you think that you can write the patch to implement\npull.ff=onlyUnlessOverridden on your own, I think you're capable of\nimplementing branch.<name>.ff=(true|false|only) and\nbranch.autoSetupFf=(true|false|only). Use the code for the existing\nbranch.<name>.rebase and branch.autoSetupRebase options as a guide,\nand people like me are available on the mailing list to support you.\n\n-Alex\n"},{"id":"516525","messageId":"CALnO6CBi-c9U-UskTzjNBH+k8VQybdSshYgs+A3_DRH-iz7zHA@mail.gmail.com","threadId":"62904","inReplyTo":"CALnO6CC71A_Bn+RhyXfmhiNCn2vFGJ+WCs8+dAnpQvGFyNZyfA@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-04-22T19:58:28Z","receivedAt":"2025-04-22T19:58:40Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Wed, Feb 5, 2025 at 4:14 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n>\n> On Wed, Feb 5, 2025 at 12:42 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > \"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n> >\n> > >> So, I dunno.\n> > >\n> > > Agreed that if pull.ff=only is supposed to override all other options\n> > > (except those on the command-line), this might be wrong. And `git pull\n> > > --rebase` works in the scenario I described.\n> >\n> > Yeah, I view --ff-only as a safety measure for the user to say \"my\n> > workflow is to make sure I do not have anything locally cooking on\n> > my branch when integrating with the other side, and stop me if I\n> > somehow made a mistake\".  If rebase or other options override, the\n> > folks in the rebasing camp, unlike in the merging camp, cannot\n> > benefit from such safety measure, which worries me.\n>\n> Is there, then, an existing combination that means roughly to treat\n> `git pull` with no other options like this:\n> - if not rebasing, forbid merging and be equivalent to --ff-only\n> - if rebasing is requested (because of branch.name.rebase or --rebase\n> or …?), allow it\n>\n> In other words, something like a pull.merge=ff (or ff-only) meaning to\n> apply the rules I've attempted to describe, in which case I would\n> leave pull.ff unset?\n>\n> I suppose pull.rebase=true is close, but is not quite the same for me\n> (I'd like to be warned when this would imply a non-fast-forward for a\n> main branch, though the \"rebasing\" logs might be sufficient)…\n\nFWIW, I found some tests that indicate, to me, that I should use\npull.rebase=true (or merges) + branch.<name>.rebase=false for the case\nI described: https://github.com/git/git/blob/08bdfd453584e489d5a551aecbdcb77584e1b958/t/t5520-pull.sh#L505-L514\n\nSo it turns out my itch was already scratched.\n"},{"id":"516526","messageId":"CALnO6CDb8_V9T3o+ON-8BZHcuf83UNGp23zxJKMc-rcGY=M1iA@mail.gmail.com","threadId":"62904","inReplyTo":"CAMMLpeSgSTU+SVeU6A_9LJvjVbho+QC8HpNQtKJvFic98xKvJQ@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-04-22T20:03:02Z","receivedAt":"2025-04-22T20:03:14Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Tue, Feb 11, 2025 at 1:56 AM Alex Henrie <alexhenrie24@gmail.com> wrote:\n[snip]\n> > > On Wed, Feb 5, 2025 at 2:14 PM D. Ben Knoble\n> > > <ben.knoble+github@gmail.com> wrote:\n> > >\n> > > > Is there, then, an existing combination that means roughly to treat\n> > > > `git pull` with no other options like this:\n> > > > - if not rebasing, forbid merging and be equivalent to --ff-only\n> > > > - if rebasing is requested (because of branch.name.rebase or --rebase\n> > > > or …?), allow it\n> > >\n> > > I think what we're missing is a branch.<name>.ffOnly option to make a\n> > > particular branch fast-forward only. Such an option would be\n> > > especially useful for the master branch, but you could set it on all\n> > > of your branches except the ones that you want to rebase. We could\n> > > even have a branch.autoSetupFfOnly option to turn on ffOnly\n> > > automatically for new branches.\n> >\n> > That is probably something that is missing, and might solve the\n> > problem, but I don't know that these in particular are something I\n> > need (read: want to implement).\n> >\n> > How do you (and Junio, and others) feel about\n> > pull.ff=onlyUnlessOverridden? The meaning would be \"like --ff-only\n> > except when branch.<name>.rebase says otherwise.\"\n> >\n> > The name of the value can be workshopped (I initially thought of\n> > \"override\" as a short value, but it may be too short to convey its\n> > intended meaning). Perhaps \"onlyOr[Branch]Rebase\"?\n> >\n> > I think this would be a smaller change that meets my needs without\n> > changing the meaning of ff=only.\n>\n> In my opinion, the matrix of which pull options override which pull\n> options is already too hard to understand. Rather than add a new\n> dimension to pull.ff, I would much prefer to fill in the gap that is\n> the lack of a per-branch fast-forward setting. It might be more work\n> in the short term, but it's an investment:\n> pull.ff=onlyUnlessOverridden would only address your particular use\n> case, but a per-branch setting could address many others. For example,\n> the user could set branch.autoSetupRebase=true to make every branch\n> rebase by default, but override it with branch.master.ff=only to make\n> the master branch fast-forward only. Or the user could have\n> branch.<name>.rebase set to either true or false as appropriate for\n> each branch, but temporarily set branch.<name>.ff=only when they are\n> in the middle of work on a branch and don't want to accidentally bring\n> in upstream changes that would interrupt their work.\n>\n> If you think that you can write the patch to implement\n> pull.ff=onlyUnlessOverridden on your own, I think you're capable of\n> implementing branch.<name>.ff=(true|false|only) and\n> branch.autoSetupFf=(true|false|only). Use the code for the existing\n> branch.<name>.rebase and branch.autoSetupRebase options as a guide,\n> and people like me are available on the mailing list to support you.\n>\n> -Alex\n\nI actually did start working on this by first writing documentation; I\ngot about as far as saying that branch.<name>.rebase overrides\nbranch.<name>.ff when pulling unless it is only and that\nbranch.<name>.ff overrides merge.ff before I realized that I was\nconstructing a complex decision-matrix of how config and CLI options\naffect what happens, and it's already overwhelming enough…\n\nIt would actually be nice to spell out the matrix somewhere, but I can\ndo that in a blog post if I ever find time. I'll leave it to others to\nincrease the complexity of that matrix :)\n"},{"id":"516527","messageId":"CALnO6CDq5BRogPCcDozTi1NEYL6nCoEDaNkFdq2+1V6vVRy=1g@mail.gmail.com","threadId":"62904","inReplyTo":"CALnO6CBi-c9U-UskTzjNBH+k8VQybdSshYgs+A3_DRH-iz7zHA@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-04-22T20:05:38Z","receivedAt":"2025-04-22T20:05:52Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Tue, Apr 22, 2025 at 3:58 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n>\n> On Wed, Feb 5, 2025 at 4:14 PM D. Ben Knoble\n> <ben.knoble+github@gmail.com> wrote:\n> >\n> > On Wed, Feb 5, 2025 at 12:42 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > >\n> > > \"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n> > >\n> > > >> So, I dunno.\n> > > >\n> > > > Agreed that if pull.ff=only is supposed to override all other options\n> > > > (except those on the command-line), this might be wrong. And `git pull\n> > > > --rebase` works in the scenario I described.\n> > >\n> > > Yeah, I view --ff-only as a safety measure for the user to say \"my\n> > > workflow is to make sure I do not have anything locally cooking on\n> > > my branch when integrating with the other side, and stop me if I\n> > > somehow made a mistake\".  If rebase or other options override, the\n> > > folks in the rebasing camp, unlike in the merging camp, cannot\n> > > benefit from such safety measure, which worries me.\n> >\n> > Is there, then, an existing combination that means roughly to treat\n> > `git pull` with no other options like this:\n> > - if not rebasing, forbid merging and be equivalent to --ff-only\n> > - if rebasing is requested (because of branch.name.rebase or --rebase\n> > or …?), allow it\n> >\n> > In other words, something like a pull.merge=ff (or ff-only) meaning to\n> > apply the rules I've attempted to describe, in which case I would\n> > leave pull.ff unset?\n> >\n> > I suppose pull.rebase=true is close, but is not quite the same for me\n> > (I'd like to be warned when this would imply a non-fast-forward for a\n> > main branch, though the \"rebasing\" logs might be sufficient)…\n>\n> FWIW, I found some tests that indicate, to me, that I should use\n> pull.rebase=true (or merges) + branch.<name>.rebase=false for the case\n> I described: https://github.com/git/git/blob/08bdfd453584e489d5a551aecbdcb77584e1b958/t/t5520-pull.sh#L505-L514\n>\n> So it turns out my itch was already scratched.\n\nI left out the commit reference, whose message described what I think\nI originally wanted:\n\n> my main or master branch is typically fast-forward only, while I want my\n> topic branches to be rebased; preferably, all of those things happen\n> for just \"git pull.\"\n"},{"id":"516528","messageId":"CALnO6CCMP5qS0f8oMyjav03CzT1AYSCiVCex1C7nqqxg=k7g-w@mail.gmail.com","threadId":"62904","inReplyTo":"CALnO6CDq5BRogPCcDozTi1NEYL6nCoEDaNkFdq2+1V6vVRy=1g@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"D. Ben Knoble","fromEmail":"ben.knoble+github@gmail.com","sentAt":"2025-04-22T20:07:05Z","receivedAt":"2025-04-22T20:07:19Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Tue, Apr 22, 2025 at 4:05 PM D. Ben Knoble\n<ben.knoble+github@gmail.com> wrote:\n>\n> On Tue, Apr 22, 2025 at 3:58 PM D. Ben Knoble\n> <ben.knoble+github@gmail.com> wrote:\n> >\n> > On Wed, Feb 5, 2025 at 4:14 PM D. Ben Knoble\n> > <ben.knoble+github@gmail.com> wrote:\n> > >\n> > > On Wed, Feb 5, 2025 at 12:42 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > > >\n> > > > \"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n> > > >\n> > > > >> So, I dunno.\n> > > > >\n> > > > > Agreed that if pull.ff=only is supposed to override all other options\n> > > > > (except those on the command-line), this might be wrong. And `git pull\n> > > > > --rebase` works in the scenario I described.\n> > > >\n> > > > Yeah, I view --ff-only as a safety measure for the user to say \"my\n> > > > workflow is to make sure I do not have anything locally cooking on\n> > > > my branch when integrating with the other side, and stop me if I\n> > > > somehow made a mistake\".  If rebase or other options override, the\n> > > > folks in the rebasing camp, unlike in the merging camp, cannot\n> > > > benefit from such safety measure, which worries me.\n> > >\n> > > Is there, then, an existing combination that means roughly to treat\n> > > `git pull` with no other options like this:\n> > > - if not rebasing, forbid merging and be equivalent to --ff-only\n> > > - if rebasing is requested (because of branch.name.rebase or --rebase\n> > > or …?), allow it\n> > >\n> > > In other words, something like a pull.merge=ff (or ff-only) meaning to\n> > > apply the rules I've attempted to describe, in which case I would\n> > > leave pull.ff unset?\n> > >\n> > > I suppose pull.rebase=true is close, but is not quite the same for me\n> > > (I'd like to be warned when this would imply a non-fast-forward for a\n> > > main branch, though the \"rebasing\" logs might be sufficient)…\n> >\n> > FWIW, I found some tests that indicate, to me, that I should use\n> > pull.rebase=true (or merges) + branch.<name>.rebase=false for the case\n> > I described: https://github.com/git/git/blob/08bdfd453584e489d5a551aecbdcb77584e1b958/t/t5520-pull.sh#L505-L514\n> >\n> > So it turns out my itch was already scratched.\n>\n> I left out the commit reference, whose message described what I think\n> I originally wanted:\n>\n> > my main or master branch is typically fast-forward only, while I want my\n> > topic branches to be rebased; preferably, all of those things happen\n> > for just \"git pull.\"\n\nSince I apparently hit Send too fast, dropped the CC list to just add\nthe reference I repeatedly forgot to paste:\n\n6b37dff17f (pull: introduce a pull.rebase option to enable --rebase, 2011-11-06)\n"},{"id":"516530","messageId":"xmqq7c3c3pno.fsf@gitster.g","threadId":"62904","inReplyTo":"CALnO6CCMP5qS0f8oMyjav03CzT1AYSCiVCex1C7nqqxg=k7g-w@mail.gmail.com","subject":"Re: [PATCH] pull: allow branch.<name>.rebase to override pull.ff=only","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-04-22T20:30:19Z","receivedAt":"2025-04-22T20:30:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"D. Ben Knoble\" <ben.knoble+github@gmail.com> writes:\n\n>> > So it turns out my itch was already scratched.\n>> ...\n>> I left out the commit reference, whose message described what I think\n>> I originally wanted:\n>\n> 6b37dff17f (pull: introduce a pull.rebase option to enable --rebase, 2011-11-06)\n\nGood to know that your itch was already scratched ;-)\n"}]}