When I close git log
, I want all its text to be cleared so that I can continue to view the text above.
However regardless of what I use with git config core.pager
(I tried less
, more
and bat
), it always leaves the text there.
Bizarrely, git log | less
and git log | bat
both properly clear the text.
How can I configure Git to properly clear its git log
output without needing to add | less
at the end?
With less
as core.pager
, git log
looks like this:
[many lines of text omitted...]
commit ...
Author: ...
Date: Sun Feb 28 16:31:22 2021 0100
initial commit
(END)
Then when I press q, the text is still there:
[many lines of text omitted...]
commit ...
Author: ...
Date: Sun Feb 28 16:31:22 2021 0100
initial commit
$
CodePudding user response:
You will want to fiddle with your core.pager
and/or its options. In this case you could do either of these:
git config --global core.pager "less -FR"
or add:
export LESS=FR
to whatever shell's login settings file you use (.profile
, for instance).
Background: pagers, or what's going on here?
When Git is sending output to a "terminal"—anything for which the C library that Git uses returns a true value from the isatty
function (pronounced "is a tee tee why"; TTY is an old abbreviation for Teletype or teletypewriter; see this Wikipedia page for much more background)—Git uses a pager. The reasons for using a pager go back to the "glass tty" or computer terminal, which in the 1980s was typically an 80-column, 24-line display device (leading to the 80x24 default size for some terminal emulators).
In other words, the terminal emulator in a modern windowing system is often emulating an extended VT100-style display device, which was emulating an 80-column mechanical printer, which was emulating a mechanical typewriter attached to a telephone line. Curiously, the printer, via paper, offered infinite scrollback—well, until the paper ran out anyway—as do most modern terminal emulators. So to some extent Git is catering to the least capable of these systems: a VT100-ish "screen" where there is no scroll-forward or back, there is only what you see on your screen.
With that in mind, we can see why pagers became popular in the 1980s. If you have a large file full of text, or a lot of output to view, and you're stuck with a display device that can only show one 80-by-24 page at a time, you run that output through a program that understands this and shows it to you that way, pausing after each page.
At UC Berkeley, a guy named Daniel Halbert wrote an early pager called more
, which would print out 23 lines at a time and then add a --More--
prompt on the 24th line as appropriate. Also in that same era, the kinds of computer terminals available were increasing, along with their capabilities (by the mid-1980s some terminals could display a whole 30 lines at a time for instance, and some terminals had some primitive color abilities). So more
grew more options and capabilities, and other pagers came along as well.
Some of these "glass ttys" had two "modes", where you had to send an initialization sequence before you could do fancy screen formatting. In the normal mode, they just printed characters the way a printer would. In the special mode, you could move the cursor to particular parts of the screen, clear the screen, and do other tricks. Editors that showed you your text "live", and pagers that could do fancy stuff, needed to "initialize cursor mode".
Now, one peculiar feature of some VT100-style terminals was the addition of a separate "screen". Going into "alternate screen mode" would hide away the current display and present the other display instead. Some people liked this mode for editors and pagers, and set things up so that when running more
or vi
or emacs
or whatever, you'd go into alternate screen mode. On exiting the editor or pager, you'd return to the normal screen: the alternate screen text would vanish and you'd see what you'd been seeing before you started the editor.
The less
command, so named because "less is more", was Mark Nudelman's answer to the problem of the original more
refusing to let you scroll backwards. As a fancier version of more
, less
has many options. And, because this alternate-screen-switching behavior annoys many users (including myself), less
has a command-line option to disable sending the ti
and te
(termcap) or smcup
/rmcup
(terminfo) strings, which is where the alternate screen sequence is normally embedded in setups that use the alternate screen.
This command-line option is -X
and setting -X
in less
means that your output does not evaporate when you exit the less
program. Setting X
(which is the opposite of -X
of course) in less
enables the ti/te / smcup/rmcup sequences, so that you do get the alternate screen.
(See also Exorcising the Evil Alternate Screen.)
What does this have to do with Git?
OK, so we now know that:
less
is a pager;less
hasX
and-X
options to fiddle with the setup and teardown of "pager mode" in the terminal emulator; and- the terminal emulator may emulate an alternate screen.
We also know that Git likes to use a pager. The pager Git will run is:
$GIT_PAGER
from the environment variables, orcore.pager
from the configuration, or$PAGER
from the environment variables, or- the built-in pager set by whoever compiled your release of Git.
If you have not set any of the first three, Git falls back on the last one. That's very commonly less
. So if you have not taken any action on your own, Git may run less
as the pager, whenever Git's output is going to a "tty" device such as your ordinary terminal emulator window in which you're running bash
or zsh
or whatever shell you prefer.1
OK, but—so what? By default, doesn't less
use X
and therefore use the alternate screen? Yes, by default, less
has X
set. But it also has F
, R
, and so on. The people who define the Git defaults don't like these defaults. Now we get into some oddities with the less
program, where it's different from most other Unix-style programs.
1If you're not familiar with the term shell, that's another long explanation. I'll defer here to Wikipedia.
Less oddities and a weird Git interaction
Most Unix-oriented programs take their default settings from an "rc" or other configuration file, such as .bashrc
or .gitconfig
. These rc and configuration files are often found in your personal home directory $HOME
: yet another environment variable. So you might expect less
to read $HOME/.lessrc
or some such.
That's not what it does. Instead, less
looks for default settings in an environment variable, $LESS
. The less
program assumes that you will put:
export LESS=X
in your .bashrc
, for instance, if you want it to default to using -X
mode. (Note that the -
in $LESS
is implied: to force X
mode you can use - X
on the command line and X
in the environment variable. Command line settings override environment variable settings.)
Git adds a rather strange wrinkle here. Perhaps it should not, but it does, and if you're going to use Git, you therefore need to know about it. In particular, before Git runs your pager—regardless of what your pager is—Git makes sure that $LESS
is set to something. If LESS
is not present in the environment variables that Git will pass on to the pager, Git sets LESS
to the explicit setting FRX
.
The -F
option has a long name: --quit-if-one-screen
. Essentially, less
counts the output lines, and if the output fits in a single "screen", less will print the output and exit immediately if this option is set. This makes the output from, e.g., git branch
just get printed and stay there and less
exits and then git branch
exits as well, when there is just a single branch, or a small number of branches that easily fit on your screen.
It is as if Git had not run any pager at all—except that if you have a setup that uses an alternate screen, what happens is that the list of branches goes into the alternate screen, which gets displayed for a fraction of a millisecond—perhaps not at all depending on how fast your computer is—and then the alternate screen display evaporates and you see just your shell prompt:
$ git branch
$
Apparently there are no branches at all!
To avoid this problem, Git's default setting includes the X
option, which—as discussed above—avoids sending the ti/te or smcup/rmcup sequences, which avoids activating the alternate screen, so that you see:
$ git branch
* main
$
which is what you'd expect. No output ever went to any alternate screen and all is well.
The last option that Git includes by default is R
. The less
command actually has two -r
options, one lowercase and one uppercase. The long form of these is --raw-control-chars
or --RAW-CONTROL-CHARS
. Both of these instruct less
to "pass through" certain control and escape sequences. The -R
version in particular allows Git to send the ESC [ ... m sequences that cause the terminal emulator to display text in particular colors, for instance. Since Git will, under various conditions, use this to (e.g.) color the current branch in green, Git needs the R
option set.
Git therefore sets this FRX
setting, but only if you have not set your own setting. If you set your own setting, that one takes precedence.
Of course, if you supply command-line options via core.pager
or $GIT_PAGER
, those take precedence as well.
One other note
Besides setting LESS=FRX
, Git also sets LV=-c
. This is for the lv
program. Git uses the same pattern here: if you have your own LV
setting, Git leaves it alone.