reversed(top()) code tags rss about

How to make tabs visible in git diff

June 22, 2014
[git] [bash] [terminal] [howto] [less] [shell]

I’m a bit tired looking at diffs like the following:

         for (int i = 0; i < 3; ++i) {
-        for (int h = 0; h < 4; ++h) {
+                for (int h = 0; h < 4; ++h) {
                         for (int k = 0; k < 4; ++k) {

Can anybody tell whether spaces were replaces with tabs or tabs were replaced with spaces without knowing anything else about the code? Probably, not.

This made me think of making tabs visible. Tried this answer to question Show special characters in Unix while using ‘less’ command, but:

  • less is not able to show tabs in a way I’d like it;
  • cat shows special characters in a way that makes output of git diff hard to read and also breaks coloring.

Instead something like the following settings in Vim would be perfect:

set list
set listchars=tab:.\ ,trail:·

Trailing spaces in changed lines (trail:·) are already highlighted in a noticeable way by git (unless core.whitespace contains -trailing-space) and can be left as they are. The most interesting part about tab:.\ is that it’s really easy to do with sed! It’s as simple as this (assuming that \t is displayed as 8 spaces):

git diff --color | sed 's/\t/.       /g' | less -R

Result looks like this:

 .       for (int i = 0; i < 3; ++i) {
-        for (int h = 0; h < 4; ++h) {
+.       .       for (int h = 0; h < 4; ++h) {
 .       .       .       for (int k = 0; k < 4; ++k) {

That’s way better. The only difficulty that needs to be solved is how to embed sed invocation into regular calls of git without redefining all git commands.

One way is to redefine pager command used by git in configuration file (~/.gitconfig or ~/.config/git/config):

[core]
    pager = sed 's/\t/.       /g' | less -R

Advantage of this approach is that git will substitute tabs only when pager is used. This means that output of git diff > change.patch won’t be affected in any way.

Disadvantage of this approach is that if any other git command will output tab character, it will be substituted as well. In my tests this didn’t happen, so I’ll leave it as it is.