169

Say I get a patch created with git format-patch. The file is basically a unified diff with some metadata. If I open the file in Vim, I can see which lines have been modified, but I cannot see which characters in the changed lines differ. Does anyone know a way (in Vim, or some other free software that runs on Ubuntu) to visualize per-character differences?

A counter example where per-character diff is visualized is when executing vimdiff a b.

update Fri Nov 12 22:36:23 UTC 2010

diffpatch is helpful for the scenario where you're working with a single file.

update Thu Jun 16 17:56:10 UTC 2016

Check out diff-highlight in git 2.9. This script does exactly what I was originally seeking.

4
  • This might be better on superuser.com Commented Jul 12, 2010 at 20:10
  • 17
    Perhaps. I chose stackoverflow.com since the FAQ mentions this is the place for questions about "software tools commonly used by programmers" Commented Jul 13, 2010 at 2:53
  • 8
    I'm not sure that this directly answers your question, but git diff --color-words is very useful for just seeing what words have change within lines, rather than the usual unified diff output. It is word-based rather than character-based, though, so if there's not much whitespace in the content you're diffing then the output may be less neat. (Edited: Oops, I see that I misunderstood what you're asking for - nevertheless maybe this comment would be useful to someone.) Commented Jul 15, 2010 at 9:04
  • Related stackoverflow.com/q/49278577/72178 Commented Jan 18, 2022 at 16:17

8 Answers 8

222

In git, you can merge without committing. Merge your patch first, then do:

git diff --word-diff-regex=. 

Note the dot after the equals sign.

Sign up to request clarification or add additional context in comments.

13 Comments

Better: git diff --color-words=..
@ntc2 You should make your comment an answer.
Upvoters please note, my original use case assumes you only have a patch file, no git repo or even base/modified versions. That's why I accepted @legoscia's answer... it describes exactly what was requested.
@ntc2 git diff --color-words=. and git diff --color-words . works differently. Better is git diff --color-words ..
@abhisekp: thanks for the pic. I think I figured it out: the git diff --color-words . is really the same as git diff --color-words -- .! I.e., the . is interpreted as a path. You can verify with mkdir x y; echo foo > x/test; git add x/test; git commit -m test; echo boo > x/test; cd y; git diff --color-words=.; git diff --color-words .; git diff --color-words -- ..
|
189

Here are some versions with less noisy output than git diff --word-diff-regex=<re> and that require less typing than, but are equivalent to, git diff --color-words --word-diff-regex=<re>.

Simple (does highlight space changes):

git diff --color-words 

Simple (highlights individual character changes; does not highlight space changes):

git diff --color-words=. 

More complex (does highlight space changes):

git diff --color-words='[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+' 

In general:

git diff --color-words=<re> 

where <re> is a regexp defining "words" for the purpose of identifying changes.

These are less noisy in that they color the changed "words", whereas using just --word-diff-regex=<re> surrounds matched "words" with colored -/+ markers.

10 Comments

I myself like --color-words, without the =. part.
git diff --color-words='\w' would work better with diacritics (git v1.7.10.4)
Your more complex version works great. I appended --word-diff=plain to additionally have [- and -] surround deletions and {+ and +} surround additions. As the manual warns, though, actual occurrences of these delimiters in the source are not escaped in any way
Your more complex version unfortunately doesn't seem to highlight e.g. indentation changes, I've opened a question on this
This answer is great! However is there a way to actually change the background of those changes to green/red?
|
51
git diff --color-words="[^[:space:]]|[[:alnum:]]+" 

The above regex (from Thomas Rast) does a decent job of separating diff fragments at the punctuation/character level (while not being as noisy as --word-diff-regex=.).

I posted a screenshot of the resulting output here.


Update:

This article has some great suggestions. Specifically, the contrib/ tree of the git repo has a diff-highlight perl script that shows fine-grained highlights.

Quick start to use it:

$ curl https://git.kernel.org/cgit/git/git.git/plain/contrib/diff-highlight/diff-highlight > diff-highlight $ chmod u+x diff-highlight $ git diff --color=always HEAD~10 | diff-highlight | less -R 

