git diff with interleaved lines

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

  • How to compare the working copy, staging copy and committed copy of a file using git
  • Getting the difference between two repositories
  • git difftool: how to compare zip files
  • GIT - How to list only newly added files between two branches
  • Unable to reset files in Git
  • Git diff fails on file with underscore in path
  • git diff gives ambigious argument error
  • How to get Git diff of the first commit?
  • 2 Solutions collect form web for “git diff with interleaved lines”

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

    If the diff is not required to be textual, you could use KDiff3:

    KDiff3 screenshot

    This will give an even greater granularity than single lines.

    Git Baby is a git and github fan, let's start git clone.