How to use --color-words with git add --patch?
GitGit Problem Overview
When diffing files, I prefer to use git diff --color-words
. Is there a way to make this the default format for diffs when using git add --patch
or git add --interactive
?
Git Solutions
Solution 1 - Git
Building off of what VonC said:
Starting with Git 2.9, you can use this command to color words during add --patch
:
git -c interactive.diffFilter="git diff --color-words" add -p
This sets the interactive.diffFilter
variable for the call to add -p
without affecting further calls. For me this is ideal because I usually want to run add -p
normally, but sometimes want to run it with --color-words
.
You can easily add an alias for this command like so:
git config --global alias.addcw '-c interactive.diffFilter="git diff --color-words" add -p'
Solution 2 - Git
Taking cue from VonC's answer. Here are detailed steps to use --interactive
option introduced in git 2.9.
Add diff-highlight to your PATH.
On Ubuntu, diff-highlight
comes with git and can be found in /usr/share/git/diff-highlight/diff-highlight
.
Otherwise, you can download and set it up manually.
cd ~/bin curl -LO "https://raw.githubusercontent.com/git/git/master/contrib/diff-highlight/diff-highlight" chmod u+x diff-highlight
Restart your shell, if necessary.
Then configure Git to filter your diffs whenever it's showing them in a pager:
git config --global pager.log 'diff-highlight | less'
git config --global pager.show 'diff-highlight | less'
git config --global pager.diff 'diff-highlight | less'
git config --global interactive.diffFilter diff-highlight
This will put an extra emphasis on the changed part of a line, which is almost same as --word-diff
.
The advantage is you get word diff every where, like git log --patch
or git add -p
.
Solution 3 - Git
I recently solved this issue, but it requires modifying a Perl script in git. That's easy and requires no special skill, however.
This solution requires that your git configuration use colorization for screen output, because that's the only circumstance under which git will show a word-based diff.
- Copy
git-add--interactive
from your installation to somewhere in yourPATH
environment variable and rename itgit-add--interactive-words
. - Edit a line about half way down to change*
@colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
to
@colored = run_cmd_pipe("git", @diff_cmd, qw(--color --color-words --), $path);
3. You can now run git add-interactive--words
to do the equivalent of git add --interactive
with colorized word-based diff.
4. However, combining git add --patch
with that is awkward because you need to pass the new script the right parameters. Fortunately, you can create an alias to the magic words in your .gitconfig
:
[alias]
iaddpw = add--interactive-words --patch=stage --
which means git iaddpw
runs the equivalent of git add --interactive --patch
with colorized word-based diff.
*- For Git 2.18, this command is:
my @display_cmd = ("git", @diff_cmd, qw(--color --), $path);
Solution 4 - Git
With git 2.9 (June 2016), you will have a new option: interactive.diffFilter
.
See commit 0114384 (27 Feb 2016) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit 2052c52, 03 Apr 2016)
> ## add --interactive
: allow custom diff highlighting programs
> The patch hunk selector of add--interactive knows how ask git for colorized diffs, and correlate them with the uncolored diffs we apply. But there's not any way for somebody who uses a diff-filter tool like contrib's diff-highlight to see their normal highlighting. > > This patch lets users define an arbitrary shell command to pipe the colorized diff through. The exact output shouldn't matter (since we just show the result to humans) as long as it is line-compatible with the original diff (so that hunk-splitting can split the colorized version, too).
You could then pipe that diff to a diff --color-words
.
As commented by Andrew Dufresne, the GitHub blog post refers to the contrib script contrib/diff-highlight
:
> You can use "--color-words
" to highlight only the changed portions of
lines. However, this can often be hard to read for code, as it loses
the line structure, and you end up with oddly formatted bits.
>
> Instead, this script post-processes the line-oriented diff, finds pairs
of lines, and highlights the differing segments.
The result puts an extra emphasis on the changed part of a line:
Regarding those diffs, "diff-highlight
" filter (in contrib/
) learned to undertand "git log --graph
" output better.
See commit 4551fbb, commit 009a81e, commit fbcf99e, commit 7ce2f4c, commit e28ae50, commit 53ab9f0, commit 5013acc (21 Mar 2018) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit d19e556, 10 Apr 2018)
See more in "diff-highlight
: detect --graph
by indent"
Note: before Git 2.17 (Q2 2018), The "interactive.diffFilter
" used by "git add -i
" must retain one-to-one correspondence between its input and output, but it was not enforced and caused end-user confusion.
We now at least make sure the filtered result has the same number of lines as its input to detect a broken filter.
See commit 42f7d45, commit af3570e (03 Mar 2018) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit c5e2df0, 14 Mar 2018)
With Git 2.30 (Q1 2021), "git add -i
"(man) failed to honor custom colors configured to show patches, which has been corrected.
See commit 96386fa, commit 890b68b, commit 0cb8939, commit afae3cb, commit 6681e36 (16 Nov 2020), commit 25d9e5c, commit c62cd17, commit 6f1a5ca, commit decc9ee (11 Nov 2020), and commit cb581b1, commit d34e450 (10 Nov 2020) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit e0d2568, 08 Dec 2020)
> ## add -p
: prefer color.diff.context
over color.diff.plain
> Signed-off-by: Johannes Schindelin
> Git's diff machinery allows users to override the colors to use in diffs, even the plain-colored context lines. As of 8dbf3eb6850 (diff.h
: rename DIFF_PLAIN
color slot to DIFF_CONTEXT,
2015-05-27, Git v2.4.5), the preferred name of the config setting is color.diff.context
, although Git still allows color.diff.plain
.
>
> In the context of git add -p
(man), this logic is a bit hard to replicate: git_diff_basic_config()
reads all config values sequentially and if it sees any color.diff.context
or color.diff.plain
, it accepts the new color.
The Perl version of git add -p
(man) needs to go through git config --get-color
(man), though, which allows only one key to be specified.
The same goes for the built-in version of git add -p
(man), which has to go through repo_config_get_value()
.
>
> The best we can do here is to look for .context
and if none is found, fall back to looking for .plain
, and if still not found, fall back to the hard-coded default (which in this case is simply the empty string, as context lines are typically rendered without colored).
>
> This still leads to inconsistencies when both config names are used: the initial diff will be colored by the diff machinery.
Once edited by a user, a hunk has to be re-colored by git add -p
(man), though, which would then use the other setting to color the context lines.
>
> In practice, this is not all that bad. The git config
(man) manual says this in the color.diff.<slot>
:
>
> context
(context text - plain
is a historical synonym)
>
> We should therefore assume that users use either one or the other, but not both names.
Besides, it is relatively uncommon to look at a hunk after editing it because it is immediately staged by default.
Solution 5 - Git
Solution
Use diff-highlight | less -FRX --tabs=4
as your diffFilter
:
git -c interactive.diffFilter="diff-highlight | less -FRX --tabs=4" add --patch
For more on diff-highlight
: source, a quick primer
Homebrew
If you're using Homebrew (OS X), you can put the following in your .gitconfig
(to use the already installed diff-highlight
):
[interactive]
diffFilter = "$(git --exec-path | sed 's/libexec/share/')/contrib/diff-highlight/diff-highlight | less -FRX --tabs=4"
1-1 correspondence between input and output
As of git 2.17, the word diff solution must keep a 1-1 correspondence between input and output lines to avoid:
$ git -c interactive.diffFilter="git diff --word-diff --color" add --patch
fatal: mismatched output from interactive.diffFilter
hint: Your filter must maintain a one-to-one correspondence
hint: between its input and output lines.
diff-so-fancy
now supports this (as of v1.4.0) since https://github.com/so-fancy/diff-so-fancy/issues/35 has been closed. See this.
Solution 6 - Git
As mentioned earlier adding diff-highlight
to the interactive.diffFilter
config key is the easiest option (since Git 2.9). The following comand does the trick on Debian/Ubuntu without copying scripts, changing permissions or mangling $PATH:
git config interactive.diffFilter "perl /usr/share/doc/git/contrib/diff-highlight/diff-highlight"
Things like git -c interactive.diffFilter="git diff --color-words" add -p
or git config interactive.diffFilter "git diff --color-words"
don't work properly: add -p
always keeps suggesting the first modified file.
Solution 7 - Git
This tool does it well https://github.com/mookid/diffr
[core]
pager = diffr | less -R
[interactive]
diffFilter = diffr
Solution 8 - Git
This is also possible via delta:
[interactive]
diffFilter = delta --color-only --features=interactive
and via diff-so-fancy:
[interactive]
diffFilter = diff-so-fancy --patch
Solution 9 - Git
In your $(HOME)/.gitconfig file add this
[color]
diff = auto
interactive = auto
This should do.