24

I'm reviewing a patch that moved a lot of things around, added a few things, and removed a few things. I'm wondering if anyone's written a utility for picking out the unique adds/removes in a universal diff?

That is, an add and a remove of the same line should cancel themselves out.

Obviously this isn't useful all the time, but sometimes it's exactly what I want :)

Zombo
  • 1
  • 62
  • 391
  • 407
Jeffrey Harris
  • 3,480
  • 25
  • 30
  • Just the adds removes, as opposed to what? Most 2-way diff tools I've seen view their entire universe as either adds or removes, or unchanged lines. – T.E.D. Sep 04 '09 at 17:36
  • I want to treat the pair of "add line XYZ" and "remove line XYZ" as cancelling one another out. I want to see lines which were added, but never removed, and vice versa. – Jeffrey Harris Sep 04 '09 at 18:11
  • If I have a file X, and somebody added line 5, and somebody deleted line 5, looking at just file X, how would I even know something happened? Where are you getting the "added..." "deleted..." information from? – Ira Baxter Sep 05 '09 at 03:15
  • You get added and deleted lines from the normal output of diff, a plus for an add, a minus for a deletion. I'm not looking for a tool that I'd use as my normal diff, just a supplement to the normal patch review process. Occasionally when someone refactors code they accidentally delete a line they didn't mean to. Those deletions are very hard to pick out of a standard diff if a block of statements have been rearranged. – Jeffrey Harris Sep 08 '09 at 14:38
  • To be clear, this is talking about moving lines within the same file, so options for `git diff` like `-M` and `-C` are not relevant. – Flimm Oct 20 '14 at 14:34
  • See https://stackoverflow.com/a/47192896/8910547 – Inigo Jan 19 '19 at 20:08

5 Answers5

21

This is what I ended up using.

Example usage:

git diff -w | /path/to/ignore_moves.py | less -R

ignore_moves.py

#!/usr/bin/python                                                                                                             

import sys
from itertools import *

RED = 31
GREEN = 32

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[0;%dm"

stack = []

def inverse(line):
    return ('-' if line[0] == '+' else '+') + line[1:].strip()

def reverse_enumerate(l):
    for i, x in enumerate(reversed(l)):
        yield len(l)-1-i, x

def dumpchanges():
    for line in stack:
        SEQ = COLOR_SEQ % (GREEN if line.startswith('+') else RED)
        print SEQ + line.strip() + RESET_SEQ
    stack[:] = []

for line in sys.stdin.readlines():
    if not line[1:].strip():
        continue # ignore empty lines                                                                                         
    if line.startswith(('---', '+++')):
        dumpchanges()
        print line.strip()
    elif line.startswith(('+', '-')):
        inverted = inverse(line)
        line = line[0] + line[1:].strip()
        for i, match in reverse_enumerate(stack):
            if inverted == match:
                stack.pop(i)
                break
        else:
            stack.append(line)

# finished reading, still have state to be dumped                                                                             
dumpchanges()
Jeffrey Harris
  • 3,480
  • 25
  • 30
  • Thank you so much! I started writing this myself and then thought... wait, some has had to have felt this pain before. – Bruno Bronosky Sep 07 '11 at 18:58
  • 3
    I liked this so much that I decided to add an option to get plain output (good for storing in a file or opening in an editor with syntax highlighting, like vim) and I made it get perfect marks from pyflakes, pylint, and pep8. https://gist.github.com/1202342 – Bruno Bronosky Sep 08 '11 at 07:13
  • The problem with this is that it deletes context lines. – Flimm Feb 25 '15 at 11:48
  • 3
    See also `git diff --color-moved` nowadays. – Per Lundberg Feb 05 '21 at 07:56
9

This worked better for me to get the diff of the modified files (omitting files that were only moved).

git diff -M -C -D

From the git diff documentation:

-M[<n>], --find-renames[=<n>]
       Detect renames. If n is specified, it is a threshold on the similarity index (i.e. amount of addition/deletions compared to the file's size). For example, -M90% means git should consider a delete/add pair to be a rename if more than 90% of the file hasn't changed.

-C[<n>], --find-copies[=<n>]
       Detect copies as well as renames. See also --find-copies-harder. If n is specified, it has the same meaning as for -M<n>.

-D, --irreversible-delete
       Omit the preimage for deletes, i.e. print only the header but not the diff between the preimage and /dev/null. The resulting patch is not meant to be applied with patch nor git apply; this is solely for people who want to just concentrate on reviewing the text after the change. In addition, the output obviously
       lack enough information to apply such a patch in reverse, even manually, hence the name of the option.
PanchaGil
  • 2,091
  • 2
  • 15
  • 8
  • 3
    This does not answer the question, it does not hide lines moved within the same file. – Flimm Oct 20 '14 at 14:34
  • This may not answer the original question asked. But it answered the question I was looking for the answer to when I found this. – Gavin S Dec 23 '15 at 16:06
5

Here it is, with Bash

git diff | diff-ignore-moved-lines

Ignore moved diff lines

Zombo
  • 1
  • 62
  • 391
  • 407
3

To 'see' only the lines that are not moved, try this in a console with black background

git -c color.diff.newMoved=black -c color.diff.oldMoved=black diff --color-moved=plain --unified=0

All the moved lines will vanish visually. You will see only the lines that are actually changed. Do note that context is also not shown.

Ref: https://groups.google.com/g/git-users/c/kycBnxmmCcU?pli=1

anoopjohn
  • 518
  • 4
  • 18
  • Good idea. Only drawback is if it's a seriously big diff where most changes are moves, you have to scroll carefully to not miss the changes. – ak2 Nov 08 '22 at 10:39
1

This is a variation of the answer provided by @Zombo above https://stackoverflow.com/a/23462451/722087

When a lot of files have changes that are only moved lines and then there are files that actually have changes other than moved lines then you would want to find those files that have changed lines. The following will allow you to do that.

Copy the file diff-ignore-moved-lines.sh into your path so that you can execute it from anywhere. Then run the following in the git folder

for file in `git diff --name-only`; do  echo $file; git --no-pager diff -- "$file" | diff-ignore-moved-lines; done
anoopjohn
  • 518
  • 4
  • 18