##// END OF EJS Templates
addremove: add -s/--similarity option...
Vadim Gelfer -
r2958:ff3ea21a default
parent child Browse files
Show More
@@ -8,7 +8,7 b''
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 demandload(globals(), 'util')
11 demandload(globals(), 'mdiff util')
12 12 demandload(globals(), 'os sys')
13 13
14 14 def make_filename(repo, pat, node,
@@ -93,19 +93,53 b' def walk(repo, pats=[], opts={}, node=No'
93 93 for r in results:
94 94 yield r
95 95
96 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None):
96 def findrenames(repo, added=None, removed=None, threshold=0.5):
97 if added is None or removed is None:
98 added, removed = repo.status()[1:3]
99 changes = repo.changelog.read(repo.dirstate.parents()[0])
100 mf = repo.manifest.read(changes[0])
101 for a in added:
102 aa = repo.wread(a)
103 bestscore, bestname = None, None
104 for r in removed:
105 rr = repo.file(r).read(mf[r])
106 delta = mdiff.textdiff(aa, rr)
107 if len(delta) < len(aa):
108 myscore = 1.0 - (float(len(delta)) / len(aa))
109 if bestscore is None or myscore > bestscore:
110 bestscore, bestname = myscore, r
111 if bestname and bestscore >= threshold:
112 yield bestname, a, bestscore
113
114 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
115 similarity=None):
97 116 if dry_run is None:
98 117 dry_run = opts.get('dry_run')
118 if similarity is None:
119 similarity = float(opts.get('similarity') or 0)
99 120 add, remove = [], []
121 mapping = {}
100 122 for src, abs, rel, exact in walk(repo, pats, opts):
101 123 if src == 'f' and repo.dirstate.state(abs) == '?':
102 124 add.append(abs)
125 mapping[abs] = rel, exact
103 126 if repo.ui.verbose or not exact:
104 127 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
105 128 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
106 129 remove.append(abs)
130 mapping[abs] = rel, exact
107 131 if repo.ui.verbose or not exact:
108 132 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
109 133 if not dry_run:
110 134 repo.add(add, wlock=wlock)
111 135 repo.remove(remove, wlock=wlock)
136 if similarity > 0:
137 for old, new, score in findrenames(repo, add, remove, similarity):
138 oldrel, oldexact = mapping[old]
139 newrel, newexact = mapping[new]
140 if repo.ui.verbose or not oldexact or not newexact:
141 repo.ui.status(_('recording removal of %s as rename to %s '
142 '(%d%% similar)\n') %
143 (oldrel, newrel, score * 100))
144 if not dry_run:
145 repo.copy(old, new, wlock=wlock)
@@ -658,8 +658,17 b' def addremove(ui, repo, *pats, **opts):'
658 658
659 659 New files are ignored if they match any of the patterns in .hgignore. As
660 660 with add, these changes take effect at the next commit.
661
662 Use the -s option to detect renamed files. With a parameter > 0,
663 this compares every removed file with every added file and records
664 those similar enough as renames. This option takes a percentage
665 between 0 (disabled) and 100 (files must be identical) as its
666 parameter. Detecting renamed files this way can be expensive.
661 667 """
662 return cmdutil.addremove(repo, pats, opts)
668 sim = float(opts.get('similarity') or 0)
669 if sim < 0 or sim > 100:
670 raise util.Abort(_('similarity must be between 0 and 100'))
671 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
663 672
664 673 def annotate(ui, repo, *pats, **opts):
665 674 """show changeset information per file line
@@ -2747,7 +2756,10 b' table = {'
2747 2756 (addremove,
2748 2757 [('I', 'include', [], _('include names matching the given patterns')),
2749 2758 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2750 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2759 ('n', 'dry-run', None,
2760 _('do not perform actions, just print output')),
2761 ('s', 'similarity', '',
2762 _('guess renamed files by similarity (0<=s<=1)'))],
2751 2763 _('hg addremove [OPTION]... [FILE]...')),
2752 2764 "^annotate":
2753 2765 (annotate,
@@ -10,3 +10,17 b' cd dir/'
10 10 touch ../foo_2 bar_2
11 11 hg -v addremove
12 12 hg -v commit -m "add 2" -d "1000000 0"
13
14 cd ..
15 hg init sim
16 cd sim
17 echo a > a
18 echo a >> a
19 echo a >> a
20 echo c > c
21 hg commit -Ama
22 mv a b
23 rm c
24 echo d > d
25 hg addremove -s 0.5
26 hg commit -mb
@@ -6,3 +6,10 b' adding dir/bar_2'
6 6 adding foo_2
7 7 dir/bar_2
8 8 foo_2
9 adding a
10 adding c
11 adding b
12 adding d
13 removing a
14 removing c
15 recording removal of a as rename to b (100% similar)
General Comments 0
You need to be logged in to leave comments. Login now