{"thread":{"id":"66349","subject":"pager: consider revisiting automatic LESS=FRX with custom core.pager","startedAt":"2026-09-19T12:20:20Z","lastAt":"2026-10-05T23:59:39Z","messageCount":6,"participants":["Francisco Boni","brian m. carlson","Todd Zullinger"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"552875","messageId":"CAKNeS+mFS_VCs_tQeFb8jBx70FwQLW0LtuqhSk4xSdbWdqDR=g@mail.gmail.com","threadId":"66349","inReplyTo":null,"subject":"pager: consider revisiting automatic LESS=FRX with custom core.pager","fromName":"Francisco Boni","fromEmail":"boboniboni@gmail.com","sentAt":"2026-09-19T12:19:40Z","receivedAt":"2026-09-19T12:20:20Z","isPatch":false,"body":"Hi,\n\nI ran into a surprising interaction between Git's pager environment\nhandling and a custom pager, and I think the longstanding automatic\n`LESS=FRX` behavior may be worth revisiting.\n\nMy setup is roughly:\n\n```\n[core]\n    pager = delta\n```\n\nwith delta eventually invoking `less`.\n\nIn my shell I had:\n\n```\nexport PAGER=\"less\"\n```\n\nbut deliberately did not set `LESS`.\n\nI also configured delta's pager explicitly as:\n\n```\nless --RAW-CONTROL-CHARS --quit-if-one-screen --quit-on-intr --ignore-case\n```\n\nWhat surprised me was that:\n\n```\ngit diff <commit1> <commit2>\n```\n\ncaused the downstream `less` process to behave as though `-X` /\n`--no-init` had been specified: it did not use the alternate screen,\nand in my terminal Page Up / Page Down also stopped behaving normally.\n\nThe `less` argv itself looked as expected and did not contain `-X`.\n\nEventually I tracked this down to Git setting:\n\n```\nLESS=FRX\n```\n\nwhen `LESS` is absent from the environment.\n\nThe particularly revealing test was:\n\n```\nLESS=\"\" git diff <commit1> <commit2>\n```\n\nWith that single change, the same delta -> less chain used the\nalternate screen normally and Page Up / Page Down worked again.\n\nI understand that Git's default `LESS=FRX` behavior is old,\ndocumented, and presumably exists to provide useful defaults when Git\ndirectly invokes `less`.\n\nWhat seems questionable to me is that it is also applied when\n`core.pager` is an arbitrary custom command.\n\nIn this case the effective chain is:\n\n```\ngit -> delta -> less\n```\n\nGit is not invoking `less` itself, but the `LESS=FRX` value injected\nby Git is inherited by delta and then by the `less` process launched\nby delta.\nAs a result, Git ends up silently configuring an implementation detail\nof a pager wrapper. A user who explicitly configures:\n\n```\ncore.pager = delta\n```\n\nwould not necessarily expect Git to alter environment variables\nspecific to another pager that delta may or may not use internally.\n\nThere is also an unintuitive distinction between:\n\n```\nunset LESS\n```\n\nand:\n\n```\nLESS=\"\"\n```\n\nThe former causes Git to inject `FRX`, while the latter causes Git to\nleave the pager environment alone.\n\nI realize simply removing the default could have substantial\ncompatibility consequences given how longstanding this behavior is.\nBut perhaps it would be worth considering whether the automatic `LESS`\ndefault should:\n\n* apply only to Git's own default pager path;\n* be suppressible explicitly through configuration; or\n* otherwise avoid affecting arbitrary custom `core.pager` commands.\n\nFor my own setup, explicitly setting:\n\n```\nexport LESS=\"--RAW-CONTROL-CHARS --quit-if-one-screen --quit-on-intr\n--ignore-case\"\n```\n\nsolves the issue cleanly, because Git then leaves `LESS` untouched.\n\nSo this is not primarily a request for a workaround; rather, I wanted\nto raise the broader behavior because the interaction with pager\nwrappers is quite surprising and difficult to diagnose.\n\nThanks,\nFrancisco Boni\n"},{"id":"552888","messageId":"aq6utXAQA-rRoKSm@fruit.crustytoothpaste.net","threadId":"66349","inReplyTo":"CAKNeS+mFS_VCs_tQeFb8jBx70FwQLW0LtuqhSk4xSdbWdqDR=g@mail.gmail.com","subject":"Re: pager: consider revisiting automatic LESS=FRX with custom core.pager","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-09-19T15:48:06Z","receivedAt":"2026-09-19T15:48:20Z","isPatch":false,"body":"On 2026-09-19 at 12:19:40, Francisco Boni wrote:\n> Hi,\n\nNey,\n\n> There is also an unintuitive distinction between:\n> \n> ```\n> unset LESS\n> ```\n> \n> and:\n> \n> ```\n> LESS=\"\"\n> ```\n> \n> The former causes Git to inject `FRX`, while the latter causes Git to\n> leave the pager environment alone.\n\nYes, that's because Git sets the environment iff it is unset.  In the\nlatter case, it is not unset: it is set to a zero-length value.\n\n> I realize simply removing the default could have substantial\n> compatibility consequences given how longstanding this behavior is.\n> But perhaps it would be worth considering whether the automatic `LESS`\n> default should:\n> \n> * apply only to Git's own default pager path\n> * be suppressible explicitly through configuration; or\n> * otherwise avoid affecting arbitrary custom `core.pager` commands.\n\nWe can't know in the general case whether the pager is less or not.  On\nFreeBSD, `more` is less, for instance, and the pager command allows\narbitrary shell commands, so determining statically which branch is\ntaken is not always possible.  Notably, Debian has `sensible-pager`,\nwhich is the default on that OS, and may (or may not) be less.\n\nUsers would be displeased if Git's pager functionality worked\ndifferently with less depending on how less was invoked or named, or if\nit weren't enabled in a case like the following:\n\n    core.pager='f() { if [ \"$(uname -s)\" = FreeBSD ]; then more \"$@\"; else less \"$@\"; fi; };f'\n\nA user might in fact do exactly that to make things work correctly on\nmultiple platforms with a single gitconfig file.  (This is why passing\ncertain environment variables or options to the shell is obligatory and\nyou cannot simply do shell parsing of the command.)\n\nI agree that this can cause unusual behaviour in the case you've\ndescribed, but that's more of the case because it's actually unusual to\nhave commands that take arguments through the environment in this way.\nThat's no longer really considered a good design; normally we use a\nconfig file instead these days.\n\nI'll note that it is configurable both through the environment and\nthrough configuration, using one of the following:\n\n    GIT_PAGER='LESS=\"\" delta'\n\nor:\n\n    git config core.pager 'LESS=\"\" delta'\n\nor, if you prefer to be still more conservative:\n\n    GIT_PAGER='env -i PATH=\"$PATH\" delta'\n\nwhich unsets all environment variables but the path for your pager.\n\n> So this is not primarily a request for a workaround; rather, I wanted\n> to raise the broader behavior because the interaction with pager\n> wrappers is quite surprising and difficult to diagnose.\n\nI think at this point, we're unlikely to change the behaviour and it\nwould be a notable and unwelcome change to do so.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"552889","messageId":"20260919163725.TExDduTp@teonanacatl.net","threadId":"66349","inReplyTo":"aq6utXAQA-rRoKSm@fruit.crustytoothpaste.net","subject":"Re: pager: consider revisiting automatic LESS=FRX with custom core.pager","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2026-09-19T16:37:33Z","receivedAt":"2026-09-19T16:37:36Z","isPatch":false,"body":"brian m. carlson wrote:\n> I agree that this can cause unusual behaviour in the case you've\n> described, but that's more of the case because it's actually unusual to\n> have commands that take arguments through the environment in this way.\n> That's no longer really considered a good design; normally we use a\n> config file instead these days.\n> \n> I'll note that it is configurable both through the environment and\n> through configuration, using one of the following:\n> \n>     GIT_PAGER='LESS=\"\" delta'\n> \n> or:\n> \n>     git config core.pager 'LESS=\"\" delta'\n> \n> or, if you prefer to be still more conservative:\n> \n>     GIT_PAGER='env -i PATH=\"$PATH\" delta'\n> \n> which unsets all environment variables but the path for your pager.\n> \n>> So this is not primarily a request for a workaround; rather, I wanted\n>> to raise the broader behavior because the interaction with pager\n>> wrappers is quite surprising and difficult to diagnose.\n> \n> I think at this point, we're unlikely to change the behaviour and it\n> would be a notable and unwelcome change to do so.\n\nAll good points.  I wonder if it would help to add a couple\nof sentences to the core.pager documentation mentioning the\nmethod of setting `LESS=\"\"` as another way to override the\noptions?  Something like:\n\n  diff --git i/Documentation/config/core.adoc w/Documentation/config/core.adoc\n  index 340329edc3..45e1fda2a2 100644\n  --- i/Documentation/config/core.adoc\n  +++ w/Documentation/config/core.adoc\n  @@ -614,9 +614,15 @@ command to `LESS=FRX less -S`. The environment does not set the\n   long lines. Similarly, setting `core.pager` to `less -+F` will\n   deactivate the `F` option specified by the environment from the\n   command-line, deactivating the \"quit if one screen\" behavior of\n  -`less`.  One can specifically activate some flags for particular\n  -commands: for example, setting `pager.blame` to `less -S` enables\n  -line truncation only for `git blame`.\n  +`less`. Another way to deactivate an option is setting `core.pager`\n  +to `LESS=\"RX\" less` or `LESS=\"\" less` to override all options. The\n  +second form is useful if the `core.pager` command eventually runs\n  +`less` or a command which respects the `LESS` environment variable\n  +but lacks command line options to override `LESS` options.\n  ++\n  +One can specifically activate some flags for particular commands: for\n  +example, setting `pager.blame` to `less -S` enables line truncation\n  +only for `git blame`.\n   +\n   Likewise, when the `LV` environment variable is unset, Git sets it\n   to `-c`.  You can override this setting by exporting `LV` with\n\nI don't know if that adds more value than the cost of\nincreasing the verbiage of an already long document.\n\n-- \nTodd\n"},{"id":"552890","messageId":"CAKNeS+nxL6HncxKN8WTasB=CfEf85Vi0yCABrB=OGojVwAwBtA@mail.gmail.com","threadId":"66349","inReplyTo":"aq6utXAQA-rRoKSm@fruit.crustytoothpaste.net","subject":"Re: pager: consider revisiting automatic LESS=FRX with custom core.pager","fromName":"Francisco Boni","fromEmail":"boboniboni@gmail.com","sentAt":"2026-09-19T16:52:59Z","receivedAt":"2026-09-19T16:53:39Z","isPatch":false,"body":"On Sat, Sep 19, 2026 at 12:48 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2026-09-19 at 12:19:40, Francisco Boni wrote:\n> > Hi,\n>\n> Ney,\n>\n> > There is also an unintuitive distinction between:\n> >\n> > ```\n> > unset LESS\n> > ```\n> >\n> > and:\n> >\n> > ```\n> > LESS=\"\"\n> > ```\n> >\n> > The former causes Git to inject `FRX`, while the latter causes Git to\n> > leave the pager environment alone.\n>\n> Yes, that's because Git sets the environment iff it is unset.  In the\n> latter case, it is not unset: it is set to a zero-length value.\n>\n> > I realize simply removing the default could have substantial\n> > compatibility consequences given how longstanding this behavior is.\n> > But perhaps it would be worth considering whether the automatic `LESS`\n> > default should:\n> >\n> > * apply only to Git's own default pager path\n> > * be suppressible explicitly through configuration; or\n> > * otherwise avoid affecting arbitrary custom `core.pager` commands.\n>\n> We can't know in the general case whether the pager is less or not.  On\n> FreeBSD, `more` is less, for instance, and the pager command allows\n> arbitrary shell commands, so determining statically which branch is\n> taken is not always possible.  Notably, Debian has `sensible-pager`,\n> which is the default on that OS, and may (or may not) be less.\n\nHi Brian,\n\nThanks for the detailed explanation. The FreeBSD \"more\" case and the\narbitrary-shell-command nature of core.pager clarified some assumptions\nI was making about how feasible it would be for Git to distinguish\n\"direct less\" from wrappers or dynamic pager selection...\n\nThat makes the compatibility argument much clearer.\nThe suggested LESS=\"\" delta override is useful to know about.\n\nAlso, thank you for your work on Git.\n\nBest,\nFrancisco Boni\n"},{"id":"554028","messageId":"20261002234203.4064847-1-tmz@pobox.com","threadId":"66349","inReplyTo":"20260919163725.TExDduTp@teonanacatl.net","subject":"[PATCH] doc: add more examples of overriding LESS in core.pager","fromName":"Todd Zullinger","fromEmail":"tmz@pobox.com","sentAt":"2026-10-02T23:41:52Z","receivedAt":"2026-10-02T23:42:35Z","isPatch":true,"body":"We set the LESS environment variable unconditionally which can surprise\nusers of pagers which respect it but presume we'd only set LESS when\n`core.pager` calls less.\n\nProvide examples of setting LESS in `core.pager` as an additional way to\noverride the options we set in LESS.\n\nReported-by: Francisco Boni <boboniboni@gmail.com>\nSigned-off-by: Todd Zullinger <tmz@pobox.com>\n---\nNow that 2.56.0 is out the door, I offer this up to see if\nit feels like a useful improvement to the docs.\n\nCheers,\nTodd\n\n Documentation/config/core.adoc | 14 +++++++++++---\n 1 file changed, 11 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc\nindex 0b697f53f1..bdc74d291c 100644\n--- a/Documentation/config/core.adoc\n+++ b/Documentation/config/core.adoc\n@@ -621,9 +621,17 @@ command to `LESS=FRX less -S`. The environment does not set the\n long lines. Similarly, setting `core.pager` to `less -+F` will\n deactivate the `F` option specified by the environment from the\n command-line, deactivating the \"quit if one screen\" behavior of\n-`less`.  One can specifically activate some flags for particular\n-commands: for example, setting `pager.blame` to `less -S` enables\n-line truncation only for `git blame`.\n+`less`.\n++\n+Another way to deactivate an option is prefixing `core.pager` with\n+`LESS=\"RX\"` to remove `-F` or `LESS=\"\"` to override all options.\n+This is useful if the `core.pager` command eventually runs `less` or\n+a command which respects the `LESS` environment variable but lacks\n+command line options to override `LESS` options.\n++\n+One can specifically activate some flags for particular commands: for\n+example, setting `pager.blame` to `less -S` enables line truncation\n+only for `git blame`.\n +\n Likewise, when the `LV` environment variable is unset, Git sets it\n to `-c`.  You can override this setting by exporting `LV` with\n-- \n2.56.0\n\n"},{"id":"554229","messageId":"asQ56mv3VbiYmtwk@fruit.crustytoothpaste.net","threadId":"66349","inReplyTo":"20261002234203.4064847-1-tmz@pobox.com","subject":"Re: [PATCH] doc: add more examples of overriding LESS in core.pager","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-10-05T23:59:39Z","receivedAt":"2026-10-05T23:59:39Z","isPatch":true,"body":"On 2026-10-02 at 23:41:52, Todd Zullinger wrote:\n> +Another way to deactivate an option is prefixing `core.pager` with\n> +`LESS=\"RX\"` to remove `-F` or `LESS=\"\"` to override all options.\n> +This is useful if the `core.pager` command eventually runs `less` or\n> +a command which respects the `LESS` environment variable but lacks\n> +command line options to override `LESS` options.\n> ++\n> +One can specifically activate some flags for particular commands: for\n> +example, setting `pager.blame` to `less -S` enables line truncation\n> +only for `git blame`.\n\nSure, this seems like an improvement.  I'm not very particular on the\nwording, but it's good that folks have the information that they need.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"}]}