Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git diff with interleaved lines

Tags:

git

git-diff

diff

I want a diff with interleaved lines, i.e. with "hunks" no longer than one line.

For example instead of

-t1 = "Christmas 2013"
-t2 = "Easter 2013"
-t3 = "Thanksgiving 2013"
+t1 = "Christmas 2014"
+t2 = "Easter 2014"
+t3 = "Thanksgiving 2014"

I want this:

-t1 = "Christmas 2013"
+t1 = "Christmas 2014"
-t2 = "Easter 2013"
+t2 = "Easter 2014"
-t3 = "Thanksgiving 2013"
+t3 = "Thanksgiving 2014"

So far I have

git diff -U0 --ignore-space-at-eol before after holidays.ini

I tried setting --break-rewrites=0%/0%, --break-rewrites=100%/0% and so on but it didn't change anything (I don't even know if it's relevant to my problem).

like image 461
AndreKR Avatar asked Mar 02 '14 22:03

AndreKR


2 Answers

None of the built-in diff algorithms will behave this way.

I'm curious as to what you'd like to see if, e.g., the change was to add one line and replace two others, so that (to grab your example) you'd have something like this:

-t1 = "Christmas 2013"
+t1 = "Christmas 2014"
+t2 = "Easter 2014"
-t3 = "Thanksgiving 2013"
+t3 = "Thanksgiving 2014"

Here, for t2, there's nothing to delete.

In any case, I believe your best bet is likely to post-process the output of git diff -U0.

If you're on a Unix-ish system you could also use original, non-unified diff, e.g.:

$ diff --git a/like_min.py b/like_min.py
index 05b9a4d..1c90084 100644
--- a/like_min.py
+++ b/like_min.py
@@ -1 +1 @@
-def like_min(iterable, key=None):
+def like_min(iterable, key=None): # comment
@@ -9 +9 @@ def like_min(iterable, key=None):
-    for candidate in it:
+    for candidate in it: # another comment
$ git show HEAD:like_min.py | diff - like_min.py
1c1
< def like_min(iterable, key=None):
---
> def like_min(iterable, key=None): # comment
9c9
<     for candidate in it:
---
>     for candidate in it: # another comment

which might be easier to post-process (depending on many details). In particular each change starts with a line number and letter code (add, change, delete), so there's no need to figure out whether something is a pure-add or pure-delete, vs changes you'd like to split into one-line-at-a-time. You still might have to turn a "change" into a "change followed by add-or-delete" if the new number of lines does not match:

$ git show HEAD:like_min.py | diff - like_min.py
1c1,2
< def like_min(iterable, key=None):
---
> def like_min(iterable, key=None): # comment
> def like_min(iterable, key=None): # comment
9c10
<     for candidate in it:
---
>     for candidate in it: # another comment

Also, "old diff" may have different (and not the desired) white-space-ignoring options.


Fiddling with --break-rewrites is orthogonal to what you want: it just changes the point at which git considers a file as "wholly rewritten", and thus shows the change as "delete entire previous file contents, insert all-new contents".

The default breakpoint is, according to the documentation, -B50%/60%, which specifies that no more than 60% of the file can be "rewritten", or equivalently, "at least 40% of the file still matches". You might want to decrease this, but probably don't want to increase it. (Incidentally, I can't seem to set this to 0%; setting it to 1% makes most changes become complete rewrites, but small changes, like changing just one line of a file, still show up as small changes rather than total-file-rewrites. This is probably because the similarity index is not based purely on line-at-a-time changes, but also includes intra-line matches.)

(That first number—the 50% in -B50%/60%—is the similarity index value used for rename detection, assuming rename detection is enabled. Think of the two numbers as the "similarity and dissimilarity index" values: similarity index is "how close is file 1 to file 2", and dissimiliarity is just 100% minus similarity.)

like image 143
torek Avatar answered Sep 21 '22 14:09

torek


I'm glad I'm not the only one who wants to do this.

The following shows old and new on adjacent lines via paste and uses uniq as the World's Worst Diff:

git show HEAD:./holidays.ini | paste -d '\n' - holidays.ini | uniq -u

"Christmas 2013"
"Christmas 2014"
"Easter 2013"
"Easter 2014"
"Thanksgiving 2013"
"Thanksgiving 2014"
like image 25
Robert Calhoun Avatar answered Sep 23 '22 14:09

Robert Calhoun