Home > Software design >  How to clear git log pager output after closing?
How to clear git log pager output after closing?

Time:10-15

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 has X 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, or
  • core.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.

  •  Tags:  
  • git
  • Related