Does Git provide any mechanism for proving that a commit moves but does not modify lines within a file? I frequently use -w
to determine whether any changes were made within a section of code whose indentation changed. If a section of code is moved vertically rather than laterally, is there anything one can do to highlight changes within the moved section?

- 5,059
- 2
- 34
- 39

- 23,918
- 16
- 76
- 105
-
3No, but it might be possible to write a custom diff tool to do that kind of analysis – Lasse V. Karlsen Jul 18 '20 at 18:19
-
So far I haven't seen any diff tool (I have seen git difftool, differential in phabricator, pull requests in bitbucket, github and gitlab) which does that. It would be interesting to know if any do from pov of code review. – skumar Jul 18 '20 at 19:59
-
Let me see if I can whip something up real fast. – Lasse V. Karlsen Jul 18 '20 at 20:06
-
3Here's a basic prototype written in C# - https://github.com/lvk-stackoverflow/SO62972103 - it uses my library for working out the diff between two files, and then analyzes the result to write some basic output. Read the README shown on the front page for examples on how to use and what it does. – Lasse V. Karlsen Jul 18 '20 at 20:36
-
Unfortunately, when using a git difftool, temporary files are created that has filenames that does not in any way resemble the original filenames, so I don't know how to report which file you're looking at the result from. Some spelunking in the git options might give you some options in this regard. – Lasse V. Karlsen Jul 18 '20 at 20:37
-
I posted a question of my own regarding the name of the file being diffed - https://stackoverflow.com/questions/62973585/when-writing-a-git-difftool-is-there-a-way-for-the-tool-to-know-which-file-is-b – Lasse V. Karlsen Jul 18 '20 at 20:42
4 Answers
No, Git doesn't provide a way of proving this. It shouldn't be too hard to parse the diff output and verify that the groups of deleted lines and added lines are the same groups. This would require some scripting, but it is doable. This project may be interesting.
If you're looking to verify this visually, you can use --color-moved
, which will color moved blocks in a different color than additions or removals that aren't moved lines.

- 64,793
- 6
- 84
- 100
I don't think Git provides any direct way of doing this, but if sorting the old and new versions of a file yield the same result, that's equivalent to the change consisting only of moving lines.
Of course that won't distinguish between swapping two blocks of lines and completely scrambling the file.
Highlighting such changes is another matter. This question, which I asked several years ago, might be relevant:
Is there a diff-like algorithm that handles moving block of lines?

- 254,901
- 44
- 429
- 631
You could try...
git diff -U0 | grep -vE '^[+-]{3}' | grep '^[+-]' | sort -k1.2 | uniq -cs1
... and check that all lines of interest start with a count of 2 (or an even number if there are duplicates also moving). The greps are optional if you don't mind a little extra output to ignore.
(-U0
skips the "context" lines in the diff output; sort -k1.2
ignores the first character (+/-) when sorting, and uniq -cs1
counts the number of matches in each group of identical lines and again ignores the first character (+/-) when comparing if lines are the same.)
This also works for regular diffs (diff -u0
, rather than git diff -U0
)

- 5,059
- 2
- 34
- 39
-
1Like the idea but it has weaknesses. `git diff etc | sed -n '/^@@/,/^diff/ { /^[-+]/p }' | sort -k1.1 | uniq -cs2` would be better, it wouldn't get bad counts on content lines beginning `++` or `--` and wouldn't ignore leading nonblanks, but it still won't help with e.g. deleting two identical lines. You'd need gawk or better instead of the uniq, `awk '$2!=last { if(ad) print "line added or removed: "last; last=$2; ad=0 } {ad+=$1=="+"; ad-=$1=="-"}' FIELDWIDTHS="1 99999999"` – jthill Aug 10 '23 at 01:15
-
Great points, @jthill. I'm going to leave the original answer because it works for my use cases and is easy to remember and type live, but I appreciate having the awk you worked out here for cases where double-adds or deletes can arise. – Joshua Goldberg Aug 10 '23 at 20:07
-
(I don't understand the -k1.1, though. When I tried it, it separates the additions from the deletions so uniq can't match similar lines.) – Joshua Goldberg Aug 10 '23 at 20:08
-
Yah, I was typing without thinking there and swapped position and offset, it should be `-k1.2` and `-s1`, sort starting with the second character, and do the uniq skipping the first. – jthill Aug 10 '23 at 22:27
-
Wouldn't that just be `-k2` then for the sort? I'll fix uniq in my answer so it only skip the first character. (I was thinking `uniq -s 2` worked more like `sort -k2`, but it skips that many characters, rather than starting to compare at the 2nd.) – Joshua Goldberg Aug 14 '23 at 21:12
-
1`sort -k2` means "starting with the second field", by default those are whitespace-separated so the sort will ignore any word at the start of a line. You just want it to ignore the first *character* at the start of a line, `-k1.2` says "start with the second character of the first field". beat on it some, `printf '%s %s\n' 1a 1 1b 1 1a 2 1b 2 | sort -k2` – jthill Aug 16 '23 at 17:02
-
You're right, I found the manpage confusing, especially because I mistook a comma for a dot when I read it. (To others, don't just read the manpage line for `-k`, also read the long discussion of field1/field2 at the bottom. `-k1.2` isn't specifying field1 and field2 as two character positions, it's specifying field1 -> ∞ as one long field, starting from char 2 of the first word of the line, and extending to the end of the line since field2 is left out.) – Joshua Goldberg Aug 16 '23 at 18:37
-
Random little tidbit, if you ever want to royally irk your friendly local code martinet you can use offset numbering when specifying keys, +offset.offset instead of -kposition.position, so `sort +0.1` keeps the offset numbering `uniq`'s `-s` uses (skip means offset, yeah?). – jthill Aug 16 '23 at 20:19
There is
git diff --color-moved <from-commit> <to-commit>
It shows moved lines in a color different from the usual added and removed lines. That's not a proof, but just a visual indication. (Also, this sort of highlighting kicks in only when the moved region spans a minimum number of characters.)
See for example
git show --color-moved aabc5617cdfb29ccf98048b0d99cae2a811df51f
in the Git source code.

- 9,150
- 1
- 15
- 35