155

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.

Community
  • 1
  • 1
Adam Monsen
  • 9,054
  • 6
  • 53
  • 82
  • This might be better on superuser.com – Daenyth Jul 12 '10 at 20:10
  • 16
    Perhaps. I chose stackoverflow.com since the FAQ mentions this is the place for questions about "software tools commonly used by programmers" – Adam Monsen Jul 13 '10 at 02: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.) – Mark Longair Jul 15 '10 at 09:04
  • Related https://stackoverflow.com/q/49278577/72178 – ks1322 Jan 18 '22 at 16:17

7 Answers7

209

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.

yingted
  • 9,996
  • 4
  • 23
  • 15
  • @Sairam Thanks, it's fixed. +1 for noticing it. – yingted Apr 13 '12 at 03:00
  • 181
    Better: `git diff --color-words=.`. – ntc2 Nov 06 '13 at 00:53
  • 6
    @ntc2 You should make your comment an answer. – Tyler Collier Sep 02 '14 at 20:42
  • 2
    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. – Adam Monsen Oct 23 '14 at 05:21
  • 2
    @ntc2 `git diff --color-words=.` and `git diff --color-words .` works differently. Better is `git diff --color-words .`. – abhisekp Aug 31 '15 at 14:27
  • @abhisekp: what's the difference? – ntc2 Sep 01 '15 at 19:04
  • @ntc2 Here you go http://i.imgur.com/Fa8vCtO.png But you're right about **Better: `git diff --color-words=.`** – abhisekp Sep 04 '15 at 10:20
  • 2
    @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 -- .`. – ntc2 Sep 04 '15 at 16:43
  • @ntc2 yes. That's a path. I got it. But I liked the way you presented the whole thing in a single like. Nice. +1 – abhisekp Sep 05 '15 at 14:34
  • No solution for `git commit -p`? – Vitaly Zdanevich Oct 04 '18 at 10:56
  • Works with `git log` too, but geez this is ugly. It surrounds differences with square brackets, and confuses whether these are part of the code or not. – Hi-Angel Oct 19 '18 at 10:01
  • @ntc2, `git diff --color-words=.`, or similar, must be the git basis by which `meld` does it's char-by-char highlighting magic (see a screenshot of meld [in my answer here](https://stackoverflow.com/a/50223487/4561887)). I've been wondering abou that for years. – Gabriel Staples Nov 28 '20 at 21:48
  • If you wanted to see changes in last commit, `git show --color-words=.` – Hritik Dec 08 '20 at 11:33
  • I thought this would help me see the indentation edit nicely and it did it for few lines but then... https://i.imgur.com/Ksa4h7Y.png – Nakilon Aug 19 '22 at 23:58
178

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.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
ntc2
  • 11,203
  • 7
  • 53
  • 70
  • 11
    I myself like `--color-words`, without the `=.` part. – Tyler Collier Sep 03 '14 at 03:05
  • 1
    `git diff --color-words='\w'` would work better with diacritics (git v1.7.10.4) – n.r. Jan 16 '15 at 20:49
  • 1
    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 – Tobias Kienzler Dec 08 '15 at 09:14
  • 2
    Your more complex version unfortunately doesn't seem to highlight e.g. indentation changes, I've opened [a question](https://stackoverflow.com/q/34350187/321973) on this – Tobias Kienzler Dec 18 '15 at 07:13
  • 2
    This answer is great! However is there a way to actually change the background of those changes to green/red? – WoLfPwNeR Apr 14 '16 at 23:12
  • @WoLfPwNeR: if you're asking how to make `git diff` change the background colors instead of the foreground colors, this might help: https://github.com/git/git/tree/master/contrib/diff-highlight; I have not tried it myself. – ntc2 Apr 15 '16 at 15:38
  • @ntc2 Looks like it doesn't work with `--color-words`, but otherwise it looks nice. I'll stick with line diff then. – WoLfPwNeR Apr 19 '16 at 18:35
  • Thanks for this great answer! Is it possible to hide lines without differences from the output? – Hatshepsut Jan 27 '17 at 19:39
  • @Hatshepsut: try adding `-U0`? – ntc2 Jan 28 '17 at 01:50
  • More complex `re` does not highlight space changes – Eugen Konkov Jan 26 '18 at 15:15
50
git diff --color-words="[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+"

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
Justin M. Keyes
  • 6,679
  • 3
  • 33
  • 60
  • 5
    You can shorten it to `--color-words=[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+'` – Eddified Dec 13 '12 at 20:22
  • i had to add `'` to the beginning of the value there. otherwise i got an error. Also, i simply using `--color-words` i get the exact same behaviour as using that regexp. – gcb Oct 14 '13 at 06:41
  • @gcb what text did you test it on to get the "exact same behavior"? – Justin M. Keyes Oct 14 '13 at 07:01
  • @JustinM.Keyes a 4 line changes in a source file. nothing weird. javascript i think. Ascii. – gcb Oct 14 '13 at 21:18
  • 3
    @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. – Justin M. Keyes Oct 14 '13 at 22:34
  • 5
    Simpler: `git diff --color-words='[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+'`. – ntc2 Nov 06 '13 at 00:54
  • @ntc2 Cool, thanks. Any idea if that is backwards compatible with say git 1.7.x? – Justin M. Keyes Nov 06 '13 at 02:21
  • No idea when this feature was introduced; saw it when reading `man git diff` to understand what your answer was doing. Also, looks like my point is the same as @Eddfied's in the first comment :P – ntc2 Nov 06 '13 at 23:18
  • I wanna give a shoutout to @ntc2, that is exactly what I was looking for when googling "git diff by character"! – alxndr May 23 '14 at 23:19
  • I'm getting a strange newline issue using this vs just `--color-words=.`. On some lines using this regex, the diff-ed character (in my case, a `#` comment character that was removed in a YAML file) jumps to the beginning of the line and is no longer indented correctly. Using `--color-words=.` appears to show the correct indentation. – Aaron Tribou Nov 09 '16 at 17:38
  • `diff-hightligh` needs to be made with `make` atm. https://github.com/Homebrew/homebrew-core/issues/15588#issuecomment-315116293 – rofrol Apr 05 '18 at 19:44
  • URL is not available. – Vitaly Zdanevich Oct 04 '18 at 11:52
  • 2
    I had installed git with Homebrew and already had that script at `/usr/local/share/git-core/contrib/diff-highlight/diff-highlight` . [This](https://github.com/Homebrew/homebrew-core/pull/2132#issuecomment-227000623) 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` – Ashutosh Jindal Jun 04 '19 at 08:46
15

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.

itsjeyd
  • 5,070
  • 2
  • 30
  • 49
legoscia
  • 39,593
  • 22
  • 116
  • 167
6

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 :)

Nick Volynkin
  • 14,023
  • 6
  • 43
  • 67
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.

thegeek
  • 2,388
  • 2
  • 13
  • 10
  • 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. – Adam Monsen Jul 13 '10 at 17:42
  • 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. – Aleksander Adamowski May 07 '11 at 14:56
  • 2
    BTW, Some other tools worth checking out, not mentioned in the linked article: `wdiff2`, `mdiff`, and the [Google's online tool](http://goo.gl/wEnID). – Aleksander Adamowski May 07 '11 at 15:03
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?

Adam Monsen
  • 9,054
  • 6
  • 53
  • 82