Mercurial > evolve
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() |