##// END OF EJS Templates
automv: use lowercase for docstring title...
Jun Wu -
r31599:e4aefdb5 default
parent child Browse files
Show More
@@ -1,100 +1,100
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. This option takes a percentage between 0
13 ``automv.similarity`` config option. This option takes a percentage between 0
14 (disabled) and 100 (files must be identical), the default is 95.
14 (disabled) and 100 (files must be identical), the default is 95.
15
15
16 """
16 """
17
17
18 # Using 95 as a default similarity is based on an analysis of the mercurial
18 # Using 95 as a default similarity is based on an analysis of the mercurial
19 # repositories of the cpython, mozilla-central & mercurial repositories, as
19 # repositories of the cpython, mozilla-central & mercurial repositories, as
20 # well as 2 very large facebook repositories. At 95 50% of all potential
20 # well as 2 very large facebook repositories. At 95 50% of all potential
21 # missed moves would be caught, as well as correspond with 87% of all
21 # missed moves would be caught, as well as correspond with 87% of all
22 # explicitly marked moves. Together, 80% of moved files are 95% similar or
22 # explicitly marked moves. Together, 80% of moved files are 95% similar or
23 # more.
23 # more.
24 #
24 #
25 # See http://markmail.org/thread/5pxnljesvufvom57 for context.
25 # See http://markmail.org/thread/5pxnljesvufvom57 for context.
26
26
27 from __future__ import absolute_import
27 from __future__ import absolute_import
28
28
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30 from mercurial import (
30 from mercurial import (
31 commands,
31 commands,
32 copies,
32 copies,
33 error,
33 error,
34 extensions,
34 extensions,
35 scmutil,
35 scmutil,
36 similar
36 similar
37 )
37 )
38
38
39 def extsetup(ui):
39 def extsetup(ui):
40 entry = extensions.wrapcommand(
40 entry = extensions.wrapcommand(
41 commands.table, 'commit', mvcheck)
41 commands.table, 'commit', mvcheck)
42 entry[1].append(
42 entry[1].append(
43 ('', 'no-automv', None,
43 ('', 'no-automv', None,
44 _('disable automatic file move detection')))
44 _('disable automatic file move detection')))
45
45
46 def mvcheck(orig, ui, repo, *pats, **opts):
46 def mvcheck(orig, ui, repo, *pats, **opts):
47 """Hook to check for moves at commit time"""
47 """Hook to check for moves at commit time"""
48 renames = None
48 renames = None
49 disabled = opts.pop('no_automv', False)
49 disabled = opts.pop('no_automv', False)
50 if not disabled:
50 if not disabled:
51 threshold = ui.configint('automv', 'similarity', 95)
51 threshold = ui.configint('automv', 'similarity', 95)
52 if not 0 <= threshold <= 100:
52 if not 0 <= threshold <= 100:
53 raise error.Abort(_('automv.similarity must be between 0 and 100'))
53 raise error.Abort(_('automv.similarity must be between 0 and 100'))
54 if threshold > 0:
54 if threshold > 0:
55 match = scmutil.match(repo[None], pats, opts)
55 match = scmutil.match(repo[None], pats, opts)
56 added, removed = _interestingfiles(repo, match)
56 added, removed = _interestingfiles(repo, match)
57 renames = _findrenames(repo, match, added, removed,
57 renames = _findrenames(repo, match, added, removed,
58 threshold / 100.0)
58 threshold / 100.0)
59
59
60 with repo.wlock():
60 with repo.wlock():
61 if renames is not None:
61 if renames is not None:
62 scmutil._markchanges(repo, (), (), renames)
62 scmutil._markchanges(repo, (), (), renames)
63 return orig(ui, repo, *pats, **opts)
63 return orig(ui, repo, *pats, **opts)
64
64
65 def _interestingfiles(repo, matcher):
65 def _interestingfiles(repo, matcher):
66 """Find what files were added or removed in this commit.
66 """Find what files were added or removed in this commit.
67
67
68 Returns a tuple of two lists: (added, removed). Only files not *already*
68 Returns a tuple of two lists: (added, removed). Only files not *already*
69 marked as moved are included in the added list.
69 marked as moved are included in the added list.
70
70
71 """
71 """
72 stat = repo.status(match=matcher)
72 stat = repo.status(match=matcher)
73 added = stat[1]
73 added = stat[1]
74 removed = stat[2]
74 removed = stat[2]
75
75
76 copy = copies._forwardcopies(repo['.'], repo[None], matcher)
76 copy = copies._forwardcopies(repo['.'], repo[None], matcher)
77 # remove the copy files for which we already have copy info
77 # remove the copy files for which we already have copy info
78 added = [f for f in added if f not in copy]
78 added = [f for f in added if f not in copy]
79
79
80 return added, removed
80 return added, removed
81
81
82 def _findrenames(repo, matcher, added, removed, similarity):
82 def _findrenames(repo, matcher, added, removed, similarity):
83 """Find what files in added are really moved files.
83 """Find what files in added are really moved files.
84
84
85 Any file named in removed that is at least similarity% similar to a file
85 Any file named in removed that is at least similarity% similar to a file
86 in added is seen as a rename.
86 in added is seen as a rename.
87
87
88 """
88 """
89 renames = {}
89 renames = {}
90 if similarity > 0:
90 if similarity > 0:
91 for src, dst, score in similar.findrenames(
91 for src, dst, score in similar.findrenames(
92 repo, added, removed, similarity):
92 repo, added, removed, similarity):
93 if repo.ui.verbose:
93 if repo.ui.verbose:
94 repo.ui.status(
94 repo.ui.status(
95 _('detected move of %s as %s (%d%% similar)\n') % (
95 _('detected move of %s as %s (%d%% similar)\n') % (
96 matcher.rel(src), matcher.rel(dst), score * 100))
96 matcher.rel(src), matcher.rel(dst), score * 100))
97 renames[dst] = src
97 renames[dst] = src
98 if renames:
98 if renames:
99 repo.ui.status(_('detected move of %d files\n') % len(renames))
99 repo.ui.status(_('detected move of %d files\n') % len(renames))
100 return renames
100 return renames
General Comments 0
You need to be logged in to leave comments. Login now