Photo by Robert Katzki on Unsplash

Git-Config: diff.colormoved

New Line? Removed Line? Moved Line? Configure Git to Show the Difference at a Glance

Karl Stolley
4 min readMar 22, 2022

--

https://pragprog.com/newsletter/

Diffing is a Git habit worth cultivating: it’s the last chance to inspect what you’re actually about to stage or commit. Diffing helps you dive more deeply into the history of a repository and examine pull requests, too. A routine diff habit is also key to crafting finer-grained commits in cases where staging an entire file with git add is just too ham-fisted.

Git’s diff output itself is a little ham-fisted. By default, git diff highlights added lines in green and removed lines in red color-capable terminal environments. In all environments, removed lines are prefixed with a minus sign, -, and added lines with a plus sign, +:

Git’s default output, revealing added and removed lines.

There’s a shortcoming to Git’s default diff output, though: any line that has been cut and pasted will appear diffed out like any other pair of added and removed lines. Cuts and pastes are a typical part of refactored and reorganized code. But the two-color diff output burdens you with some unnecessary cognitive load when you’re trying to ensure the integrity of any moved lines: When you need to move something around or simply reformat it, it’s harder to tell whether the line truly is unchanged, apart from formatting or placement. Commas, curly braces, and semicolons are all too easy to leave out of a copy and paste operation.

For example, here’s a Markdown file with three lines moved from the top of the file to the end:

Moved lines appear as sets of removed and added lines.

Even in that simple case, closer inspection is needed to determine whether the lines have been moved without modification. Let’s look at a few options to change Git’s diff.colormoved configuration value to highlight cut and pasted lines differently from lines that are genuinely added, removed, or modified.

Customizing diff.colormoved

The diff.colormovedconfiguration property can be set to any one of the permitted values for the--color-movedoption on git diff. I prefer to set mine to zebra. By rerunning the same git diff command on the above JavaScript, it’s now much more obvious what’s been added and removed, and what’s simply been copied elsewhere. The original location of moved lines is shown in a fuchsia color, and the new location in turquoise:

With the zebra setting, moved lines are colored differently. Readers with sharp eyes will note a red `-` on the trailing empty line, and a green `+` on a newly inserted empty line.

With zebra coloring, it should be more obvious when a line has been accidentally modified through, say, a sloppy cut and paste: the modified line will take the default red and green coloring.

When you want to more aggressively highlight smaller or insignificant moves, like the empty lines in the example above, you can pass in the plain option directly togit diff. (The zebra value ignores moved blocks of fewer than 20 characters.) The plain option can go a little overboard with highlighting trivial lines. So I don’t generally set it on diff.colormoved. But it’s a useful enough option that it’s worth aliasing:

$ git config --global alias.dm "diff --color-moved=plain"

The zebra setting did not highlight the instance of a moved, empty line above. But when you run the git dm alias, plain picks the empty lines right up, presenting them as moved — even if they’ve been reordered:

With the plain setting, insignificant changes — like empty lines — are shown as moved. The red and green markers on the empty lines from the “zebra” value above are here fuchsia and turquoise — just like the other moved lines.

And hey: since we’re talking about aliases, have you ever prematurely run git add . to stage your work for commit before diffing? And have you then seen how git diff reports no output at all? Not a problem. There’s no need to unstage the file with git restore: just add the --staged argument onto the end of git diff. Better yet? Make it an alias:

$ git config --global alias.ds "diff --staged"

Calling up git ds will now diff your staged files — rather than the files in your working tree — against your most recent commit. It’ll use your preferred configuration value on diff.colormoved, too.

In the next post, we’ll look more closely at a related diff-output configuration value, diff.colormovedws, so that simple changes in whitespace (like when moved code is indented or outdented) are not reported as wholly changed, added/removed lines in Git’s diff output. We’ll also look at some general whitespace configuration-values to make a few thorny problems a little easier to handle in Git.

Karl Stolley’s book, Programming WebRTC: Build Real-Time Streaming Applications for the Web, is available in beta from The Pragmatic Bookshelf. You can save 35 percent with promo code git_config_2022 now through April 30, 2022. Promo codes are not valid on prior purchases.

--

--

Karl Stolley
The Pragmatic Programmers

Author of Programming WebRTC, out in beta at Pragmatic Programmers. Chicagoan. Developer & writer.