##// END OF EJS Templates
automv: use public API for getting copies...
Martin von Zweigbergk -
r42746:abd902a8 default
parent child Browse files
Show More
@@ -1,111 +1,111 b''
1 1 # automv.py
2 2 #
3 3 # Copyright 2013-2016 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """check for unrecorded moves at commit time (EXPERIMENTAL)
8 8
9 9 This extension checks at commit/amend time if any of the committed files
10 10 comes from an unrecorded mv.
11 11
12 12 The threshold at which a file is considered a move can be set with the
13 13 ``automv.similarity`` config option. This option takes a percentage between 0
14 14 (disabled) and 100 (files must be identical), the default is 95.
15 15
16 16 """
17 17
18 18 # Using 95 as a default similarity is based on an analysis of the mercurial
19 19 # repositories of the cpython, mozilla-central & mercurial repositories, as
20 20 # well as 2 very large facebook repositories. At 95 50% of all potential
21 21 # missed moves would be caught, as well as correspond with 87% of all
22 22 # explicitly marked moves. Together, 80% of moved files are 95% similar or
23 23 # more.
24 24 #
25 25 # See http://markmail.org/thread/5pxnljesvufvom57 for context.
26 26
27 27 from __future__ import absolute_import
28 28
29 29 from mercurial.i18n import _
30 30 from mercurial import (
31 31 commands,
32 32 copies,
33 33 error,
34 34 extensions,
35 35 pycompat,
36 36 registrar,
37 37 scmutil,
38 38 similar
39 39 )
40 40
41 41 configtable = {}
42 42 configitem = registrar.configitem(configtable)
43 43
44 44 configitem('automv', 'similarity',
45 45 default=95,
46 46 )
47 47
48 48 def extsetup(ui):
49 49 entry = extensions.wrapcommand(
50 50 commands.table, 'commit', mvcheck)
51 51 entry[1].append(
52 52 ('', 'no-automv', None,
53 53 _('disable automatic file move detection')))
54 54
55 55 def mvcheck(orig, ui, repo, *pats, **opts):
56 56 """Hook to check for moves at commit time"""
57 57 opts = pycompat.byteskwargs(opts)
58 58 renames = None
59 59 disabled = opts.pop('no_automv', False)
60 60 if not disabled:
61 61 threshold = ui.configint('automv', 'similarity')
62 62 if not 0 <= threshold <= 100:
63 63 raise error.Abort(_('automv.similarity must be between 0 and 100'))
64 64 if threshold > 0:
65 65 match = scmutil.match(repo[None], pats, opts)
66 66 added, removed = _interestingfiles(repo, match)
67 67 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
68 68 renames = _findrenames(repo, uipathfn, added, removed,
69 69 threshold / 100.0)
70 70
71 71 with repo.wlock():
72 72 if renames is not None:
73 73 scmutil._markchanges(repo, (), (), renames)
74 74 return orig(ui, repo, *pats, **pycompat.strkwargs(opts))
75 75
76 76 def _interestingfiles(repo, matcher):
77 77 """Find what files were added or removed in this commit.
78 78
79 79 Returns a tuple of two lists: (added, removed). Only files not *already*
80 80 marked as moved are included in the added list.
81 81
82 82 """
83 83 stat = repo.status(match=matcher)
84 84 added = stat[1]
85 85 removed = stat[2]
86 86
87 copy = copies._forwardcopies(repo['.'], repo[None], matcher)
87 copy = copies.pathcopies(repo['.'], repo[None], matcher)
88 88 # remove the copy files for which we already have copy info
89 89 added = [f for f in added if f not in copy]
90 90
91 91 return added, removed
92 92
93 93 def _findrenames(repo, uipathfn, added, removed, similarity):
94 94 """Find what files in added are really moved files.
95 95
96 96 Any file named in removed that is at least similarity% similar to a file
97 97 in added is seen as a rename.
98 98
99 99 """
100 100 renames = {}
101 101 if similarity > 0:
102 102 for src, dst, score in similar.findrenames(
103 103 repo, added, removed, similarity):
104 104 if repo.ui.verbose:
105 105 repo.ui.status(
106 106 _('detected move of %s as %s (%d%% similar)\n') % (
107 107 uipathfn(src), uipathfn(dst), score * 100))
108 108 renames[dst] = src
109 109 if renames:
110 110 repo.ui.status(_('detected move of %d files\n') % len(renames))
111 111 return renames
General Comments 0
You need to be logged in to leave comments. Login now