##// END OF EJS Templates
automv: switch to specifying the similarity as an integer (0-100)...
Martijn Pieters -
r28152:5ec1ce8f default
parent child Browse files
Show More
@@ -1,85 +1,87 b''
1 # automv.py
1 # automv.py
2 #
2 #
3 # Copyright 2013-2016 Facebook, Inc.
3 # Copyright 2013-2016 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """Check for unrecorded moves at commit time (EXPERIMENTAL)
7 """Check for unrecorded moves at commit time (EXPERIMENTAL)
8
8
9 This extension checks at commit/amend time if any of the committed files
9 This extension checks at commit/amend time if any of the committed files
10 comes from an unrecorded mv.
10 comes from an unrecorded mv.
11
11
12 The threshold at which a file is considered a move can be set with the
12 The threshold at which a file is considered a move can be set with the
13 ``automv.similarity`` config option; the default value is 1.00.
13 ``automv.similarity`` config option. This option takes a percentage between 0
14 (disabled) and 100 (files must be identical), the default is 100.
14
15
15 """
16 """
16 from __future__ import absolute_import
17 from __future__ import absolute_import
17
18
18 from mercurial import (
19 from mercurial import (
19 commands,
20 commands,
20 copies,
21 copies,
21 extensions,
22 extensions,
22 scmutil,
23 scmutil,
23 similar
24 similar
24 )
25 )
25 from mercurial.i18n import _
26 from mercurial.i18n import _
26
27
27 def extsetup(ui):
28 def extsetup(ui):
28 entry = extensions.wrapcommand(
29 entry = extensions.wrapcommand(
29 commands.table, 'commit', mvcheck)
30 commands.table, 'commit', mvcheck)
30 entry[1].append(
31 entry[1].append(
31 ('', 'no-automv', None,
32 ('', 'no-automv', None,
32 _('disable automatic file move detection')))
33 _('disable automatic file move detection')))
33
34
34 def mvcheck(orig, ui, repo, *pats, **opts):
35 def mvcheck(orig, ui, repo, *pats, **opts):
35 """Hook to check for moves at commit time"""
36 """Hook to check for moves at commit time"""
36 renames = None
37 renames = None
37 disabled = opts.pop('no_automv', False)
38 disabled = opts.pop('no_automv', False)
38 if not disabled:
39 if not disabled:
39 threshold = float(ui.config('automv', 'similarity', '1.00'))
40 threshold = float(ui.config('automv', 'similarity', '100'))
40 if threshold > 0:
41 if threshold > 0:
41 match = scmutil.match(repo[None], pats, opts)
42 match = scmutil.match(repo[None], pats, opts)
42 added, removed = _interestingfiles(repo, match)
43 added, removed = _interestingfiles(repo, match)
43 renames = _findrenames(repo, match, added, removed, threshold)
44 renames = _findrenames(repo, match, added, removed,
45 threshold / 100.0)
44
46
45 with repo.wlock():
47 with repo.wlock():
46 if renames is not None:
48 if renames is not None:
47 scmutil._markchanges(repo, (), (), renames)
49 scmutil._markchanges(repo, (), (), renames)
48 return orig(ui, repo, *pats, **opts)
50 return orig(ui, repo, *pats, **opts)
49
51
50 def _interestingfiles(repo, matcher):
52 def _interestingfiles(repo, matcher):
51 """Find what files were added or removed in this commit.
53 """Find what files were added or removed in this commit.
52
54
53 Returns a tuple of two lists: (added, removed). Only files not *already*
55 Returns a tuple of two lists: (added, removed). Only files not *already*
54 marked as moved are included in the added list.
56 marked as moved are included in the added list.
55
57
56 """
58 """
57 stat = repo.status(match=matcher)
59 stat = repo.status(match=matcher)
58 added = stat[1]
60 added = stat[1]
59 removed = stat[2]
61 removed = stat[2]
60
62
61 copy = copies._forwardcopies(repo['.'], repo[None], matcher)
63 copy = copies._forwardcopies(repo['.'], repo[None], matcher)
62 # remove the copy files for which we already have copy info
64 # remove the copy files for which we already have copy info
63 added = [f for f in added if f not in copy]
65 added = [f for f in added if f not in copy]
64
66
65 return added, removed
67 return added, removed
66
68
67 def _findrenames(repo, matcher, added, removed, similarity):
69 def _findrenames(repo, matcher, added, removed, similarity):
68 """Find what files in added are really moved files.
70 """Find what files in added are really moved files.
69
71
70 Any file named in removed that is at least similarity% similar to a file
72 Any file named in removed that is at least similarity% similar to a file
71 in added is seen as a rename.
73 in added is seen as a rename.
72
74
73 """
75 """
74 renames = {}
76 renames = {}
75 if similarity > 0:
77 if similarity > 0:
76 for src, dst, score in similar.findrenames(
78 for src, dst, score in similar.findrenames(
77 repo, added, removed, similarity):
79 repo, added, removed, similarity):
78 if repo.ui.verbose:
80 if repo.ui.verbose:
79 repo.ui.status(
81 repo.ui.status(
80 _('detected move of %s as %s (%d%% similar)\n') % (
82 _('detected move of %s as %s (%d%% similar)\n') % (
81 matcher.rel(src), matcher.rel(dst), score * 100))
83 matcher.rel(src), matcher.rel(dst), score * 100))
82 renames[dst] = src
84 renames[dst] = src
83 if renames:
85 if renames:
84 repo.ui.status(_('detected move of %d files\n') % len(renames))
86 repo.ui.status(_('detected move of %d files\n') % len(renames))
85 return renames
87 return renames
General Comments 0
You need to be logged in to leave comments. Login now