Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Diff only changed parts of lines

Tags:

git

bash

diff

Is it possible to show only differences between old and new lines using git diff?

Here is content of file a.txt:

123456789

And here is content of file b.txt

123455789

Here is normal git diff output:

normal output

And here is what I'm trying to accomplish: wanted output

I'd prefer to do this using git diff, but if it can't be done I'm ok with any other app.

like image 897
xx77aBs Avatar asked Sep 02 '14 09:09

xx77aBs


1 Answers

This is what the --word-diff option to git diff does. Though not to that level of granularity by default.

To get single character diffs like that would require a custom --word-diff-regex as well. (I have '([^[:alnum:]]|[^[:space:]])' in a git alias here which seems to do that though. Though that wasn't the point when I wrote it.)

You can abbreviate --word-diff=color --word-diff-regex='...' to --color-words='...' too by the way.

See if this does what you want:

$ cat worddiff.awk
BEGIN {
    RS="\n?~\n"
    FS="\n"
}

# Special case the diff header/chunk header lines.
/^diff --git/ {
    print
    next
}

{
    delete outs
    for (i=1; i<=NF; i++) {
        if ($i ~ /^[-+]/) {
            mode = substr($i, 1, 1)
            $i = ((mode=="-")?red:green) substr($i, 2) reset
            outs[mode] = outs[mode] $i reset
            outs["set" mode]++
        } else {
            gsub(/^ /, "", $i)
            outs["-"] = outs["-"] $i
            outs["+"] = outs["+"] $i
        }
    }

    # If we didn't have any changes then this is a context line and we need to
    # print it out.
    if (!outs["set-"] && !outs["set+"]) {
        print " " outs["-"]
        next
    }

    if (outs["set-"]) {
        print red "-" reset outs["-"]
    }

    if (outs["set+"]) {
        print green "+" reset outs["+"]
    }
}

Used as:

git diff --word-diff=porcelain | awk -v red="$(tput setaf 1)" -v green="$(tput setaf 2)" -v reset="$(tput sgr0)" -f worddiff.awk

or

git diff --word-diff-regex='([^[:alnum:]]|[^[:space:]])' --word-diff=porcelain | awk -v red="$(tput setaf 1)" -v green="$(tput setaf 2)" -v reset="$(tput sgr0)" -f worddiff.awk

That awk could probably be cleaner and there's a non-GNU awk split idiom to clear an array that I can't recall offhand if delete a is a problem.

Edit: Updated code above to match newest revision of gist. The original special case pattern was overly permissive. The original code did not handle added blank lines correctly. The updated code doesn't either but does the best that I believe is possible given the known limitations of --word-diff=porcelain.

like image 140
Etan Reisner Avatar answered Oct 16 '22 02:10

Etan Reisner