608

When I have a diff, how can I colorize it so that it looks good?

I want it for the command line, so please no GUI solutions.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
daniel kullmann
  • 13,653
  • 8
  • 51
  • 67

17 Answers17

700

Man pages for diff suggest no solution for colorization from within itself. Please consider using colordiff. It's a wrapper around diff that produces the same output as diff, except that it augments the output using colored syntax highlighting to increase readability:

diff old new | colordiff

or just:

colordiff old new

Installation:

  • Ubuntu/Debian: sudo apt-get install colordiff
  • OS X: brew install colordiff or port install colordiff
Dror
  • 12,174
  • 21
  • 90
  • 160
kaji
  • 7,709
  • 1
  • 20
  • 17
385

Use Vim:

diff /path/to/a /path/to/b | vim -R -

Or better still, VimDiff (or vim -d, which is shorter to type) will show differences between two, three or four files side-by-side.

Examples:

vim -d /path/to/[ab]

vimdiff file1 file2 file3 file4
Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • function ddiff(){ diff $1 $2 | vim -R - } export -f ddiff – Jichao Aug 20 '13 at 22:28
  • 8
    @Jichao: I prefer to learn the commands rather than alias them away. That way I can use them anywhere, even when my dotfiles aren't available. – johnsyweb Aug 21 '13 at 01:59
  • vim -d has the advantage of remembering the last open position, if correct vimrc setting is used. (Such as [here](http://vim.wikia.com/wiki/Restore_cursor_to_file_position_in_previous_editing_session).) –  Oct 04 '13 at 00:07
  • looks good! I just dont understand why they dont allow ctrl+c or ctrl+q or ctrl+x on vim to exit; I always have to look on internet to quit it hehe.. – Aquarius Power Jun 21 '14 at 17:41
  • 1
    @AquariusPower: [`ctrl-c`](http://stackoverflow.com/q/5030164/78845) and [`ctrl-x`](http://stackoverflow.com/q/13327285/78845) have other uses in Vim. [`ctrl-q`](http://stackoverflow.com/a/7883860/78845) is captured by many terminals. See [Writing and quitting](http://vimdoc.sourceforge.net/htmldoc/editing.html#write-quit) to find the way that best suits your needs. – johnsyweb Jun 22 '14 at 00:51
  • 1
    `vimdiff` won't handle directory `diff`s. Alternatively, you can save diff to a file, and open it with `vim`. That way you will have `vim` colorize it using built-in syntax highlighting facilities. – x-yuri May 04 '15 at 13:58
  • @x-yuri. Yes... you could use your shell to do this too, `vim -d =(ls -l ~/src/m) =(ls -l ~/src/n)` – johnsyweb May 05 '15 at 05:13
  • 1
    First, what kind of shell is this? `zsh`? I don't recognize `=(...)` construct. Second, I had `diff -ur a b` in mind. – x-yuri May 05 '15 at 09:47
  • 3
    Indeed. In Zsh, a [command of the form `=(...)` is replaced with the name of a _file_ containing its output.](http://zsh.sourceforge.net/Intro/intro_7.html) – johnsyweb May 05 '15 at 23:07
  • `diff -ur ~/src/{m,n} | vim -` is a nice alternative. Thanks. – johnsyweb May 05 '15 at 23:09
  • @Johnsyweb and @x-yuri - in Bash it's `<(...)` instead. – Brian McCutchon Jan 07 '16 at 06:32
  • @BrianMcCutchon The `<(...)` form is present in zsh as well. – Victor Zamanian Mar 18 '16 at 11:08
  • Actually for me it's shortest to type `vimd` + `tab` (autocomplete). – Lindlof Feb 09 '17 at 08:32
  • 1
    This solution is better than the accepted answer, as it does not require you to have system administrator rights and install any additional tool – amanzoor Aug 12 '19 at 10:01
  • Adding a note that `-R` is vim's readonly mode so you can also use the equivalent `view` command which is just vim's readonly command, i.e., `diff file1 file 2 | view -`. Adding it here for those that may prefer one over the other. Cool solution, OP! – John Pancoast Sep 19 '21 at 19:41
203

Actually there seems to be yet another option (which I only noticed recently, when running into the problem described above):

git diff --no-index <file1> <file2>
# output to console instead of opening a pager
git --no-pager diff --no-index <file1> <file2>

If you have Git around (which you already might be using anyway), then you will be able to use it for comparison, even if the files themselves are not under version control. If not enabled for you by default, then enabling color support here seems to be considerably easier than some of the previously mentioned workarounds.

Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
Lars Baehren
  • 2,041
  • 1
  • 12
  • 2
152

diff --color option (added to GNU diffutils 3.4 in 2016-08-08)

This is the default diff implementation on most distributions, which will soon be getting it.

Ubuntu 18.04 (Bionic Beaver) has diffutils 3.6 and therefore has it.

On 3.5 it looks like this:

Enter image description here

Tested with:

diff --color -u \
  <(seq 6 | sed 's/$/ a/') \
  <(seq 8 | grep -Ev '^(2|3)$' | sed 's/$/ a/')

Apparently added in commit c0fa19fe92da71404f809aafb5f51cfd99b1bee2 (Mar 2015).

Word-level diff

Like diff-highlight. It is not possible it seems, but there is a feature request: https://lists.gnu.org/archive/html/diffutils-devel/2017-01/msg00001.html

Related questions:

ydiff does it though. See below.

ydiff side-by-side word level diff

https://github.com/ymattw/ydiff

Is this nirvana?

python3 -m pip install --user ydiff
diff -u a b | ydiff -s

Outcome:

Enter image description here

If the lines are too narrow (default 80 columns), fit to the screen with:

diff -u a b | ydiff -w 0 -s

Contents of the test files:

a

1
2
3
4
5 the original line the original line the original line the original line
6
7
8
9
10
11
12
13
14
15 the original line the original line the original line the original line
16
17
18
19
20

b

1
2
3
4
5 the original line the original line the original line the original line
6
7
8
9
10
11
12
13
14
15 the original line the original line the original line the original line
16
17
18
19
20

ydiff Git integration

ydiff integrates with Git without any configuration required.

From inside a Git repository, instead of git diff, you can do just:

ydiff -s

and instead of git log:

ydiff -ls

See also: How can I get a side-by-side diff when I do "git diff"?

Tested on Ubuntu 16.04 (Xenial Xerus), Git 2.18.0, and ydiff 1.1.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
79

And for those occasions when a yum install colordiff or an apt-get install colordiff is not an option due to some insane constraint beyond your immediate control, or you're just feeling crazy, you can reinvent the wheel with a line of sed:

sed 's/^-/\x1b[41m-/;s/^+/\x1b[42m+/;s/^@/\x1b[34m@/;s/$/\x1b[0m/'

Throw that in a shell script and pipe unified diff output through it.

It makes hunk markers blue and highlights new/old filenames and added/removed lines in green and red background, respectively.1 And it will make trailing space2 changes more readily apparent than colordiff can.


1 Incidentally, the reason for highlighting the filenames the same as the modified lines is that to correctly differentiate between the filenames and the modified lines requires properly parsing the diff format, which is not something to tackle with a regex. Highlighting them the same works "well enough" visually and makes the problem trivial. That said, there are some interesting subtleties.

2 But not trailing tabs. Apparently tabs don't get their background set, at least in my xterm. It does make tab vs. space changes stand out a bit though.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
retracile
  • 12,167
  • 4
  • 35
  • 42
  • 5
    @Matt: Here's a brute-force approach for a Mac: `sed "s/^-/\`echo -e \"\x1b\"\`[41m-/;s/^+/\`echo -e \"\x1b\"\`[42m+/;s/^@/\`echo -e \"\x1b\"\`[34m@/;s/$/\`echo -e \"\x1b\"\`[0m/"` (though I expect there is a better way). – retracile Aug 23 '13 at 14:34
  • 1
    Hmm, it sort of worked... gave the 3 dashes between each chunk a pink background. – Matt Montag Aug 23 '13 at 22:43
  • Manual unreadable, unmaintainable way without an option to easily change the colors for example. –  Oct 04 '13 at 00:03
  • @Svetlana I think you're taking this a bit too seriously... :) – retracile Oct 05 '13 at 17:09
  • 2
    Dude this is awesome! Way to go! That is some nice sed wizardry. – fthinker Oct 17 '13 at 00:44
  • 1
    aliased this awesomeness as colorize-diff-u. Thanks. – Marty Neal Mar 20 '14 at 22:32
  • 6
    sed 's/^-/\x1b[31m-/;s/^+/\x1b[32m+/;s/^@/\x1b[34m@/;s/$/\x1b[0m/' looks also great – Yura May 07 '15 at 11:20
  • Particularly, this answer by retracile has inspired me to develop the bash function as wrapper over the original diff tool. I have briefly described it here - http://with-love-from-siberia.blogspot.ru/2016/07/color-diff.html. I have made attempt to cover all the possible formatting options. Sadly, it has some lacks described by retracile in his article but those lacks are quite rare. So I can say this it works properly. – jsxt Aug 08 '16 at 15:07
  • There's always a case for doing something without involving yum, apt-get, etc. -- something as simple as not wanting to bother IT with installing yet another program that only one developer will use. – Posco Grubb Feb 10 '17 at 20:29
  • Thanks! You can also use `sed -e '1,2s/^/\x1b[97m/;3,$s/^-/\x1b[31m-/;3,$s/^+/\x1b[32m+/;3,$s/^@/\x1b[36m@/;s/$/\x1b[0m/'` for an output closer to `diff --color`. – tricasse Jan 09 '18 at 16:09
  • Here is another solution that works on macOS and does not require spelling out the ANSI escape sequences explicitly: `diff -u old new | sed "s/^-/$(tput setaf 1)&/; s/^+/$(tput setaf 2)&/; s/^@/$(tput setaf 6)&/; s/$/$(tput sgr0)/"`. See https://stackoverflow.com/a/55088585/303363 for more details. – Susam Pal Mar 10 '19 at 14:18
18

Coloured, word-level diff ouput

Here's what you can do with the the below script and diff-highlight:

Coloured diff screenshot

#!/bin/sh -eu

# Use diff-highlight to show word-level differences

diff -U3 --minimal "$@" |
  sed 's/^-/\x1b[1;31m-/;s/^+/\x1b[1;32m+/;s/^@/\x1b[1;34m@/;s/$/\x1b[0m/' |
  diff-highlight

(Credit to @retracile's answer for the sed highlighting)

αғsнιη
  • 2,627
  • 2
  • 25
  • 38
Tom Hale
  • 40,825
  • 36
  • 187
  • 242
  • How do I use this output in GVim? – Hemant Sharma Jan 06 '17 at 06:04
  • For `vim`, use a plugin, eg [diffchar](https://github.com/rickhowe/diffchar.vim). – Tom Hale Jan 11 '17 at 05:13
  • This answer deserves more credit! It uses a toolchain you probably already have, and works awesome (and applies to git log, git show, and git diff! – Jesse Brandeburg Jul 16 '21 at 22:42
  • Actually, `diff-highlight` does not do a word-level output. It just detects a common prefix and a common suffix on a changed line. For instance, if several words are changed, it will highlight everything from the first changed word to the last changed word. Moreover, this is effective only when only one line is changed between unchanged lines. In GNU Emacs, diff-mode does much better. – vinc17 Mar 11 '22 at 23:43
16

You can change the Subversion configuration to use colordiff:

~/.subversion/config.diff

 ### Set diff-cmd to the absolute path of your 'diff' program.
 ###   This will override the compile-time default, which is to use
 ###   Subversion's internal diff implementation.
-# diff-cmd = diff_program (diff, gdiff, etc.)
+diff-cmd = colordiff

via: https://gist.github.com/westonruter/846524

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Azd325
  • 5,752
  • 5
  • 34
  • 57
11

I use grc (Generic Colouriser), which allows you to colour the output of a number of commands including diff.

It is a Python script which can be wrapped around any command. So instead of invoking diff file1 file2, you would invoke grc diff file1 file2 to see colourised output. I have aliased diff to grc diff to make it easier.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dogbane
  • 266,786
  • 75
  • 396
  • 414
10

Here is another solution that invokes sed to insert the appropriate ANSI escape sequences for colors to show the +, -, and @ lines in red, green, and cyan, respectively.

diff -u old new | sed "s/^-/$(tput setaf 1)&/; s/^+/$(tput setaf 2)&/; s/^@/$(tput setaf 6)&/; s/$/$(tput sgr0)/"

Unlike the other solutions to this question, this solution does not spell out the ANSI escape sequences explicitly. Instead, it invokes the tput setaf and tput sgr0 commands to generate the ANSI escape sequences to set an appropriate color and reset terminal attributes, respectively.

To see the available colors for each argument to tput setaf, use this command:

for i in {0..255}; do tput setaf $i; printf %4d $i; done; tput sgr0; echo

Here is how the output looks:

enter image description here

Here is the evidence that the tput setaf and tput sgr0 commands generate the appropriate ANSI escape sequences:

$ tput setaf 1 | xxd -g1
00000000: 1b 5b 33 31 6d                                   .[31m
$ tput setaf 2 | xxd -g1
00000000: 1b 5b 33 32 6d                                   .[32m
$ tput setaf 6 | xxd -g1
00000000: 1b 5b 33 36 6d                                   .[36m
$ tput sgr0 | xxd -g1
00000000: 1b 28 42 1b 5b 6d                                .(B.[m
Susam Pal
  • 32,765
  • 12
  • 81
  • 103
  • if you get `tput: unkown terminfo capability in 'srg0'` means your os is old and tput does not recognize srg0 , i was able to use `tput setaf 7` in last sed command to get color back to white – niken Nov 25 '21 at 15:28
9

Since wdiff accepts arguments specifying the string at the beginning and end of both insertions and deletions, you can use ANSI color sequences as those strings:

wdiff -n -w $'\033[30;41m' -x $'\033[0m' -y $'\033[30;42m' -z $'\033[0m' file1 file2

For example, this is the output of comparing two CSV files:

diff output of CSV files

Example from 2.2 Actual examples of wdiff usage.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jcomeau_ictx
  • 37,688
  • 6
  • 92
  • 107
5

No one has mentioned delta so far. It supports syntax colored diff view with syntax highlighting.

Image source: Delta

Jongwook Choi
  • 8,171
  • 3
  • 25
  • 22
4

I would suggest you to give diff-so-fancy a try. I use it during my work and it sure seems great as of now. It comes packed with many options and it's really easy to configure your diffs the way you want.

You can install it by:

sudo npm install -g diff-so-fancy

or on Mac:

brew install diff-so-fancy

Afterwards, you can highlight your diffs like this:

diff -u file1 file2 | diff-so-fancy
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
Naveen
  • 85
  • 8
4

Character-level color diff: Install ccdiff

ccdiff -r /usr/share/dict/words /tmp/new-dict

Output of ccdiff

apricot
  • 3,599
  • 3
  • 19
  • 20
  • 1
    `ccdiff` seems to work well as a diff program, but unfortunately, it cannot color an existing diff (such as a patch). – vinc17 Mar 11 '22 at 23:46
2

With the bat command:

diff file1 file2 | bat -l diff
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Weihang Jian
  • 7,826
  • 4
  • 44
  • 55
  • 1
    What is *bat* supposed to do? Can you elaborate? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/58573534/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Aug 11 '21 at 13:23
1
diff --color=always file_a file_b | less

works for me

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
0

On recent versions of Git on Ubuntu, you can enable diff-highlighting with:

sudo ln -s /usr/share/doc/git/contrib/diff-highlight/diff-highlight /usr/local/bin
sudo chmod a+x /usr/share/doc/git/contrib/diff-highlight/diff-highlight

And then adding this to your .gitconfig file:

[pager]
    log = diff-highlight | less
    show = diff-highlight | less
    diff = diff-highlight | less

It's possible the script is located somewhere else in other distributions. You can use locate diff-highlight to find out where.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
naught101
  • 18,687
  • 19
  • 90
  • 138
0

My favorite choice is vdiff <file1> <file2> function (I forgot from where I got it).

It will open two windows in Vim side-by-side, to see clearly see the difference between the two files.

vdiff () {
    if [ "${#}" -ne 2 ] ; then
        echo "vdiff requires two arguments"
        echo "  comparing dirs:  vdiff dir_a dir_b"
        echo "  comparing files: vdiff file_a file_b"
        return 1
    fi

    local left="${1}"
    local right="${2}"

    if [ -d "${left}" ] && [ -d "${right}" ]; then
        vim +"DirDiff ${left} ${right}"
    else
        vim -d "${left}" "${right}"
    fi
}

Put this script in your (.alias) or (.zshrc), and then call it using vdiff <file1> <file2>.

Example

Enter image description here

The results are:

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dr Neo
  • 724
  • 6
  • 11