Search code examples
gitterminalcolorsescapingtext-files

Why do "git diff" and "git diff --color-words" get colored output differently?


If I do git diff I get outputs like this:

enter image description here

but when I add the option --color-words, the output looks like this

enter image description here

Clearly the difference is due to what --color-words does.

But I would have guessed that the colors in the terminal, in both cases, are rendered the same way from the escape sequence perspective.

However, that doesn't seem the case. Here's what the two commands produce when redirecting their output to a file:

$ git diff src/packages.tex > log1
$ git diff --color-words src/packages.tex > log2

log1

diff --git a/src/packages.tex b/src/packages.tex
index acb4a7b..20b0624 100644
--- a/src/packages.tex
+++ b/src/packages.tex
@@ -11,6 +11,7 @@
 \usepackage[english, russian]{babel}
 
 % questi vanno in qualsiasi ordine
+\usepackage{attrib}
 \usepackage{booktabs}
 \usepackage{cancel}
 \usepackage{dtk-logos}
@@ -21,7 +22,7 @@
 \usepackage{graphicx}
 \usepackage{imakeidx}
 \usepackage{indentfirst}
-\usepackage[left, pagewise]{lineno}
+%\usepackage[left, pagewise]{lineno}
 \usepackage{lipsum}
 \usepackage{luacolor}
 \usepackage{makecell}
@@ -37,6 +38,7 @@
 \usepackage{tikz}
 \usepackage{tikzscale}
 \usepackage{tipa}
+\usepackage{varwidth}
 \usepackage{verse}
 \usepackage{xcolor}
 \usepackage{xfp}

log2

^[[34mdiff --git a/src/packages.tex b/src/packages.tex^[[m
^[[34mindex acb4a7b..20b0624 100644^[[m
^[[34m--- a/src/packages.tex^[[m
^[[34m+++ b/src/packages.tex^[[m
^[[36m@@ -11,6 +11,7 @@^[[m
^[[37m\usepackage[english, russian]{babel}^[[m

^[[37m% questi vanno in qualsiasi ordine^[[m
^[[1;32m\usepackage{attrib}^[[m
^[[37m\usepackage{booktabs}^[[m
^[[37m\usepackage{cancel}^[[m
^[[37m\usepackage{dtk-logos}^[[m
^[[36m@@ -21,7 +22,7 @@^[[m
^[[37m\usepackage{graphicx}^[[m
^[[37m\usepackage{imakeidx}^[[m
^[[37m\usepackage{indentfirst}^[[m
^[[1;32m%^[[m^[[37m\usepackage[left, pagewise]{lineno}^[[m
^[[37m\usepackage{lipsum}^[[m
^[[37m\usepackage{luacolor}^[[m
^[[37m\usepackage{makecell}^[[m
^[[36m@@ -37,6 +38,7 @@^[[m
^[[37m\usepackage{tikz}^[[m
^[[37m\usepackage{tikzscale}^[[m
^[[37m\usepackage{tipa}^[[m
^[[1;32m\usepackage{varwidth}^[[m
^[[37m\usepackage{verse}^[[m
^[[37m\usepackage{xcolor}^[[m
^[[37m\usepackage{xfp}^[[m

(In the latter, the ^[ is the escape character, which I'd obtain in Vim by typing Ctrl+VEscape, for instance.)

Why does git diff use no escape sequences for the color and still gets colored output and git diff --color-words uses escape sequence to render the same colors?


Solution

  • git diff’s color output is controlled by color.ui and color.diff.

    git diff --color-words does this according to man git diff:

    • --color-words is equivalent to --word-diff=color
    • In turn the mode color (the argument to the switch) implies --color
    • --color means --color=always [1]

    So you are running git diff --color-words --color=always.

    Back to git diff: the default for color.ui is true, or auto. Which means that the output is colored if the output is a terminal.

    This means that you are running:

    git diff --color=auto
    

    Which does not print any escape sequences if the output is a file. [2]

    Meanwhile your other command:

    git diff --color-words --color=always
    

    Will always print escape sequences, even when the output is a file.

    Notes

    1. always means that the the output is always colored, no matter where the output is going (file or terminal); c.f. auto which colors when the output is a terminal but not if it is a file
    2. It seems that Git uses isatty(3): “test whether a file descriptor refers to a terminal”. It seems that it also uses the same thing in order to test whether it should call the pager for long output.