16 Comments

You can shorten it to --color-words=[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+'
@gcb The text content matters. If your changes are separated by whitespace, there's no difference. But if you change if you change something like foo.bar to foo.qux you will see the difference.
Simpler: git diff --color-words='[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+'.
I had installed git with Homebrew and already had that script at /usr/local/share/git-core/contrib/diff-highlight/diff-highlight . This seems to suggest that Homebrew's git does install the entire contrib in /usr/local/share/git-core/contrib/. So finally, the following worked for me git diff --color=always | /usr/local/share/git-core/contrib/diff-highlight/diff-highlight
Has anyone witnessed a clear, reproducible effect of including that final string literal, "|UTF_8_GUARD" ? I think it's just been "tagging along" this whole time, with no harm done, but no benefit. I can see it's been copied many times from here and there on stackexchange, but it's never explained or acknowledged. None of the old links here (read using wayback machine) seem to use that text, at all. I'd have expected the "UTF_8_GUARD" to have shown up in a man page or some source code, somewhere, but it wasn't matching anywhere.
|
16

Given your references to Vim in the question, I'm not sure if this is the answer you want :) but Emacs can do this. Open the file containing the diff, make sure that you're in diff-mode (if the file is named foo.diff or foo.patch this happens automatically; otherwise type M-x diff-mode RET), go to the hunk you are interested in and hit C-c C-b for refine-hunk. Or step through the file one hunk at a time with M-n; that will do the refining automatically.

3 Comments

Works for me! Heh, I've used Vim for 10 years, but I just installed emacs. :)
But emacs doesn't support reading from stdin, I can't do e.g. git log master.. -p | emacs -
@Hi-Angel You could open Emacs and type M-! to run the command and capture the output in a buffer.
7

If you have nothing against installing NodeJS, there's a package called "diff-so-fancy" (https://github.com/so-fancy/diff-so-fancy), which is very easy to install and works perfectly:

npm install -g diff-so-fancy git diff --color | diff-so-fancy | less -R 

Edit: Just found out it's actually a wrapper for the official diff-highlight... At least it's easier to install for perlophobes like me and the GitHub page is nicely documented :)

Comments

2

Am not aware of per character difference tool, but there is a per word difference tool: wdiff.

refer examples Top 4 File Difference Tools on UNIX / Linux – Diff, Colordiff, Wdiff, Vimdiff.

3 Comments

wdiff is interesting, thanks! To clarify my original question, I'm looking for something that provides enhanced syntax highlighting for a single file that happens to be in unified diff format.
Slightly offtopic (about word-for-word diffs, not enhancing a preexisting diff output), but I've found the following combinations best for word-for-word visualizations: * wdiff old_file new_file | cdiff * vimdiff , then inside vim :windo wincmd K in order to switch to vertical window layout (one below the other) from the side by side one. That layout is much better for files with long lines.
BTW, Some other tools worth checking out, not mentioned in the linked article: wdiff2, mdiff, and the Google's online tool.
1

After a little research, I notice this question has come up twice recently on the main Vim mailing list. The NrrwRgn plugin was mentioned both times (make two narrow regions and diff them). Using NrrwRgn as described by Christian Brabandt feels more like a workaround than a solution, but maybe that's good enough.

I tried out NrrwRgn and it, together with :diffthis, was indeed useful for illustrating per-character differences within parts of a single file. But it took many keystrokes. My Vimscript is pretty rusty, but it could likely be scripted. Maybe NrrwRgn could be enhanced to provide the desired functionality.

Thoughts?

Comments

1

diffr is my tool of choice now.

example diff

Installation on Windows:

  1. winget install -e --id Rustlang.Rustup
  2. cargo install diffr
  3. git config --global core.pager "diffr | less -R"
  4. git config --global interactive.difffilter diffr

In case there are issues with less: winget install jftuga.less

1 Comment

Cool, but it still requires two files. OP was asking about highlighting for a single patch file, not two individual files with differences. See the OP and accepted answer. I also concede this question has become a search result for visualizing differences between multiple files, regardless of my original question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.