7 from optparse
import OptionParser
8 from git_stats
import commit
9 from git_stats
import diff
10 from git_stats
import parse
12 def calculateDiffSize(difference
):
13 """Calculates the true diff size
15 All lines that start with '+\t', '-\t' are counted.
16 Lines that are of size 1 are also counted.
21 # Take each line and only count if it is part of the diff
22 for line
in difference
:
23 if (len(line
) == 1 or line
[1] == '\t' and
24 (line
[0] == '+' or line
[0] == '-')):
29 def findMatch(left
, right
):
30 """Tries to find a match between left and right
32 If it is plausible that there is a match the difference is returend.
33 Otherwise False is returned.
36 # Get the diff and convert it to a usable format
37 res
= difflib
.unified_diff(left
, right
, n
=0, lineterm
="")
40 # Get some sizes for easy calculation
41 ressize
= calculateDiffSize(res
)
43 rightsize
= len(right
)
45 # The difference is larger than either side
46 if ressize
> leftsize
or ressize
> rightsize
:
49 # The difference is larger than the average
50 if ressize
> (leftsize
+ rightsize
)/2:
53 # This is probably a match, return the difference
57 """Tries to find a match between added and removed hunks
59 The diff of the specified commit is retreived and it is
60 split into hunks. The hunks that were added are compared
61 with the hunks that were deleted. If they are similar the
62 pair is deemed a match.
66 result
= commit
.getDiff(target
)
67 targetDiff
= result
.split('\n')
69 # And have it parsed, but don't add line numbering to the hunks
70 parsedDiffs
= diff
.parseCommitDiff(targetDiff
, number
=False)
72 # To store the matches in
75 # Iterate over all the diffs, e.g., take all pairs
76 for left
in parsedDiffs
:
77 for right
in parsedDiffs
:
78 # Don't compare with self, that'd always match
82 # A removal hunk, not interesting as a left side
83 # Only interesting when comparing with addition
84 if not left
.linesAdded
:
87 # An add hunk, not interesting as a right side
88 # We are interested in this as a left side
89 if not right
.linesDeleted
:
92 # Try to find a match for this pair
93 res
= findMatch(left
.linesAdded
, right
.linesDeleted
)
99 result
.append(left
.linesAdded
, right
.linesAdded
, res
)
104 """Dispatches matching related commands
107 # Make the help show 'progname commit' instead of just 'progname'
108 progname
= os
.path
.basename(sys
.argv
[0]) + " matcher"
110 parser
= OptionParser(option_class
=parse
.GitOption
, prog
=progname
)
115 help="match the chunks of a diff to find code moves")
117 parser
.set_default("matcher", "HEAD")
119 (options
, args
) = parser
.parse_args(list(args
))
122 parser
.error("Please specify a commit to analyse")
125 result
= match(options
.matcher
)