5 # This software may be used and distributed according to the terms |
5 # This software may be used and distributed according to the terms |
6 # of the GNU General Public License, incorporated herein by reference. |
6 # of the GNU General Public License, incorporated herein by reference. |
7 |
7 |
8 from node import * |
8 from node import * |
9 from i18n import _ |
9 from i18n import _ |
10 import os, sys, mdiff, util, templater, patch |
10 import os, sys, mdiff, bdiff, util, templater, patch |
11 |
11 |
12 revrangesep = ':' |
12 revrangesep = ':' |
13 |
13 |
14 def revpair(repo, revs): |
14 def revpair(repo, revs): |
15 '''return pair of nodes, given list of revisions. second item can |
15 '''return pair of nodes, given list of revisions. second item can |
144 for src, fn in repo.walk(node=node, files=files, match=matchfn, |
144 for src, fn in repo.walk(node=node, files=files, match=matchfn, |
145 badmatch=badmatch): |
145 badmatch=badmatch): |
146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact |
146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact |
147 |
147 |
148 def findrenames(repo, added=None, removed=None, threshold=0.5): |
148 def findrenames(repo, added=None, removed=None, threshold=0.5): |
|
149 '''find renamed files -- yields (before, after, score) tuples''' |
149 if added is None or removed is None: |
150 if added is None or removed is None: |
150 added, removed = repo.status()[1:3] |
151 added, removed = repo.status()[1:3] |
151 ctx = repo.changectx() |
152 ctx = repo.changectx() |
152 for a in added: |
153 for a in added: |
153 aa = repo.wread(a) |
154 aa = repo.wread(a) |
154 bestscore, bestname = None, None |
155 bestname, bestscore = None, threshold |
155 for r in removed: |
156 for r in removed: |
156 rr = ctx.filectx(r).data() |
157 rr = ctx.filectx(r).data() |
157 delta = mdiff.textdiff(aa, rr) |
158 |
158 if len(delta) < len(aa): |
159 # bdiff.blocks() returns blocks of matching lines |
159 myscore = 1.0 - (float(len(delta)) / len(aa)) |
160 # count the number of bytes in each |
160 if bestscore is None or myscore > bestscore: |
161 equal = 0 |
161 bestscore, bestname = myscore, r |
162 alines = mdiff.splitnewlines(aa) |
162 if bestname and bestscore >= threshold: |
163 matches = bdiff.blocks(aa, rr) |
|
164 for x1,x2,y1,y2 in matches: |
|
165 for line in alines[x1:x2]: |
|
166 equal += len(line) |
|
167 |
|
168 myscore = equal*2.0 / (len(aa)+len(rr)) |
|
169 if myscore >= bestscore: |
|
170 bestname, bestscore = r, myscore |
|
171 if bestname: |
163 yield bestname, a, bestscore |
172 yield bestname, a, bestscore |
164 |
173 |
165 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None, |
174 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None, |
166 similarity=None): |
175 similarity=None): |
167 if dry_run is None: |
176 if dry_run is None: |