Show More
@@ -8,7 +8,7 b'' | |||||
8 | from demandload import demandload |
|
8 | from demandload import demandload | |
9 | from node import * |
|
9 | from node import * | |
10 | from i18n import gettext as _ |
|
10 | from i18n import gettext as _ | |
11 | demandload(globals(), 'util') |
|
11 | demandload(globals(), 'mdiff util') | |
12 | demandload(globals(), 'os sys') |
|
12 | demandload(globals(), 'os sys') | |
13 |
|
13 | |||
14 | def make_filename(repo, pat, node, |
|
14 | def make_filename(repo, pat, node, | |
@@ -93,19 +93,53 b' def walk(repo, pats=[], opts={}, node=No' | |||||
93 | for r in results: |
|
93 | for r in results: | |
94 | yield r |
|
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 | if dry_run is None: |
|
116 | if dry_run is None: | |
98 | dry_run = opts.get('dry_run') |
|
117 | dry_run = opts.get('dry_run') | |
|
118 | if similarity is None: | |||
|
119 | similarity = float(opts.get('similarity') or 0) | |||
99 | add, remove = [], [] |
|
120 | add, remove = [], [] | |
|
121 | mapping = {} | |||
100 | for src, abs, rel, exact in walk(repo, pats, opts): |
|
122 | for src, abs, rel, exact in walk(repo, pats, opts): | |
101 | if src == 'f' and repo.dirstate.state(abs) == '?': |
|
123 | if src == 'f' and repo.dirstate.state(abs) == '?': | |
102 | add.append(abs) |
|
124 | add.append(abs) | |
|
125 | mapping[abs] = rel, exact | |||
103 | if repo.ui.verbose or not exact: |
|
126 | if repo.ui.verbose or not exact: | |
104 | repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) |
|
127 | repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) | |
105 | if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): |
|
128 | if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): | |
106 | remove.append(abs) |
|
129 | remove.append(abs) | |
|
130 | mapping[abs] = rel, exact | |||
107 | if repo.ui.verbose or not exact: |
|
131 | if repo.ui.verbose or not exact: | |
108 | repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) |
|
132 | repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) | |
109 | if not dry_run: |
|
133 | if not dry_run: | |
110 | repo.add(add, wlock=wlock) |
|
134 | repo.add(add, wlock=wlock) | |
111 | repo.remove(remove, wlock=wlock) |
|
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 | New files are ignored if they match any of the patterns in .hgignore. As |
|
659 | New files are ignored if they match any of the patterns in .hgignore. As | |
660 | with add, these changes take effect at the next commit. |
|
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 | def annotate(ui, repo, *pats, **opts): |
|
673 | def annotate(ui, repo, *pats, **opts): | |
665 | """show changeset information per file line |
|
674 | """show changeset information per file line | |
@@ -2747,7 +2756,10 b' table = {' | |||||
2747 | (addremove, |
|
2756 | (addremove, | |
2748 | [('I', 'include', [], _('include names matching the given patterns')), |
|
2757 | [('I', 'include', [], _('include names matching the given patterns')), | |
2749 | ('X', 'exclude', [], _('exclude names matching the given patterns')), |
|
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 | _('hg addremove [OPTION]... [FILE]...')), |
|
2763 | _('hg addremove [OPTION]... [FILE]...')), | |
2752 | "^annotate": |
|
2764 | "^annotate": | |
2753 | (annotate, |
|
2765 | (annotate, |
@@ -10,3 +10,17 b' cd dir/' | |||||
10 | touch ../foo_2 bar_2 |
|
10 | touch ../foo_2 bar_2 | |
11 | hg -v addremove |
|
11 | hg -v addremove | |
12 | hg -v commit -m "add 2" -d "1000000 0" |
|
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 |
General Comments 0
You need to be logged in to leave comments.
Login now