comparison hgext/directaccess.py @ 1450:5f6e78aea094 stable

merge with stable Test are updated to run with current mercurial stable (3.4.1)
author Pierre-Yves David <pierre-yves.david@fb.com>
date Thu, 25 Jun 2015 16:55:27 -0700
parents 7023a01b9007
children 2f8a4d496585
comparison
equal deleted inserted replaced
1438:3295353b1363 1450:5f6e78aea094
1 """ This extension provides direct access
2 It is the ability to refer and access hidden sha in commands provided that you
3 know their value.
4 For example hg log -r xxx where xxx is a commit has should work whether xxx is
5 hidden or not as we assume that the user knows what he is doing when referring
6 to xxx.
7 """
8 from mercurial import extensions
9 from mercurial import cmdutil
10 from mercurial import repoview
11 from mercurial import branchmap
12 from mercurial import revset
13 from mercurial import error
14 from mercurial import commands
15 from mercurial import hg
16 from mercurial.i18n import _
17
18 cmdtable = {}
19 command = cmdutil.command(cmdtable)
20
21 # By default, all the commands have directaccess with warnings
22 # List of commands that have no directaccess and directaccess with no warning
23 directaccesslevel = [
24 # Format:
25 # ('nowarning', 'evolve', 'prune'),
26 # means: no directaccess warning, for the command in evolve named prune
27 #
28 # ('error', None, 'serve'),
29 # means: no directaccess for the command in core named serve
30 #
31 # The list is ordered alphabetically by command names, starting with all
32 # the commands in core then all the commands in the extensions
33 #
34 # The general guideline is:
35 # - remove directaccess warnings for read only commands
36 # - no direct access for commands with consequences outside of the repo
37 # - leave directaccess warnings for all the other commands
38 #
39 ('nowarning', None, 'annotate'),
40 ('nowarning', None, 'archive'),
41 ('nowarning', None, 'bisect'),
42 ('nowarning', None, 'bookmarks'),
43 ('nowarning', None, 'bundle'),
44 ('nowarning', None, 'cat'),
45 ('nowarning', None, 'diff'),
46 ('nowarning', None, 'export'),
47 ('nowarning', None, 'identify'),
48 ('nowarning', None, 'incoming'),
49 ('nowarning', None, 'log'),
50 ('nowarning', None, 'manifest'),
51 ('error', None, 'outgoing'), # confusing if push errors and not outgoing
52 ('error', None, 'push'), # destructive
53 ('nowarning', None, 'revert'),
54 ('error', None, 'serve'),
55 ('nowarning', None, 'tags'),
56 ('nowarning', None, 'unbundle'),
57 ('nowarning', None, 'update'),
58 ]
59
60 def reposetup(ui, repo):
61 repo._explicitaccess = set()
62
63 def _computehidden(repo):
64 hidden = repoview.filterrevs(repo, 'visible')
65 cl = repo.changelog
66 dynamic = hidden & repo._explicitaccess
67 if dynamic:
68 blocked = cl.ancestors(dynamic, inclusive=True)
69 hidden = frozenset(r for r in hidden if r not in blocked)
70 return hidden
71
72 def setupdirectaccess():
73 """ Add two new filtername that behave like visible to provide direct access
74 and direct access with warning. Wraps the commands to setup direct access """
75 repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden})
76 repoview.filtertable.update({'visible-directaccess-warn': _computehidden})
77 branchmap.subsettable['visible-directaccess-nowarn'] = 'visible'
78 branchmap.subsettable['visible-directaccess-warn'] = 'visible'
79
80 for warn, ext, cmd in directaccesslevel:
81 try:
82 cmdtable = extensions.find(ext).cmdtable if ext else commands.table
83 wrapper = wrapwitherror if warn == 'error' else wrapwithoutwarning
84 extensions.wrapcommand(cmdtable, cmd, wrapper)
85 except (error.UnknownCommand, KeyError):
86 pass
87
88 def wrapwitherror(orig, ui, repo, *args, **kwargs):
89 if repo and repo.filtername == 'visible-directaccess-warn':
90 repo = repo.filtered('visible')
91 return orig(ui, repo, *args, **kwargs)
92
93 def wrapwithoutwarning(orig, ui, repo, *args, **kwargs):
94 if repo and repo.filtername == 'visible-directaccess-warn':
95 repo = repo.filtered("visible-directaccess-nowarn")
96 return orig(ui, repo, *args, **kwargs)
97
98 def uisetup(ui):
99 """ Change ordering of extensions to ensure that directaccess extsetup comes
100 after the one of the extensions in the loadsafter list """
101 loadsafter = ui.configlist('directaccess','loadsafter')
102 order = list(extensions._order)
103 directaccesidx = order.index('directaccess')
104
105 # The min idx for directaccess to load after all the extensions in loadafter
106 minidxdirectaccess = directaccesidx
107
108 for ext in loadsafter:
109 try:
110 minidxdirectaccess = max(minidxdirectaccess, order.index(ext))
111 except ValueError:
112 pass # extension not loaded
113
114 if minidxdirectaccess > directaccesidx:
115 order.insert(minidxdirectaccess + 1, 'directaccess')
116 order.remove('directaccess')
117 extensions._order = order
118
119 def _repository(orig, *args, **kwargs):
120 """Make visible-directaccess-warn the default filter for new repos"""
121 repo = orig(*args, **kwargs)
122 return repo.filtered("visible-directaccess-warn")
123
124 def extsetup(ui):
125 extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
126 extensions.wrapfunction(hg, 'repository', _repository)
127 setupdirectaccess()
128
129 def gethashsymbols(tree):
130 # Returns the list of symbols of the tree that look like hashes
131 # for example for the revset 3::abe3ff it will return ('abe3ff')
132 if not tree:
133 return []
134
135 if len(tree) == 2 and tree[0] == "symbol":
136 try:
137 int(tree[1])
138 return []
139 except ValueError as e:
140 return [tree[1]]
141 elif len(tree) == 3:
142 return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
143 else:
144 return []
145
146 def _posttreebuilthook(orig, tree, repo):
147 # This is use to enabled direct hash access
148 # We extract the symbols that look like hashes and add them to the
149 # explicitaccess set
150 orig(tree, repo)
151 filternm = ""
152 if repo is not None:
153 filternm = repo.filtername
154 if filternm is not None and filternm.startswith('visible-directaccess'):
155 prelength = len(repo._explicitaccess)
156 accessbefore = set(repo._explicitaccess)
157 repo.symbols = gethashsymbols(tree)
158 cl = repo.unfiltered().changelog
159 for node in repo.symbols:
160 try:
161 node = cl._partialmatch(node)
162 except error.LookupError:
163 node = None
164 if node is not None:
165 rev = cl.rev(node)
166 if rev not in repo.changelog:
167 repo._explicitaccess.add(rev)
168 if prelength != len(repo._explicitaccess):
169 if repo.filtername != 'visible-directaccess-nowarn':
170 unhiddencommits = repo._explicitaccess - accessbefore
171 repo.ui.warn( _("Warning: accessing hidden changesets %s "
172 "for write operation\n") %
173 (",".join([str(repo.unfiltered()[l])
174 for l in unhiddencommits])))
175 repo.invalidatevolatilesets()