##// END OF EJS Templates
vcs: added similar assert_path method as other backend have for API consistency
super-admin -
r1311:6c127320 default
parent child Browse files
Show More
@@ -1,1217 +1,1236 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2023 RhodeCode GmbH
2 # Copyright (C) 2014-2023 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import binascii
18 import binascii
19 import io
19 import io
20 import logging
20 import logging
21 import stat
21 import stat
22 import sys
22 import sys
23 import urllib.request
23 import urllib.request
24 import urllib.parse
24 import urllib.parse
25 import hashlib
25 import hashlib
26 import traceback
26
27
27 from hgext import largefiles, rebase
28 from hgext import largefiles, rebase
28
29
29 from mercurial import commands
30 from mercurial import commands
30 from mercurial import unionrepo
31 from mercurial import unionrepo
31 from mercurial import verify
32 from mercurial import verify
32 from mercurial import repair
33 from mercurial import repair
33 from mercurial.error import AmbiguousPrefixLookupError
34 from mercurial.error import AmbiguousPrefixLookupError
34 from mercurial.utils.urlutil import path as hg_path
35 from mercurial.utils.urlutil import path as hg_path
35
36
36 import vcsserver
37 import vcsserver
37 from vcsserver import exceptions
38 from vcsserver import exceptions
38 from vcsserver.base import (
39 from vcsserver.base import (
39 RepoFactory,
40 RepoFactory,
40 obfuscate_qs,
41 obfuscate_qs,
41 raise_from_original,
42 raise_from_original,
42 store_archive_in_cache,
43 store_archive_in_cache,
43 ArchiveNode,
44 ArchiveNode,
44 BytesEnvelope,
45 BytesEnvelope,
45 BinaryEnvelope,
46 BinaryEnvelope,
46 )
47 )
47 from vcsserver.hgcompat import (
48 from vcsserver.hgcompat import (
48 archival,
49 archival,
49 bin,
50 bin,
50 clone,
51 clone,
51 config as hgconfig,
52 config as hgconfig,
52 diffopts,
53 diffopts,
53 hex,
54 hex,
54 get_ctx,
55 get_ctx,
55 hg_url as url_parser,
56 hg_url as url_parser,
56 httpbasicauthhandler,
57 httpbasicauthhandler,
57 httpdigestauthhandler,
58 httpdigestauthhandler,
58 make_peer,
59 make_peer,
59 instance,
60 instance,
60 match,
61 match,
61 memctx,
62 memctx,
62 exchange,
63 exchange,
63 memfilectx,
64 memfilectx,
64 nullrev,
65 nullrev,
65 hg_merge,
66 hg_merge,
66 patch,
67 patch,
67 peer,
68 peer,
68 revrange,
69 revrange,
69 ui,
70 ui,
70 hg_tag,
71 hg_tag,
71 Abort,
72 Abort,
72 LookupError,
73 LookupError,
73 RepoError,
74 RepoError,
74 RepoLookupError,
75 RepoLookupError,
75 InterventionRequired,
76 InterventionRequired,
76 RequirementError,
77 RequirementError,
77 alwaysmatcher,
78 alwaysmatcher,
78 patternmatcher,
79 patternmatcher,
79 hgext_strip,
80 hgext_strip,
80 )
81 )
81 from vcsserver.lib.str_utils import ascii_bytes, ascii_str, safe_str, safe_bytes, convert_to_str
82 from vcsserver.lib.str_utils import ascii_bytes, ascii_str, safe_str, safe_bytes, convert_to_str
82 from vcsserver.vcs_base import RemoteBase
83 from vcsserver.vcs_base import RemoteBase
83 from vcsserver.config import hooks as hooks_config
84 from vcsserver.config import hooks as hooks_config
84 from vcsserver.lib.exc_tracking import format_exc
85 from vcsserver.lib.exc_tracking import format_exc
85
86
86 log = logging.getLogger(__name__)
87 log = logging.getLogger(__name__)
87
88
88
89
89 def make_ui_from_config(repo_config, interactive=True):
90 def make_ui_from_config(repo_config, interactive=True):
90
91
91 class LoggingUI(ui.ui):
92 class LoggingUI(ui.ui):
92
93
93 def status(self, *msg, **opts):
94 def status(self, *msg, **opts):
94 str_msg = map(safe_str, msg)
95 str_msg = map(safe_str, msg)
95 log.info(' '.join(str_msg).rstrip('\n'))
96 log.info(' '.join(str_msg).rstrip('\n'))
96 #super(LoggingUI, self).status(*msg, **opts)
97 #super(LoggingUI, self).status(*msg, **opts)
97
98
98 def warn(self, *msg, **opts):
99 def warn(self, *msg, **opts):
99 str_msg = map(safe_str, msg)
100 str_msg = map(safe_str, msg)
100 log.warning('ui_logger:'+' '.join(str_msg).rstrip('\n'))
101 log.warning('ui_logger:'+' '.join(str_msg).rstrip('\n'))
101 #super(LoggingUI, self).warn(*msg, **opts)
102 #super(LoggingUI, self).warn(*msg, **opts)
102
103
103 def error(self, *msg, **opts):
104 def error(self, *msg, **opts):
104 str_msg = map(safe_str, msg)
105 str_msg = map(safe_str, msg)
105 log.error('ui_logger:'+' '.join(str_msg).rstrip('\n'))
106 log.error('ui_logger:'+' '.join(str_msg).rstrip('\n'))
106 #super(LoggingUI, self).error(*msg, **opts)
107 #super(LoggingUI, self).error(*msg, **opts)
107
108
108 def note(self, *msg, **opts):
109 def note(self, *msg, **opts):
109 str_msg = map(safe_str, msg)
110 str_msg = map(safe_str, msg)
110 log.info('ui_logger:'+' '.join(str_msg).rstrip('\n'))
111 log.info('ui_logger:'+' '.join(str_msg).rstrip('\n'))
111 #super(LoggingUI, self).note(*msg, **opts)
112 #super(LoggingUI, self).note(*msg, **opts)
112
113
113 def debug(self, *msg, **opts):
114 def debug(self, *msg, **opts):
114 str_msg = map(safe_str, msg)
115 str_msg = map(safe_str, msg)
115 log.debug('ui_logger:'+' '.join(str_msg).rstrip('\n'))
116 log.debug('ui_logger:'+' '.join(str_msg).rstrip('\n'))
116 #super(LoggingUI, self).debug(*msg, **opts)
117 #super(LoggingUI, self).debug(*msg, **opts)
117
118
118 baseui = LoggingUI()
119 baseui = LoggingUI()
119
120
120 # clean the baseui object
121 # clean the baseui object
121 baseui._ocfg = hgconfig.config()
122 baseui._ocfg = hgconfig.config()
122 baseui._ucfg = hgconfig.config()
123 baseui._ucfg = hgconfig.config()
123 baseui._tcfg = hgconfig.config()
124 baseui._tcfg = hgconfig.config()
124
125
125 for section, option, value in repo_config:
126 for section, option, value in repo_config:
126 baseui.setconfig(ascii_bytes(section), ascii_bytes(option), ascii_bytes(value))
127 baseui.setconfig(ascii_bytes(section), ascii_bytes(option), ascii_bytes(value))
127
128
128 # make our hgweb quiet so it doesn't print output
129 # make our hgweb quiet so it doesn't print output
129 baseui.setconfig(b'ui', b'quiet', b'true')
130 baseui.setconfig(b'ui', b'quiet', b'true')
130
131
131 baseui.setconfig(b'ui', b'paginate', b'never')
132 baseui.setconfig(b'ui', b'paginate', b'never')
132 # for better Error reporting of Mercurial
133 # for better Error reporting of Mercurial
133 baseui.setconfig(b'ui', b'message-output', b'stderr')
134 baseui.setconfig(b'ui', b'message-output', b'stderr')
134
135
135 # force mercurial to only use 1 thread, otherwise it may try to set a
136 # force mercurial to only use 1 thread, otherwise it may try to set a
136 # signal in a non-main thread, thus generating a ValueError.
137 # signal in a non-main thread, thus generating a ValueError.
137 baseui.setconfig(b'worker', b'numcpus', 1)
138 baseui.setconfig(b'worker', b'numcpus', 1)
138
139
139 # If there is no config for the largefiles extension, we explicitly disable
140 # If there is no config for the largefiles extension, we explicitly disable
140 # it here. This overrides settings from repositories hgrc file. Recent
141 # it here. This overrides settings from repositories hgrc file. Recent
141 # mercurial versions enable largefiles in hgrc on clone from largefile
142 # mercurial versions enable largefiles in hgrc on clone from largefile
142 # repo.
143 # repo.
143 if not baseui.hasconfig(b'extensions', b'largefiles'):
144 if not baseui.hasconfig(b'extensions', b'largefiles'):
144 log.debug('Explicitly disable largefiles extension for repo.')
145 log.debug('Explicitly disable largefiles extension for repo.')
145 baseui.setconfig(b'extensions', b'largefiles', b'!')
146 baseui.setconfig(b'extensions', b'largefiles', b'!')
146
147
147 baseui.setconfig(b'ui', b'interactive', b'true' if interactive else b'false')
148 baseui.setconfig(b'ui', b'interactive', b'true' if interactive else b'false')
148 return baseui
149 return baseui
149
150
150
151
151 def reraise_safe_exceptions(func):
152 def reraise_safe_exceptions(func):
152 """Decorator for converting mercurial exceptions to something neutral."""
153 """Decorator for converting mercurial exceptions to something neutral."""
153
154
154 def wrapper(*args, **kwargs):
155 def wrapper(*args, **kwargs):
155 try:
156 try:
156 return func(*args, **kwargs)
157 return func(*args, **kwargs)
157 except (Abort, InterventionRequired) as e:
158 except (Abort, InterventionRequired) as e:
158 raise_from_original(exceptions.AbortException(e), e)
159 raise_from_original(exceptions.AbortException(e), e)
159 except RepoLookupError as e:
160 except RepoLookupError as e:
160 raise_from_original(exceptions.LookupException(e), e)
161 raise_from_original(exceptions.LookupException(e), e)
161 except RequirementError as e:
162 except RequirementError as e:
162 raise_from_original(exceptions.RequirementException(e), e)
163 raise_from_original(exceptions.RequirementException(e), e)
163 except RepoError as e:
164 except RepoError as e:
164 raise_from_original(exceptions.VcsException(e), e)
165 raise_from_original(exceptions.VcsException(e), e)
165 except LookupError as e:
166 except LookupError as e:
166 raise_from_original(exceptions.LookupException(e), e)
167 raise_from_original(exceptions.LookupException(e), e)
167 except Exception as e:
168 except Exception as e:
168 if not hasattr(e, '_vcs_kind'):
169 if not hasattr(e, '_vcs_kind'):
169 log.exception("Unhandled exception in hg remote call")
170 log.exception("Unhandled exception in hg remote call")
170 raise_from_original(exceptions.UnhandledException(e), e)
171 raise_from_original(exceptions.UnhandledException(e), e)
171
172
172 raise
173 raise
173 return wrapper
174 return wrapper
174
175
175
176
176 class MercurialFactory(RepoFactory):
177 class MercurialFactory(RepoFactory):
177 repo_type = 'hg'
178 repo_type = 'hg'
178
179
179 def _create_config(self, config, hooks=True):
180 def _create_config(self, config, hooks=True):
180 if not hooks:
181 if not hooks:
181
182
182 hooks_to_clean = {
183 hooks_to_clean = {
183
184
184 hooks_config.HOOK_REPO_SIZE,
185 hooks_config.HOOK_REPO_SIZE,
185 hooks_config.HOOK_PRE_PULL,
186 hooks_config.HOOK_PRE_PULL,
186 hooks_config.HOOK_PULL,
187 hooks_config.HOOK_PULL,
187
188
188 hooks_config.HOOK_PRE_PUSH,
189 hooks_config.HOOK_PRE_PUSH,
189 # TODO: what about PRETXT, this was disabled in pre 5.0.0
190 # TODO: what about PRETXT, this was disabled in pre 5.0.0
190 hooks_config.HOOK_PRETX_PUSH,
191 hooks_config.HOOK_PRETX_PUSH,
191
192
192 }
193 }
193 new_config = []
194 new_config = []
194 for section, option, value in config:
195 for section, option, value in config:
195 if section == 'hooks' and option in hooks_to_clean:
196 if section == 'hooks' and option in hooks_to_clean:
196 continue
197 continue
197 new_config.append((section, option, value))
198 new_config.append((section, option, value))
198 config = new_config
199 config = new_config
199
200
200 baseui = make_ui_from_config(config)
201 baseui = make_ui_from_config(config)
201 return baseui
202 return baseui
202
203
203 def _create_repo(self, wire, create):
204 def _create_repo(self, wire, create):
204 baseui = self._create_config(wire["config"])
205 baseui = self._create_config(wire["config"])
205 repo = instance(baseui, safe_bytes(wire["path"]), create)
206 repo = instance(baseui, safe_bytes(wire["path"]), create)
206 log.debug('repository created: got HG object: %s', repo)
207 log.debug('repository created: got HG object: %s', repo)
207 return repo
208 return repo
208
209
209 def repo(self, wire, create=False):
210 def repo(self, wire, create=False):
210 """
211 """
211 Get a repository instance for the given path.
212 Get a repository instance for the given path.
212 """
213 """
213 return self._create_repo(wire, create)
214 return self._create_repo(wire, create)
214
215
215
216
216 def patch_ui_message_output(baseui):
217 def patch_ui_message_output(baseui):
217 baseui.setconfig(b'ui', b'quiet', b'false')
218 baseui.setconfig(b'ui', b'quiet', b'false')
218 output = io.BytesIO()
219 output = io.BytesIO()
219
220
220 def write(data, **unused_kwargs):
221 def write(data, **unused_kwargs):
221 output.write(data)
222 output.write(data)
222
223
223 baseui.status = write
224 baseui.status = write
224 baseui.write = write
225 baseui.write = write
225 baseui.warn = write
226 baseui.warn = write
226 baseui.debug = write
227 baseui.debug = write
227
228
228 return baseui, output
229 return baseui, output
229
230
230
231
231 def get_obfuscated_url(url_obj):
232 def get_obfuscated_url(url_obj):
232 url_obj.passwd = b'*****' if url_obj.passwd else url_obj.passwd
233 url_obj.passwd = b'*****' if url_obj.passwd else url_obj.passwd
233 url_obj.query = obfuscate_qs(url_obj.query)
234 url_obj.query = obfuscate_qs(url_obj.query)
234 obfuscated_uri = str(url_obj)
235 obfuscated_uri = str(url_obj)
235 return obfuscated_uri
236 return obfuscated_uri
236
237
237
238
238 def normalize_url_for_hg(url: str):
239 def normalize_url_for_hg(url: str):
239 _proto = None
240 _proto = None
240
241
241 if '+' in url[:url.find('://')]:
242 if '+' in url[:url.find('://')]:
242 _proto = url[0:url.find('+')]
243 _proto = url[0:url.find('+')]
243 url = url[url.find('+') + 1:]
244 url = url[url.find('+') + 1:]
244 return url, _proto
245 return url, _proto
245
246
246
247
247 class HgRemote(RemoteBase):
248 class HgRemote(RemoteBase):
248
249
249 def __init__(self, factory):
250 def __init__(self, factory):
250 self._factory = factory
251 self._factory = factory
251 self._bulk_methods = {
252 self._bulk_methods = {
252 "affected_files": self.ctx_files,
253 "affected_files": self.ctx_files,
253 "author": self.ctx_user,
254 "author": self.ctx_user,
254 "branch": self.ctx_branch,
255 "branch": self.ctx_branch,
255 "children": self.ctx_children,
256 "children": self.ctx_children,
256 "date": self.ctx_date,
257 "date": self.ctx_date,
257 "message": self.ctx_description,
258 "message": self.ctx_description,
258 "parents": self.ctx_parents,
259 "parents": self.ctx_parents,
259 "status": self.ctx_status,
260 "status": self.ctx_status,
260 "obsolete": self.ctx_obsolete,
261 "obsolete": self.ctx_obsolete,
261 "phase": self.ctx_phase,
262 "phase": self.ctx_phase,
262 "hidden": self.ctx_hidden,
263 "hidden": self.ctx_hidden,
263 "_file_paths": self.ctx_list,
264 "_file_paths": self.ctx_list,
264 }
265 }
265 self._bulk_file_methods = {
266 self._bulk_file_methods = {
266 "size": self.fctx_size,
267 "size": self.fctx_size,
267 "data": self.fctx_node_data,
268 "data": self.fctx_node_data,
268 "flags": self.fctx_flags,
269 "flags": self.fctx_flags,
269 "is_binary": self.is_binary,
270 "is_binary": self.is_binary,
270 "md5": self.md5_hash,
271 "md5": self.md5_hash,
271 }
272 }
272
273
273 def _get_ctx(self, repo, ref):
274 def _get_ctx(self, repo, ref):
274 return get_ctx(repo, ref)
275 return get_ctx(repo, ref)
275
276
276 @reraise_safe_exceptions
277 @reraise_safe_exceptions
277 def discover_hg_version(self):
278 def discover_hg_version(self):
278 from mercurial import util
279 from mercurial import util
279 return safe_str(util.version())
280 return safe_str(util.version())
280
281
281 @reraise_safe_exceptions
282 @reraise_safe_exceptions
282 def is_empty(self, wire):
283 def is_empty(self, wire):
283 repo = self._factory.repo(wire)
284 repo = self._factory.repo(wire)
284
285
285 try:
286 try:
286 return len(repo) == 0
287 return len(repo) == 0
287 except Exception:
288 except Exception:
288 log.exception("failed to read object_store")
289 log.exception("failed to read object_store")
289 return False
290 return False
290
291
291 @reraise_safe_exceptions
292 @reraise_safe_exceptions
292 def bookmarks(self, wire):
293 def bookmarks(self, wire):
293 cache_on, context_uid, repo_id = self._cache_on(wire)
294 cache_on, context_uid, repo_id = self._cache_on(wire)
294 region = self._region(wire)
295 region = self._region(wire)
295
296
296 @region.conditional_cache_on_arguments(condition=cache_on)
297 @region.conditional_cache_on_arguments(condition=cache_on)
297 def _bookmarks(_context_uid, _repo_id):
298 def _bookmarks(_context_uid, _repo_id):
298 repo = self._factory.repo(wire)
299 repo = self._factory.repo(wire)
299 return {safe_str(name): ascii_str(hex(sha)) for name, sha in repo._bookmarks.items()}
300 return {safe_str(name): ascii_str(hex(sha)) for name, sha in repo._bookmarks.items()}
300
301
301 return _bookmarks(context_uid, repo_id)
302 return _bookmarks(context_uid, repo_id)
302
303
303 @reraise_safe_exceptions
304 @reraise_safe_exceptions
304 def branches(self, wire, normal, closed):
305 def branches(self, wire, normal, closed):
305 cache_on, context_uid, repo_id = self._cache_on(wire)
306 cache_on, context_uid, repo_id = self._cache_on(wire)
306 region = self._region(wire)
307 region = self._region(wire)
307
308
308 @region.conditional_cache_on_arguments(condition=cache_on)
309 @region.conditional_cache_on_arguments(condition=cache_on)
309 def _branches(_context_uid, _repo_id, _normal, _closed):
310 def _branches(_context_uid, _repo_id, _normal, _closed):
310 repo = self._factory.repo(wire)
311 repo = self._factory.repo(wire)
311 iter_branches = repo.branchmap().iterbranches()
312 iter_branches = repo.branchmap().iterbranches()
312 bt = {}
313 bt = {}
313 for branch_name, _heads, tip_node, is_closed in iter_branches:
314 for branch_name, _heads, tip_node, is_closed in iter_branches:
314 if normal and not is_closed:
315 if normal and not is_closed:
315 bt[safe_str(branch_name)] = ascii_str(hex(tip_node))
316 bt[safe_str(branch_name)] = ascii_str(hex(tip_node))
316 if closed and is_closed:
317 if closed and is_closed:
317 bt[safe_str(branch_name)] = ascii_str(hex(tip_node))
318 bt[safe_str(branch_name)] = ascii_str(hex(tip_node))
318
319
319 return bt
320 return bt
320
321
321 return _branches(context_uid, repo_id, normal, closed)
322 return _branches(context_uid, repo_id, normal, closed)
322
323
323 @reraise_safe_exceptions
324 @reraise_safe_exceptions
324 def bulk_request(self, wire, commit_id, pre_load):
325 def bulk_request(self, wire, commit_id, pre_load):
325 cache_on, context_uid, repo_id = self._cache_on(wire)
326 cache_on, context_uid, repo_id = self._cache_on(wire)
326 region = self._region(wire)
327 region = self._region(wire)
327
328
328 @region.conditional_cache_on_arguments(condition=cache_on)
329 @region.conditional_cache_on_arguments(condition=cache_on)
329 def _bulk_request(_repo_id, _commit_id, _pre_load):
330 def _bulk_request(_repo_id, _commit_id, _pre_load):
330 result = {}
331 result = {}
331 for attr in pre_load:
332 for attr in pre_load:
332 try:
333 try:
333 method = self._bulk_methods[attr]
334 method = self._bulk_methods[attr]
334 wire.update({'cache': False}) # disable cache for bulk calls so we don't double cache
335 wire.update({'cache': False}) # disable cache for bulk calls so we don't double cache
335 result[attr] = method(wire, commit_id)
336 result[attr] = method(wire, commit_id)
336 except KeyError as e:
337 except KeyError as e:
337 raise exceptions.VcsException(e)(
338 raise exceptions.VcsException(e)(
338 f'Unknown bulk attribute: "{attr}"')
339 f'Unknown bulk attribute: "{attr}"')
339 return result
340 return result
340
341
341 return _bulk_request(repo_id, commit_id, sorted(pre_load))
342 return _bulk_request(repo_id, commit_id, sorted(pre_load))
342
343
343 @reraise_safe_exceptions
344 @reraise_safe_exceptions
344 def ctx_branch(self, wire, commit_id):
345 def ctx_branch(self, wire, commit_id):
345 cache_on, context_uid, repo_id = self._cache_on(wire)
346 cache_on, context_uid, repo_id = self._cache_on(wire)
346 region = self._region(wire)
347 region = self._region(wire)
347
348
348 @region.conditional_cache_on_arguments(condition=cache_on)
349 @region.conditional_cache_on_arguments(condition=cache_on)
349 def _ctx_branch(_repo_id, _commit_id):
350 def _ctx_branch(_repo_id, _commit_id):
350 repo = self._factory.repo(wire)
351 repo = self._factory.repo(wire)
351 ctx = self._get_ctx(repo, commit_id)
352 ctx = self._get_ctx(repo, commit_id)
352 return ctx.branch()
353 return ctx.branch()
353 return _ctx_branch(repo_id, commit_id)
354 return _ctx_branch(repo_id, commit_id)
354
355
355 @reraise_safe_exceptions
356 @reraise_safe_exceptions
356 def ctx_date(self, wire, commit_id):
357 def ctx_date(self, wire, commit_id):
357 cache_on, context_uid, repo_id = self._cache_on(wire)
358 cache_on, context_uid, repo_id = self._cache_on(wire)
358 region = self._region(wire)
359 region = self._region(wire)
359
360
360 @region.conditional_cache_on_arguments(condition=cache_on)
361 @region.conditional_cache_on_arguments(condition=cache_on)
361 def _ctx_date(_repo_id, _commit_id):
362 def _ctx_date(_repo_id, _commit_id):
362 repo = self._factory.repo(wire)
363 repo = self._factory.repo(wire)
363 ctx = self._get_ctx(repo, commit_id)
364 ctx = self._get_ctx(repo, commit_id)
364 return ctx.date()
365 return ctx.date()
365 return _ctx_date(repo_id, commit_id)
366 return _ctx_date(repo_id, commit_id)
366
367
367 @reraise_safe_exceptions
368 @reraise_safe_exceptions
368 def ctx_description(self, wire, revision):
369 def ctx_description(self, wire, revision):
369 repo = self._factory.repo(wire)
370 repo = self._factory.repo(wire)
370 ctx = self._get_ctx(repo, revision)
371 ctx = self._get_ctx(repo, revision)
371 return ctx.description()
372 return ctx.description()
372
373
373 @reraise_safe_exceptions
374 @reraise_safe_exceptions
374 def ctx_files(self, wire, commit_id):
375 def ctx_files(self, wire, commit_id):
375 cache_on, context_uid, repo_id = self._cache_on(wire)
376 cache_on, context_uid, repo_id = self._cache_on(wire)
376 region = self._region(wire)
377 region = self._region(wire)
377
378
378 @region.conditional_cache_on_arguments(condition=cache_on)
379 @region.conditional_cache_on_arguments(condition=cache_on)
379 def _ctx_files(_repo_id, _commit_id):
380 def _ctx_files(_repo_id, _commit_id):
380 repo = self._factory.repo(wire)
381 repo = self._factory.repo(wire)
381 ctx = self._get_ctx(repo, commit_id)
382 ctx = self._get_ctx(repo, commit_id)
382 return ctx.files()
383 return ctx.files()
383
384
384 return _ctx_files(repo_id, commit_id)
385 return _ctx_files(repo_id, commit_id)
385
386
386 @reraise_safe_exceptions
387 @reraise_safe_exceptions
387 def ctx_list(self, path, revision):
388 def ctx_list(self, path, revision):
388 repo = self._factory.repo(path)
389 repo = self._factory.repo(path)
389 ctx = self._get_ctx(repo, revision)
390 ctx = self._get_ctx(repo, revision)
390 return list(ctx)
391 return list(ctx)
391
392
392 @reraise_safe_exceptions
393 @reraise_safe_exceptions
393 def ctx_parents(self, wire, commit_id):
394 def ctx_parents(self, wire, commit_id):
394 cache_on, context_uid, repo_id = self._cache_on(wire)
395 cache_on, context_uid, repo_id = self._cache_on(wire)
395 region = self._region(wire)
396 region = self._region(wire)
396
397
397 @region.conditional_cache_on_arguments(condition=cache_on)
398 @region.conditional_cache_on_arguments(condition=cache_on)
398 def _ctx_parents(_repo_id, _commit_id):
399 def _ctx_parents(_repo_id, _commit_id):
399 repo = self._factory.repo(wire)
400 repo = self._factory.repo(wire)
400 ctx = self._get_ctx(repo, commit_id)
401 ctx = self._get_ctx(repo, commit_id)
401 return [parent.hex() for parent in ctx.parents()
402 return [parent.hex() for parent in ctx.parents()
402 if not (parent.hidden() or parent.obsolete())]
403 if not (parent.hidden() or parent.obsolete())]
403
404
404 return _ctx_parents(repo_id, commit_id)
405 return _ctx_parents(repo_id, commit_id)
405
406
406 @reraise_safe_exceptions
407 @reraise_safe_exceptions
407 def ctx_children(self, wire, commit_id):
408 def ctx_children(self, wire, commit_id):
408 cache_on, context_uid, repo_id = self._cache_on(wire)
409 cache_on, context_uid, repo_id = self._cache_on(wire)
409 region = self._region(wire)
410 region = self._region(wire)
410
411
411 @region.conditional_cache_on_arguments(condition=cache_on)
412 @region.conditional_cache_on_arguments(condition=cache_on)
412 def _ctx_children(_repo_id, _commit_id):
413 def _ctx_children(_repo_id, _commit_id):
413 repo = self._factory.repo(wire)
414 repo = self._factory.repo(wire)
414 ctx = self._get_ctx(repo, commit_id)
415 ctx = self._get_ctx(repo, commit_id)
415 return [child.hex() for child in ctx.children()
416 return [child.hex() for child in ctx.children()
416 if not (child.hidden() or child.obsolete())]
417 if not (child.hidden() or child.obsolete())]
417
418
418 return _ctx_children(repo_id, commit_id)
419 return _ctx_children(repo_id, commit_id)
419
420
420 @reraise_safe_exceptions
421 @reraise_safe_exceptions
421 def ctx_phase(self, wire, commit_id):
422 def ctx_phase(self, wire, commit_id):
422 cache_on, context_uid, repo_id = self._cache_on(wire)
423 cache_on, context_uid, repo_id = self._cache_on(wire)
423 region = self._region(wire)
424 region = self._region(wire)
424
425
425 @region.conditional_cache_on_arguments(condition=cache_on)
426 @region.conditional_cache_on_arguments(condition=cache_on)
426 def _ctx_phase(_context_uid, _repo_id, _commit_id):
427 def _ctx_phase(_context_uid, _repo_id, _commit_id):
427 repo = self._factory.repo(wire)
428 repo = self._factory.repo(wire)
428 ctx = self._get_ctx(repo, commit_id)
429 ctx = self._get_ctx(repo, commit_id)
429 # public=0, draft=1, secret=3
430 # public=0, draft=1, secret=3
430 return ctx.phase()
431 return ctx.phase()
431 return _ctx_phase(context_uid, repo_id, commit_id)
432 return _ctx_phase(context_uid, repo_id, commit_id)
432
433
433 @reraise_safe_exceptions
434 @reraise_safe_exceptions
434 def ctx_obsolete(self, wire, commit_id):
435 def ctx_obsolete(self, wire, commit_id):
435 cache_on, context_uid, repo_id = self._cache_on(wire)
436 cache_on, context_uid, repo_id = self._cache_on(wire)
436 region = self._region(wire)
437 region = self._region(wire)
437
438
438 @region.conditional_cache_on_arguments(condition=cache_on)
439 @region.conditional_cache_on_arguments(condition=cache_on)
439 def _ctx_obsolete(_context_uid, _repo_id, _commit_id):
440 def _ctx_obsolete(_context_uid, _repo_id, _commit_id):
440 repo = self._factory.repo(wire)
441 repo = self._factory.repo(wire)
441 ctx = self._get_ctx(repo, commit_id)
442 ctx = self._get_ctx(repo, commit_id)
442 return ctx.obsolete()
443 return ctx.obsolete()
443 return _ctx_obsolete(context_uid, repo_id, commit_id)
444 return _ctx_obsolete(context_uid, repo_id, commit_id)
444
445
445 @reraise_safe_exceptions
446 @reraise_safe_exceptions
446 def ctx_hidden(self, wire, commit_id):
447 def ctx_hidden(self, wire, commit_id):
447 cache_on, context_uid, repo_id = self._cache_on(wire)
448 cache_on, context_uid, repo_id = self._cache_on(wire)
448 region = self._region(wire)
449 region = self._region(wire)
449
450
450 @region.conditional_cache_on_arguments(condition=cache_on)
451 @region.conditional_cache_on_arguments(condition=cache_on)
451 def _ctx_hidden(_context_uid, _repo_id, _commit_id):
452 def _ctx_hidden(_context_uid, _repo_id, _commit_id):
452 repo = self._factory.repo(wire)
453 repo = self._factory.repo(wire)
453 ctx = self._get_ctx(repo, commit_id)
454 ctx = self._get_ctx(repo, commit_id)
454 return ctx.hidden()
455 return ctx.hidden()
455 return _ctx_hidden(context_uid, repo_id, commit_id)
456 return _ctx_hidden(context_uid, repo_id, commit_id)
456
457
457 @reraise_safe_exceptions
458 @reraise_safe_exceptions
458 def ctx_substate(self, wire, revision):
459 def ctx_substate(self, wire, revision):
459 repo = self._factory.repo(wire)
460 repo = self._factory.repo(wire)
460 ctx = self._get_ctx(repo, revision)
461 ctx = self._get_ctx(repo, revision)
461 return ctx.substate
462 return ctx.substate
462
463
463 @reraise_safe_exceptions
464 @reraise_safe_exceptions
464 def ctx_status(self, wire, revision):
465 def ctx_status(self, wire, revision):
465 repo = self._factory.repo(wire)
466 repo = self._factory.repo(wire)
466 ctx = self._get_ctx(repo, revision)
467 ctx = self._get_ctx(repo, revision)
467 status = repo[ctx.p1().node()].status(other=ctx.node())
468 status = repo[ctx.p1().node()].status(other=ctx.node())
468 # object of status (odd, custom named tuple in mercurial) is not
469 # object of status (odd, custom named tuple in mercurial) is not
469 # correctly serializable, we make it a list, as the underling
470 # correctly serializable, we make it a list, as the underling
470 # API expects this to be a list
471 # API expects this to be a list
471 return list(status)
472 return list(status)
472
473
473 @reraise_safe_exceptions
474 @reraise_safe_exceptions
474 def ctx_user(self, wire, revision):
475 def ctx_user(self, wire, revision):
475 repo = self._factory.repo(wire)
476 repo = self._factory.repo(wire)
476 ctx = self._get_ctx(repo, revision)
477 ctx = self._get_ctx(repo, revision)
477 return ctx.user()
478 return ctx.user()
478
479
479 @reraise_safe_exceptions
480 @reraise_safe_exceptions
480 def check_url(self, url, config):
481 def check_url(self, url, config):
481 url, _proto = normalize_url_for_hg(url)
482 url, _proto = normalize_url_for_hg(url)
482 url_obj = url_parser(safe_bytes(url))
483 url_obj = url_parser(safe_bytes(url))
483
484
484 test_uri = safe_str(url_obj.authinfo()[0])
485 test_uri = safe_str(url_obj.authinfo()[0])
485 authinfo = url_obj.authinfo()[1]
486 authinfo = url_obj.authinfo()[1]
486 obfuscated_uri = get_obfuscated_url(url_obj)
487 obfuscated_uri = get_obfuscated_url(url_obj)
487 log.info("Checking URL for remote cloning/import: %s", obfuscated_uri)
488 log.info("Checking URL for remote cloning/import: %s", obfuscated_uri)
488
489
489 handlers = []
490 handlers = []
490 if authinfo:
491 if authinfo:
491 # create a password manager
492 # create a password manager
492 passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
493 passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
493 passmgr.add_password(*convert_to_str(authinfo))
494 passmgr.add_password(*convert_to_str(authinfo))
494
495
495 handlers.extend((httpbasicauthhandler(passmgr),
496 handlers.extend((httpbasicauthhandler(passmgr),
496 httpdigestauthhandler(passmgr)))
497 httpdigestauthhandler(passmgr)))
497
498
498 o = urllib.request.build_opener(*handlers)
499 o = urllib.request.build_opener(*handlers)
499 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
500 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
500 ('Accept', 'application/mercurial-0.1')]
501 ('Accept', 'application/mercurial-0.1')]
501
502
502 q = {"cmd": 'between'}
503 q = {"cmd": 'between'}
503 q.update({'pairs': "{}-{}".format('0' * 40, '0' * 40)})
504 q.update({'pairs': "{}-{}".format('0' * 40, '0' * 40)})
504 qs = f'?{urllib.parse.urlencode(q)}'
505 qs = f'?{urllib.parse.urlencode(q)}'
505 cu = f"{test_uri}{qs}"
506 cu = f"{test_uri}{qs}"
506
507
507 try:
508 try:
508 req = urllib.request.Request(cu, None, {})
509 req = urllib.request.Request(cu, None, {})
509 log.debug("Trying to open URL %s", obfuscated_uri)
510 log.debug("Trying to open URL %s", obfuscated_uri)
510 resp = o.open(req)
511 resp = o.open(req)
511 if resp.code != 200:
512 if resp.code != 200:
512 raise exceptions.URLError()('Return Code is not 200')
513 raise exceptions.URLError()('Return Code is not 200')
513 except Exception as e:
514 except Exception as e:
514 log.warning("URL cannot be opened: %s", obfuscated_uri, exc_info=True)
515 log.warning("URL cannot be opened: %s", obfuscated_uri, exc_info=True)
515 # means it cannot be cloned
516 # means it cannot be cloned
516 raise exceptions.URLError(e)(f"[{obfuscated_uri}] org_exc: {e}")
517 raise exceptions.URLError(e)(f"[{obfuscated_uri}] org_exc: {e}")
517
518
518 # now check if it's a proper hg repo, but don't do it for svn
519 # now check if it's a proper hg repo, but don't do it for svn
519 try:
520 try:
520 if _proto == 'svn':
521 if _proto == 'svn':
521 pass
522 pass
522 else:
523 else:
523 # check for pure hg repos
524 # check for pure hg repos
524 log.debug(
525 log.debug(
525 "Verifying if URL is a Mercurial repository: %s", obfuscated_uri)
526 "Verifying if URL is a Mercurial repository: %s", obfuscated_uri)
526 # Create repo path with custom mercurial path object
527 # Create repo path with custom mercurial path object
527 ui = make_ui_from_config(config, interactive=False)
528 ui = make_ui_from_config(config, interactive=False)
528 repo_path = hg_path(ui=ui, rawloc=safe_bytes(url))
529 repo_path = hg_path(ui=ui, rawloc=safe_bytes(url))
529 peer_checker = make_peer(ui, repo_path, False)
530 peer_checker = make_peer(ui, repo_path, False)
530 peer_checker.lookup(b'tip')
531 peer_checker.lookup(b'tip')
531 except Exception as e:
532 except Exception as e:
532 log.warning("URL is not a valid Mercurial repository: %s",
533 log.warning("URL is not a valid Mercurial repository: %s",
533 obfuscated_uri)
534 obfuscated_uri)
534 raise exceptions.URLError(e)(
535 raise exceptions.URLError(e)(
535 f"url [{obfuscated_uri}] does not look like an hg repo org_exc: {e}")
536 f"url [{obfuscated_uri}] does not look like an hg repo org_exc: {e}")
536
537
537 log.info("URL is a valid Mercurial repository: %s", obfuscated_uri)
538 log.info("URL is a valid Mercurial repository: %s", obfuscated_uri)
538 return True
539 return True
539
540
540 @reraise_safe_exceptions
541 @reraise_safe_exceptions
541 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context):
542 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context):
542 repo = self._factory.repo(wire)
543 repo = self._factory.repo(wire)
543
544
544 if file_filter:
545 if file_filter:
545 # unpack the file-filter
546 # unpack the file-filter
546 repo_path, node_path = file_filter
547 repo_path, node_path = file_filter
547 match_filter = match(safe_bytes(repo_path), b'', [safe_bytes(node_path)])
548 match_filter = match(safe_bytes(repo_path), b'', [safe_bytes(node_path)])
548 else:
549 else:
549 match_filter = file_filter
550 match_filter = file_filter
550 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context, showfunc=1)
551 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context, showfunc=1)
551
552
552 try:
553 try:
553 diff_iter = patch.diff(
554 diff_iter = patch.diff(
554 repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts)
555 repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts)
555 return BytesEnvelope(b"".join(diff_iter))
556 return BytesEnvelope(b"".join(diff_iter))
556 except RepoLookupError as e:
557 except RepoLookupError as e:
557 raise exceptions.LookupException(e)()
558 raise exceptions.LookupException(e)()
558
559
559 @reraise_safe_exceptions
560 @reraise_safe_exceptions
560 def node_history(self, wire, revision, path, limit):
561 def node_history(self, wire, revision, path, limit):
561 cache_on, context_uid, repo_id = self._cache_on(wire)
562 cache_on, context_uid, repo_id = self._cache_on(wire)
562 region = self._region(wire)
563 region = self._region(wire)
563
564
564 @region.conditional_cache_on_arguments(condition=cache_on)
565 @region.conditional_cache_on_arguments(condition=cache_on)
565 def _node_history(_context_uid, _repo_id, _revision, _path, _limit):
566 def _node_history(_context_uid, _repo_id, _revision, _path, _limit):
566 repo = self._factory.repo(wire)
567 repo = self._factory.repo(wire)
567
568
568 ctx = self._get_ctx(repo, revision)
569 ctx = self._get_ctx(repo, revision)
569 fctx = ctx.filectx(safe_bytes(path))
570 fctx = ctx.filectx(safe_bytes(path))
570
571
571 def history_iter():
572 def history_iter():
572 limit_rev = fctx.rev()
573 limit_rev = fctx.rev()
573
574
574 for fctx_candidate in reversed(list(fctx.filelog())):
575 for fctx_candidate in reversed(list(fctx.filelog())):
575 f_obj = fctx.filectx(fctx_candidate)
576 f_obj = fctx.filectx(fctx_candidate)
576
577
577 # NOTE: This can be problematic...we can hide ONLY history node resulting in empty history
578 # NOTE: This can be problematic...we can hide ONLY history node resulting in empty history
578 _ctx = f_obj.changectx()
579 _ctx = f_obj.changectx()
579 if _ctx.hidden() or _ctx.obsolete():
580 if _ctx.hidden() or _ctx.obsolete():
580 continue
581 continue
581
582
582 if limit_rev >= f_obj.rev():
583 if limit_rev >= f_obj.rev():
583 yield f_obj
584 yield f_obj
584
585
585 history = []
586 history = []
586 for cnt, obj in enumerate(history_iter()):
587 for cnt, obj in enumerate(history_iter()):
587 if limit and cnt >= limit:
588 if limit and cnt >= limit:
588 break
589 break
589 history.append(hex(obj.node()))
590 history.append(hex(obj.node()))
590
591
591 return [x for x in history]
592 return [x for x in history]
592 return _node_history(context_uid, repo_id, revision, path, limit)
593 return _node_history(context_uid, repo_id, revision, path, limit)
593
594
594 @reraise_safe_exceptions
595 @reraise_safe_exceptions
595 def node_history_until(self, wire, revision, path, limit):
596 def node_history_until(self, wire, revision, path, limit):
596 cache_on, context_uid, repo_id = self._cache_on(wire)
597 cache_on, context_uid, repo_id = self._cache_on(wire)
597 region = self._region(wire)
598 region = self._region(wire)
598
599
599 @region.conditional_cache_on_arguments(condition=cache_on)
600 @region.conditional_cache_on_arguments(condition=cache_on)
600 def _node_history_until(_context_uid, _repo_id):
601 def _node_history_until(_context_uid, _repo_id):
601 repo = self._factory.repo(wire)
602 repo = self._factory.repo(wire)
602 ctx = self._get_ctx(repo, revision)
603 ctx = self._get_ctx(repo, revision)
603 fctx = ctx.filectx(safe_bytes(path))
604 fctx = ctx.filectx(safe_bytes(path))
604
605
605 file_log = list(fctx.filelog())
606 file_log = list(fctx.filelog())
606 if limit:
607 if limit:
607 # Limit to the last n items
608 # Limit to the last n items
608 file_log = file_log[-limit:]
609 file_log = file_log[-limit:]
609
610
610 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
611 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
611 return _node_history_until(context_uid, repo_id, revision, path, limit)
612 return _node_history_until(context_uid, repo_id, revision, path, limit)
612
613
613 @reraise_safe_exceptions
614 @reraise_safe_exceptions
614 def bulk_file_request(self, wire, commit_id, path, pre_load):
615 def bulk_file_request(self, wire, commit_id, path, pre_load):
615 cache_on, context_uid, repo_id = self._cache_on(wire)
616 cache_on, context_uid, repo_id = self._cache_on(wire)
616 region = self._region(wire)
617 region = self._region(wire)
617
618
618 @region.conditional_cache_on_arguments(condition=cache_on)
619 @region.conditional_cache_on_arguments(condition=cache_on)
619 def _bulk_file_request(_repo_id, _commit_id, _path, _pre_load):
620 def _bulk_file_request(_repo_id, _commit_id, _path, _pre_load):
620 result = {}
621 result = {}
621 for attr in pre_load:
622 for attr in pre_load:
622 try:
623 try:
623 method = self._bulk_file_methods[attr]
624 method = self._bulk_file_methods[attr]
624 wire.update({'cache': False}) # disable cache for bulk calls so we don't double cache
625 wire.update({'cache': False}) # disable cache for bulk calls so we don't double cache
625 result[attr] = method(wire, _commit_id, _path)
626 result[attr] = method(wire, _commit_id, _path)
626 except KeyError as e:
627 except KeyError as e:
627 raise exceptions.VcsException(e)(f'Unknown bulk attribute: "{attr}"')
628 raise exceptions.VcsException(e)(f'Unknown bulk attribute: "{attr}"')
628 return result
629 return result
629
630
630 return BinaryEnvelope(_bulk_file_request(repo_id, commit_id, path, sorted(pre_load)))
631 return BinaryEnvelope(_bulk_file_request(repo_id, commit_id, path, sorted(pre_load)))
631
632
632 @reraise_safe_exceptions
633 @reraise_safe_exceptions
633 def fctx_annotate(self, wire, revision, path):
634 def fctx_annotate(self, wire, revision, path):
634 repo = self._factory.repo(wire)
635 repo = self._factory.repo(wire)
635 ctx = self._get_ctx(repo, revision)
636 ctx = self._get_ctx(repo, revision)
636 fctx = ctx.filectx(safe_bytes(path))
637 fctx = ctx.filectx(safe_bytes(path))
637
638
638 result = []
639 result = []
639 for i, annotate_obj in enumerate(fctx.annotate(), 1):
640 for i, annotate_obj in enumerate(fctx.annotate(), 1):
640 ln_no = i
641 ln_no = i
641 sha = hex(annotate_obj.fctx.node())
642 sha = hex(annotate_obj.fctx.node())
642 content = annotate_obj.text
643 content = annotate_obj.text
643 result.append((ln_no, ascii_str(sha), content))
644 result.append((ln_no, ascii_str(sha), content))
644 return BinaryEnvelope(result)
645 return BinaryEnvelope(result)
645
646
646 @reraise_safe_exceptions
647 @reraise_safe_exceptions
647 def fctx_node_data(self, wire, revision, path):
648 def fctx_node_data(self, wire, revision, path):
648 repo = self._factory.repo(wire)
649 repo = self._factory.repo(wire)
649 ctx = self._get_ctx(repo, revision)
650 ctx = self._get_ctx(repo, revision)
650 fctx = ctx.filectx(safe_bytes(path))
651 fctx = ctx.filectx(safe_bytes(path))
651 return BytesEnvelope(fctx.data())
652 return BytesEnvelope(fctx.data())
652
653
653 @reraise_safe_exceptions
654 @reraise_safe_exceptions
654 def fctx_flags(self, wire, commit_id, path):
655 def fctx_flags(self, wire, commit_id, path):
655 cache_on, context_uid, repo_id = self._cache_on(wire)
656 cache_on, context_uid, repo_id = self._cache_on(wire)
656 region = self._region(wire)
657 region = self._region(wire)
657
658
658 @region.conditional_cache_on_arguments(condition=cache_on)
659 @region.conditional_cache_on_arguments(condition=cache_on)
659 def _fctx_flags(_repo_id, _commit_id, _path):
660 def _fctx_flags(_repo_id, _commit_id, _path):
660 repo = self._factory.repo(wire)
661 repo = self._factory.repo(wire)
661 ctx = self._get_ctx(repo, commit_id)
662 ctx = self._get_ctx(repo, commit_id)
662 fctx = ctx.filectx(safe_bytes(path))
663 fctx = ctx.filectx(safe_bytes(path))
663 return fctx.flags()
664 return fctx.flags()
664
665
665 return _fctx_flags(repo_id, commit_id, path)
666 return _fctx_flags(repo_id, commit_id, path)
666
667
667 @reraise_safe_exceptions
668 @reraise_safe_exceptions
668 def fctx_size(self, wire, commit_id, path):
669 def fctx_size(self, wire, commit_id, path):
669 cache_on, context_uid, repo_id = self._cache_on(wire)
670 cache_on, context_uid, repo_id = self._cache_on(wire)
670 region = self._region(wire)
671 region = self._region(wire)
671
672
672 @region.conditional_cache_on_arguments(condition=cache_on)
673 @region.conditional_cache_on_arguments(condition=cache_on)
673 def _fctx_size(_repo_id, _revision, _path):
674 def _fctx_size(_repo_id, _revision, _path):
674 repo = self._factory.repo(wire)
675 repo = self._factory.repo(wire)
675 ctx = self._get_ctx(repo, commit_id)
676 ctx = self._get_ctx(repo, commit_id)
676 fctx = ctx.filectx(safe_bytes(path))
677 fctx = ctx.filectx(safe_bytes(path))
677 return fctx.size()
678 return fctx.size()
678 return _fctx_size(repo_id, commit_id, path)
679 return _fctx_size(repo_id, commit_id, path)
679
680
680 @reraise_safe_exceptions
681 @reraise_safe_exceptions
681 def get_all_commit_ids(self, wire, name):
682 def get_all_commit_ids(self, wire, name):
682 cache_on, context_uid, repo_id = self._cache_on(wire)
683 cache_on, context_uid, repo_id = self._cache_on(wire)
683 region = self._region(wire)
684 region = self._region(wire)
684
685
685 @region.conditional_cache_on_arguments(condition=cache_on)
686 @region.conditional_cache_on_arguments(condition=cache_on)
686 def _get_all_commit_ids(_context_uid, _repo_id, _name):
687 def _get_all_commit_ids(_context_uid, _repo_id, _name):
687 repo = self._factory.repo(wire)
688 repo = self._factory.repo(wire)
688 revs = [ascii_str(repo[x].hex()) for x in repo.filtered(b'visible').changelog.revs()]
689 revs = [ascii_str(repo[x].hex()) for x in repo.filtered(b'visible').changelog.revs()]
689 return revs
690 return revs
690 return _get_all_commit_ids(context_uid, repo_id, name)
691 return _get_all_commit_ids(context_uid, repo_id, name)
691
692
692 @reraise_safe_exceptions
693 @reraise_safe_exceptions
693 def get_config_value(self, wire, section, name, untrusted=False):
694 def get_config_value(self, wire, section, name, untrusted=False):
694 repo = self._factory.repo(wire)
695 repo = self._factory.repo(wire)
695 return repo.ui.config(ascii_bytes(section), ascii_bytes(name), untrusted=untrusted)
696 return repo.ui.config(ascii_bytes(section), ascii_bytes(name), untrusted=untrusted)
696
697
697 @reraise_safe_exceptions
698 @reraise_safe_exceptions
698 def is_large_file(self, wire, commit_id, path):
699 def is_large_file(self, wire, commit_id, path):
699 cache_on, context_uid, repo_id = self._cache_on(wire)
700 cache_on, context_uid, repo_id = self._cache_on(wire)
700 region = self._region(wire)
701 region = self._region(wire)
701
702
702 @region.conditional_cache_on_arguments(condition=cache_on)
703 @region.conditional_cache_on_arguments(condition=cache_on)
703 def _is_large_file(_context_uid, _repo_id, _commit_id, _path):
704 def _is_large_file(_context_uid, _repo_id, _commit_id, _path):
704 return largefiles.lfutil.isstandin(safe_bytes(path))
705 return largefiles.lfutil.isstandin(safe_bytes(path))
705
706
706 return _is_large_file(context_uid, repo_id, commit_id, path)
707 return _is_large_file(context_uid, repo_id, commit_id, path)
707
708
708 @reraise_safe_exceptions
709 @reraise_safe_exceptions
709 def is_binary(self, wire, revision, path):
710 def is_binary(self, wire, revision, path):
710 cache_on, context_uid, repo_id = self._cache_on(wire)
711 cache_on, context_uid, repo_id = self._cache_on(wire)
711 region = self._region(wire)
712 region = self._region(wire)
712
713
713 @region.conditional_cache_on_arguments(condition=cache_on)
714 @region.conditional_cache_on_arguments(condition=cache_on)
714 def _is_binary(_repo_id, _sha, _path):
715 def _is_binary(_repo_id, _sha, _path):
715 repo = self._factory.repo(wire)
716 repo = self._factory.repo(wire)
716 ctx = self._get_ctx(repo, revision)
717 ctx = self._get_ctx(repo, revision)
717 fctx = ctx.filectx(safe_bytes(path))
718 fctx = ctx.filectx(safe_bytes(path))
718 return fctx.isbinary()
719 return fctx.isbinary()
719
720
720 return _is_binary(repo_id, revision, path)
721 return _is_binary(repo_id, revision, path)
721
722
722 @reraise_safe_exceptions
723 @reraise_safe_exceptions
723 def md5_hash(self, wire, revision, path):
724 def md5_hash(self, wire, revision, path):
724 cache_on, context_uid, repo_id = self._cache_on(wire)
725 cache_on, context_uid, repo_id = self._cache_on(wire)
725 region = self._region(wire)
726 region = self._region(wire)
726
727
727 @region.conditional_cache_on_arguments(condition=cache_on)
728 @region.conditional_cache_on_arguments(condition=cache_on)
728 def _md5_hash(_repo_id, _sha, _path):
729 def _md5_hash(_repo_id, _sha, _path):
729 repo = self._factory.repo(wire)
730 repo = self._factory.repo(wire)
730 ctx = self._get_ctx(repo, revision)
731 ctx = self._get_ctx(repo, revision)
731 fctx = ctx.filectx(safe_bytes(path))
732 fctx = ctx.filectx(safe_bytes(path))
732 return hashlib.md5(fctx.data()).hexdigest()
733 return hashlib.md5(fctx.data()).hexdigest()
733
734
734 return _md5_hash(repo_id, revision, path)
735 return _md5_hash(repo_id, revision, path)
735
736
736 @reraise_safe_exceptions
737 @reraise_safe_exceptions
737 def in_largefiles_store(self, wire, sha):
738 def in_largefiles_store(self, wire, sha):
738 repo = self._factory.repo(wire)
739 repo = self._factory.repo(wire)
739 return largefiles.lfutil.instore(repo, sha)
740 return largefiles.lfutil.instore(repo, sha)
740
741
741 @reraise_safe_exceptions
742 @reraise_safe_exceptions
742 def in_user_cache(self, wire, sha):
743 def in_user_cache(self, wire, sha):
743 repo = self._factory.repo(wire)
744 repo = self._factory.repo(wire)
744 return largefiles.lfutil.inusercache(repo.ui, sha)
745 return largefiles.lfutil.inusercache(repo.ui, sha)
745
746
746 @reraise_safe_exceptions
747 @reraise_safe_exceptions
747 def store_path(self, wire, sha):
748 def store_path(self, wire, sha):
748 repo = self._factory.repo(wire)
749 repo = self._factory.repo(wire)
749 return largefiles.lfutil.storepath(repo, sha)
750 return largefiles.lfutil.storepath(repo, sha)
750
751
751 @reraise_safe_exceptions
752 @reraise_safe_exceptions
752 def link(self, wire, sha, path):
753 def link(self, wire, sha, path):
753 repo = self._factory.repo(wire)
754 repo = self._factory.repo(wire)
754 largefiles.lfutil.link(
755 largefiles.lfutil.link(
755 largefiles.lfutil.usercachepath(repo.ui, sha), path)
756 largefiles.lfutil.usercachepath(repo.ui, sha), path)
756
757
757 @reraise_safe_exceptions
758 @reraise_safe_exceptions
758 def localrepository(self, wire, create=False):
759 def localrepository(self, wire, create=False):
759 self._factory.repo(wire, create=create)
760 self._factory.repo(wire, create=create)
760
761
761 @reraise_safe_exceptions
762 @reraise_safe_exceptions
763 def assert_correct_path(self, wire):
764 cache_on, context_uid, repo_id = self._cache_on(wire)
765 region = self._region(wire)
766
767 @region.conditional_cache_on_arguments(condition=cache_on)
768 def _assert_correct_path(_context_uid, _repo_id):
769 try:
770 self._factory.repo(wire, create=False)
771 except Exception:
772 path = wire.get('path')
773 tb = traceback.format_exc()
774 log.debug("Invalid Mercurial path `%s`, tb: %s", path, tb)
775 return False
776 return True
777
778 return _assert_correct_path(context_uid, repo_id)
779
780 @reraise_safe_exceptions
762 def lookup(self, wire, revision, both):
781 def lookup(self, wire, revision, both):
763 cache_on, context_uid, repo_id = self._cache_on(wire)
782 cache_on, context_uid, repo_id = self._cache_on(wire)
764 region = self._region(wire)
783 region = self._region(wire)
765
784
766 @region.conditional_cache_on_arguments(condition=cache_on)
785 @region.conditional_cache_on_arguments(condition=cache_on)
767 def _lookup(_context_uid, _repo_id, _revision, _both):
786 def _lookup(_context_uid, _repo_id, _revision, _both):
768 repo = self._factory.repo(wire)
787 repo = self._factory.repo(wire)
769 rev = _revision
788 rev = _revision
770 if isinstance(rev, int):
789 if isinstance(rev, int):
771 # NOTE(marcink):
790 # NOTE(marcink):
772 # since Mercurial doesn't support negative indexes properly
791 # since Mercurial doesn't support negative indexes properly
773 # we need to shift accordingly by one to get proper index, e.g
792 # we need to shift accordingly by one to get proper index, e.g
774 # repo[-1] => repo[-2]
793 # repo[-1] => repo[-2]
775 # repo[0] => repo[-1]
794 # repo[0] => repo[-1]
776 if rev <= 0:
795 if rev <= 0:
777 rev = rev + -1
796 rev = rev + -1
778 try:
797 try:
779 ctx = self._get_ctx(repo, rev)
798 ctx = self._get_ctx(repo, rev)
780 except AmbiguousPrefixLookupError:
799 except AmbiguousPrefixLookupError:
781 e = RepoLookupError(rev)
800 e = RepoLookupError(rev)
782 e._org_exc_tb = format_exc(sys.exc_info())
801 e._org_exc_tb = format_exc(sys.exc_info())
783 raise exceptions.LookupException(e)(rev)
802 raise exceptions.LookupException(e)(rev)
784 except (TypeError, RepoLookupError, binascii.Error) as e:
803 except (TypeError, RepoLookupError, binascii.Error) as e:
785 e._org_exc_tb = format_exc(sys.exc_info())
804 e._org_exc_tb = format_exc(sys.exc_info())
786 raise exceptions.LookupException(e)(rev)
805 raise exceptions.LookupException(e)(rev)
787 except LookupError as e:
806 except LookupError as e:
788 e._org_exc_tb = format_exc(sys.exc_info())
807 e._org_exc_tb = format_exc(sys.exc_info())
789 raise exceptions.LookupException(e)(e.name)
808 raise exceptions.LookupException(e)(e.name)
790
809
791 if not both:
810 if not both:
792 return ctx.hex()
811 return ctx.hex()
793
812
794 ctx = repo[ctx.hex()]
813 ctx = repo[ctx.hex()]
795 return ctx.hex(), ctx.rev()
814 return ctx.hex(), ctx.rev()
796
815
797 return _lookup(context_uid, repo_id, revision, both)
816 return _lookup(context_uid, repo_id, revision, both)
798
817
799 @reraise_safe_exceptions
818 @reraise_safe_exceptions
800 def sync_push(self, wire, url):
819 def sync_push(self, wire, url):
801 if not self.check_url(url, wire['config']):
820 if not self.check_url(url, wire['config']):
802 return
821 return
803
822
804 repo = self._factory.repo(wire)
823 repo = self._factory.repo(wire)
805
824
806 # Disable any prompts for this repo
825 # Disable any prompts for this repo
807 repo.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
826 repo.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
808
827
809 bookmarks = list(dict(repo._bookmarks).keys())
828 bookmarks = list(dict(repo._bookmarks).keys())
810 remote = peer(repo, {}, safe_bytes(url))
829 remote = peer(repo, {}, safe_bytes(url))
811 # Disable any prompts for this remote
830 # Disable any prompts for this remote
812 remote.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
831 remote.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
813
832
814 return exchange.push(
833 return exchange.push(
815 repo, remote, newbranch=True, bookmarks=bookmarks).cgresult
834 repo, remote, newbranch=True, bookmarks=bookmarks).cgresult
816
835
817 @reraise_safe_exceptions
836 @reraise_safe_exceptions
818 def revision(self, wire, rev):
837 def revision(self, wire, rev):
819 repo = self._factory.repo(wire)
838 repo = self._factory.repo(wire)
820 ctx = self._get_ctx(repo, rev)
839 ctx = self._get_ctx(repo, rev)
821 return ctx.rev()
840 return ctx.rev()
822
841
823 @reraise_safe_exceptions
842 @reraise_safe_exceptions
824 def rev_range(self, wire, commit_filter):
843 def rev_range(self, wire, commit_filter):
825 cache_on, context_uid, repo_id = self._cache_on(wire)
844 cache_on, context_uid, repo_id = self._cache_on(wire)
826 region = self._region(wire)
845 region = self._region(wire)
827
846
828 @region.conditional_cache_on_arguments(condition=cache_on)
847 @region.conditional_cache_on_arguments(condition=cache_on)
829 def _rev_range(_context_uid, _repo_id, _filter):
848 def _rev_range(_context_uid, _repo_id, _filter):
830 repo = self._factory.repo(wire)
849 repo = self._factory.repo(wire)
831 revisions = [
850 revisions = [
832 ascii_str(repo[rev].hex())
851 ascii_str(repo[rev].hex())
833 for rev in revrange(repo, list(map(ascii_bytes, commit_filter)))
852 for rev in revrange(repo, list(map(ascii_bytes, commit_filter)))
834 ]
853 ]
835 return revisions
854 return revisions
836
855
837 return _rev_range(context_uid, repo_id, sorted(commit_filter))
856 return _rev_range(context_uid, repo_id, sorted(commit_filter))
838
857
839 @reraise_safe_exceptions
858 @reraise_safe_exceptions
840 def rev_range_hash(self, wire, node):
859 def rev_range_hash(self, wire, node):
841 repo = self._factory.repo(wire)
860 repo = self._factory.repo(wire)
842
861
843 def get_revs(repo, rev_opt):
862 def get_revs(repo, rev_opt):
844 if rev_opt:
863 if rev_opt:
845 revs = revrange(repo, rev_opt)
864 revs = revrange(repo, rev_opt)
846 if len(revs) == 0:
865 if len(revs) == 0:
847 return (nullrev, nullrev)
866 return (nullrev, nullrev)
848 return max(revs), min(revs)
867 return max(revs), min(revs)
849 else:
868 else:
850 return len(repo) - 1, 0
869 return len(repo) - 1, 0
851
870
852 stop, start = get_revs(repo, [node + ':'])
871 stop, start = get_revs(repo, [node + ':'])
853 revs = [ascii_str(repo[r].hex()) for r in range(start, stop + 1)]
872 revs = [ascii_str(repo[r].hex()) for r in range(start, stop + 1)]
854 return revs
873 return revs
855
874
856 @reraise_safe_exceptions
875 @reraise_safe_exceptions
857 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
876 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
858 org_path = safe_bytes(wire["path"])
877 org_path = safe_bytes(wire["path"])
859 other_path = safe_bytes(kwargs.pop('other_path', ''))
878 other_path = safe_bytes(kwargs.pop('other_path', ''))
860
879
861 # case when we want to compare two independent repositories
880 # case when we want to compare two independent repositories
862 if other_path and other_path != wire["path"]:
881 if other_path and other_path != wire["path"]:
863 baseui = self._factory._create_config(wire["config"])
882 baseui = self._factory._create_config(wire["config"])
864 repo = unionrepo.makeunionrepository(baseui, other_path, org_path)
883 repo = unionrepo.makeunionrepository(baseui, other_path, org_path)
865 else:
884 else:
866 repo = self._factory.repo(wire)
885 repo = self._factory.repo(wire)
867 return list(repo.revs(rev_spec, *args))
886 return list(repo.revs(rev_spec, *args))
868
887
869 @reraise_safe_exceptions
888 @reraise_safe_exceptions
870 def verify(self, wire,):
889 def verify(self, wire,):
871 repo = self._factory.repo(wire)
890 repo = self._factory.repo(wire)
872 baseui = self._factory._create_config(wire['config'])
891 baseui = self._factory._create_config(wire['config'])
873
892
874 baseui, output = patch_ui_message_output(baseui)
893 baseui, output = patch_ui_message_output(baseui)
875
894
876 repo.ui = baseui
895 repo.ui = baseui
877 verify.verify(repo)
896 verify.verify(repo)
878 return output.getvalue()
897 return output.getvalue()
879
898
880 @reraise_safe_exceptions
899 @reraise_safe_exceptions
881 def hg_update_cache(self, wire,):
900 def hg_update_cache(self, wire,):
882 repo = self._factory.repo(wire)
901 repo = self._factory.repo(wire)
883 baseui = self._factory._create_config(wire['config'])
902 baseui = self._factory._create_config(wire['config'])
884 baseui, output = patch_ui_message_output(baseui)
903 baseui, output = patch_ui_message_output(baseui)
885
904
886 repo.ui = baseui
905 repo.ui = baseui
887 with repo.wlock(), repo.lock():
906 with repo.wlock(), repo.lock():
888 repo.updatecaches(full=True)
907 repo.updatecaches(full=True)
889
908
890 return output.getvalue()
909 return output.getvalue()
891
910
892 @reraise_safe_exceptions
911 @reraise_safe_exceptions
893 def hg_rebuild_fn_cache(self, wire,):
912 def hg_rebuild_fn_cache(self, wire,):
894 repo = self._factory.repo(wire)
913 repo = self._factory.repo(wire)
895 baseui = self._factory._create_config(wire['config'])
914 baseui = self._factory._create_config(wire['config'])
896 baseui, output = patch_ui_message_output(baseui)
915 baseui, output = patch_ui_message_output(baseui)
897
916
898 repo.ui = baseui
917 repo.ui = baseui
899
918
900 repair.rebuildfncache(baseui, repo)
919 repair.rebuildfncache(baseui, repo)
901
920
902 return output.getvalue()
921 return output.getvalue()
903
922
904 @reraise_safe_exceptions
923 @reraise_safe_exceptions
905 def tags(self, wire):
924 def tags(self, wire):
906 cache_on, context_uid, repo_id = self._cache_on(wire)
925 cache_on, context_uid, repo_id = self._cache_on(wire)
907 region = self._region(wire)
926 region = self._region(wire)
908
927
909 @region.conditional_cache_on_arguments(condition=cache_on)
928 @region.conditional_cache_on_arguments(condition=cache_on)
910 def _tags(_context_uid, _repo_id):
929 def _tags(_context_uid, _repo_id):
911 repo = self._factory.repo(wire)
930 repo = self._factory.repo(wire)
912 return {safe_str(name): ascii_str(hex(sha)) for name, sha in repo.tags().items()}
931 return {safe_str(name): ascii_str(hex(sha)) for name, sha in repo.tags().items()}
913
932
914 return _tags(context_uid, repo_id)
933 return _tags(context_uid, repo_id)
915
934
916 @reraise_safe_exceptions
935 @reraise_safe_exceptions
917 def update(self, wire, node='', clean=False):
936 def update(self, wire, node='', clean=False):
918 repo = self._factory.repo(wire)
937 repo = self._factory.repo(wire)
919 baseui = self._factory._create_config(wire['config'])
938 baseui = self._factory._create_config(wire['config'])
920 node = safe_bytes(node)
939 node = safe_bytes(node)
921
940
922 commands.update(baseui, repo, node=node, clean=clean)
941 commands.update(baseui, repo, node=node, clean=clean)
923
942
924 @reraise_safe_exceptions
943 @reraise_safe_exceptions
925 def identify(self, wire):
944 def identify(self, wire):
926 repo = self._factory.repo(wire)
945 repo = self._factory.repo(wire)
927 baseui = self._factory._create_config(wire['config'])
946 baseui = self._factory._create_config(wire['config'])
928 output = io.BytesIO()
947 output = io.BytesIO()
929 baseui.write = output.write
948 baseui.write = output.write
930 # This is required to get a full node id
949 # This is required to get a full node id
931 baseui.debugflag = True
950 baseui.debugflag = True
932 commands.identify(baseui, repo, id=True)
951 commands.identify(baseui, repo, id=True)
933
952
934 return output.getvalue()
953 return output.getvalue()
935
954
936 @reraise_safe_exceptions
955 @reraise_safe_exceptions
937 def heads(self, wire, branch=None):
956 def heads(self, wire, branch=None):
938 repo = self._factory.repo(wire)
957 repo = self._factory.repo(wire)
939 baseui = self._factory._create_config(wire['config'])
958 baseui = self._factory._create_config(wire['config'])
940 output = io.BytesIO()
959 output = io.BytesIO()
941
960
942 def write(data, **unused_kwargs):
961 def write(data, **unused_kwargs):
943 output.write(data)
962 output.write(data)
944
963
945 baseui.write = write
964 baseui.write = write
946 if branch:
965 if branch:
947 args = [safe_bytes(branch)]
966 args = [safe_bytes(branch)]
948 else:
967 else:
949 args = []
968 args = []
950 commands.heads(baseui, repo, template=b'{node} ', *args)
969 commands.heads(baseui, repo, template=b'{node} ', *args)
951
970
952 return output.getvalue()
971 return output.getvalue()
953
972
954 @reraise_safe_exceptions
973 @reraise_safe_exceptions
955 def ancestor(self, wire, revision1, revision2):
974 def ancestor(self, wire, revision1, revision2):
956 repo = self._factory.repo(wire)
975 repo = self._factory.repo(wire)
957 changelog = repo.changelog
976 changelog = repo.changelog
958 lookup = repo.lookup
977 lookup = repo.lookup
959 a = changelog.ancestor(lookup(safe_bytes(revision1)), lookup(safe_bytes(revision2)))
978 a = changelog.ancestor(lookup(safe_bytes(revision1)), lookup(safe_bytes(revision2)))
960 return hex(a)
979 return hex(a)
961
980
962 @reraise_safe_exceptions
981 @reraise_safe_exceptions
963 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
982 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
964 baseui = self._factory._create_config(wire["config"], hooks=hooks)
983 baseui = self._factory._create_config(wire["config"], hooks=hooks)
965 clone(baseui, safe_bytes(source), safe_bytes(dest), noupdate=not update_after_clone)
984 clone(baseui, safe_bytes(source), safe_bytes(dest), noupdate=not update_after_clone)
966
985
967 @reraise_safe_exceptions
986 @reraise_safe_exceptions
968 def commitctx(self, wire, message, parents, commit_time, commit_timezone, user, files, extra, removed, updated):
987 def commitctx(self, wire, message, parents, commit_time, commit_timezone, user, files, extra, removed, updated):
969
988
970 repo = self._factory.repo(wire)
989 repo = self._factory.repo(wire)
971 baseui = self._factory._create_config(wire['config'])
990 baseui = self._factory._create_config(wire['config'])
972 publishing = baseui.configbool(b'phases', b'publish')
991 publishing = baseui.configbool(b'phases', b'publish')
973
992
974 def _filectxfn(_repo, ctx, path: bytes):
993 def _filectxfn(_repo, ctx, path: bytes):
975 """
994 """
976 Marks given path as added/changed/removed in a given _repo. This is
995 Marks given path as added/changed/removed in a given _repo. This is
977 for internal mercurial commit function.
996 for internal mercurial commit function.
978 """
997 """
979
998
980 # check if this path is removed
999 # check if this path is removed
981 if safe_str(path) in removed:
1000 if safe_str(path) in removed:
982 # returning None is a way to mark node for removal
1001 # returning None is a way to mark node for removal
983 return None
1002 return None
984
1003
985 # check if this path is added
1004 # check if this path is added
986 for node in updated:
1005 for node in updated:
987 if safe_bytes(node['path']) == path:
1006 if safe_bytes(node['path']) == path:
988 return memfilectx(
1007 return memfilectx(
989 _repo,
1008 _repo,
990 changectx=ctx,
1009 changectx=ctx,
991 path=safe_bytes(node['path']),
1010 path=safe_bytes(node['path']),
992 data=safe_bytes(node['content']),
1011 data=safe_bytes(node['content']),
993 islink=False,
1012 islink=False,
994 isexec=bool(node['mode'] & stat.S_IXUSR),
1013 isexec=bool(node['mode'] & stat.S_IXUSR),
995 copysource=False)
1014 copysource=False)
996 abort_exc = exceptions.AbortException()
1015 abort_exc = exceptions.AbortException()
997 raise abort_exc(f"Given path haven't been marked as added, changed or removed ({path})")
1016 raise abort_exc(f"Given path haven't been marked as added, changed or removed ({path})")
998
1017
999 if publishing:
1018 if publishing:
1000 new_commit_phase = b'public'
1019 new_commit_phase = b'public'
1001 else:
1020 else:
1002 new_commit_phase = b'draft'
1021 new_commit_phase = b'draft'
1003 with repo.ui.configoverride({(b'phases', b'new-commit'): new_commit_phase}):
1022 with repo.ui.configoverride({(b'phases', b'new-commit'): new_commit_phase}):
1004 kwargs = {safe_bytes(k): safe_bytes(v) for k, v in extra.items()}
1023 kwargs = {safe_bytes(k): safe_bytes(v) for k, v in extra.items()}
1005 commit_ctx = memctx(
1024 commit_ctx = memctx(
1006 repo=repo,
1025 repo=repo,
1007 parents=parents,
1026 parents=parents,
1008 text=safe_bytes(message),
1027 text=safe_bytes(message),
1009 files=[safe_bytes(x) for x in files],
1028 files=[safe_bytes(x) for x in files],
1010 filectxfn=_filectxfn,
1029 filectxfn=_filectxfn,
1011 user=safe_bytes(user),
1030 user=safe_bytes(user),
1012 date=(commit_time, commit_timezone),
1031 date=(commit_time, commit_timezone),
1013 extra=kwargs)
1032 extra=kwargs)
1014
1033
1015 n = repo.commitctx(commit_ctx)
1034 n = repo.commitctx(commit_ctx)
1016 new_id = hex(n)
1035 new_id = hex(n)
1017
1036
1018 return new_id
1037 return new_id
1019
1038
1020 @reraise_safe_exceptions
1039 @reraise_safe_exceptions
1021 def pull(self, wire, url, commit_ids=None):
1040 def pull(self, wire, url, commit_ids=None):
1022 repo = self._factory.repo(wire)
1041 repo = self._factory.repo(wire)
1023 # Disable any prompts for this repo
1042 # Disable any prompts for this repo
1024 repo.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
1043 repo.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
1025
1044
1026 remote = peer(repo, {}, safe_bytes(url))
1045 remote = peer(repo, {}, safe_bytes(url))
1027 # Disable any prompts for this remote
1046 # Disable any prompts for this remote
1028 remote.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
1047 remote.ui.setconfig(b'ui', b'interactive', b'false', b'-y')
1029
1048
1030 if commit_ids:
1049 if commit_ids:
1031 commit_ids = [bin(commit_id) for commit_id in commit_ids]
1050 commit_ids = [bin(commit_id) for commit_id in commit_ids]
1032
1051
1033 return exchange.pull(
1052 return exchange.pull(
1034 repo, remote, heads=commit_ids, force=None).cgresult
1053 repo, remote, heads=commit_ids, force=None).cgresult
1035
1054
1036 @reraise_safe_exceptions
1055 @reraise_safe_exceptions
1037 def pull_cmd(self, wire, source, bookmark='', branch='', revision='', hooks=True):
1056 def pull_cmd(self, wire, source, bookmark='', branch='', revision='', hooks=True):
1038 repo = self._factory.repo(wire)
1057 repo = self._factory.repo(wire)
1039 baseui = self._factory._create_config(wire['config'], hooks=hooks)
1058 baseui = self._factory._create_config(wire['config'], hooks=hooks)
1040
1059
1041 source = safe_bytes(source)
1060 source = safe_bytes(source)
1042
1061
1043 # Mercurial internally has a lot of logic that checks ONLY if
1062 # Mercurial internally has a lot of logic that checks ONLY if
1044 # option is defined, we just pass those if they are defined then
1063 # option is defined, we just pass those if they are defined then
1045 opts = {"remote_hidden": False}
1064 opts = {"remote_hidden": False}
1046
1065
1047 if bookmark:
1066 if bookmark:
1048 opts['bookmark'] = [safe_bytes(x) for x in bookmark] \
1067 opts['bookmark'] = [safe_bytes(x) for x in bookmark] \
1049 if isinstance(bookmark, list) else safe_bytes(bookmark)
1068 if isinstance(bookmark, list) else safe_bytes(bookmark)
1050
1069
1051 if branch:
1070 if branch:
1052 opts['branch'] = [safe_bytes(x) for x in branch] \
1071 opts['branch'] = [safe_bytes(x) for x in branch] \
1053 if isinstance(branch, list) else safe_bytes(branch)
1072 if isinstance(branch, list) else safe_bytes(branch)
1054
1073
1055 if revision:
1074 if revision:
1056 opts['rev'] = [safe_bytes(x) for x in revision] \
1075 opts['rev'] = [safe_bytes(x) for x in revision] \
1057 if isinstance(revision, list) else safe_bytes(revision)
1076 if isinstance(revision, list) else safe_bytes(revision)
1058
1077
1059 commands.pull(baseui, repo, source, **opts)
1078 commands.pull(baseui, repo, source, **opts)
1060
1079
1061 @reraise_safe_exceptions
1080 @reraise_safe_exceptions
1062 def push(self, wire, revisions, dest_path, hooks: bool = True, push_branches: bool = False):
1081 def push(self, wire, revisions, dest_path, hooks: bool = True, push_branches: bool = False):
1063 repo = self._factory.repo(wire)
1082 repo = self._factory.repo(wire)
1064 baseui = self._factory._create_config(wire['config'], hooks=hooks)
1083 baseui = self._factory._create_config(wire['config'], hooks=hooks)
1065
1084
1066 revisions = [safe_bytes(x) for x in revisions] \
1085 revisions = [safe_bytes(x) for x in revisions] \
1067 if isinstance(revisions, list) else safe_bytes(revisions)
1086 if isinstance(revisions, list) else safe_bytes(revisions)
1068
1087
1069 commands.push(baseui, repo, safe_bytes(dest_path),
1088 commands.push(baseui, repo, safe_bytes(dest_path),
1070 rev=revisions,
1089 rev=revisions,
1071 new_branch=push_branches)
1090 new_branch=push_branches)
1072
1091
1073 @reraise_safe_exceptions
1092 @reraise_safe_exceptions
1074 def strip(self, wire, revision, update, backup):
1093 def strip(self, wire, revision, update, backup):
1075 repo = self._factory.repo(wire)
1094 repo = self._factory.repo(wire)
1076 ctx = self._get_ctx(repo, revision)
1095 ctx = self._get_ctx(repo, revision)
1077 hgext_strip.strip(
1096 hgext_strip.strip(
1078 repo.baseui, repo, ctx.node(), update=update, backup=backup)
1097 repo.baseui, repo, ctx.node(), update=update, backup=backup)
1079
1098
1080 @reraise_safe_exceptions
1099 @reraise_safe_exceptions
1081 def get_unresolved_files(self, wire):
1100 def get_unresolved_files(self, wire):
1082 repo = self._factory.repo(wire)
1101 repo = self._factory.repo(wire)
1083
1102
1084 log.debug('Calculating unresolved files for repo: %s', repo)
1103 log.debug('Calculating unresolved files for repo: %s', repo)
1085 output = io.BytesIO()
1104 output = io.BytesIO()
1086
1105
1087 def write(data, **unused_kwargs):
1106 def write(data, **unused_kwargs):
1088 output.write(data)
1107 output.write(data)
1089
1108
1090 baseui = self._factory._create_config(wire['config'])
1109 baseui = self._factory._create_config(wire['config'])
1091 baseui.write = write
1110 baseui.write = write
1092
1111
1093 commands.resolve(baseui, repo, list=True)
1112 commands.resolve(baseui, repo, list=True)
1094 unresolved = output.getvalue().splitlines(0)
1113 unresolved = output.getvalue().splitlines(0)
1095 return unresolved
1114 return unresolved
1096
1115
1097 @reraise_safe_exceptions
1116 @reraise_safe_exceptions
1098 def merge(self, wire, revision):
1117 def merge(self, wire, revision):
1099 repo = self._factory.repo(wire)
1118 repo = self._factory.repo(wire)
1100 baseui = self._factory._create_config(wire['config'])
1119 baseui = self._factory._create_config(wire['config'])
1101 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1120 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1102
1121
1103 # In case of sub repositories are used mercurial prompts the user in
1122 # In case of sub repositories are used mercurial prompts the user in
1104 # case of merge conflicts or different sub repository sources. By
1123 # case of merge conflicts or different sub repository sources. By
1105 # setting the interactive flag to `False` mercurial doesn't prompt the
1124 # setting the interactive flag to `False` mercurial doesn't prompt the
1106 # used but instead uses a default value.
1125 # used but instead uses a default value.
1107 repo.ui.setconfig(b'ui', b'interactive', b'false')
1126 repo.ui.setconfig(b'ui', b'interactive', b'false')
1108 commands.merge(baseui, repo, rev=safe_bytes(revision))
1127 commands.merge(baseui, repo, rev=safe_bytes(revision))
1109
1128
1110 @reraise_safe_exceptions
1129 @reraise_safe_exceptions
1111 def merge_state(self, wire):
1130 def merge_state(self, wire):
1112 repo = self._factory.repo(wire)
1131 repo = self._factory.repo(wire)
1113 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1132 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1114
1133
1115 # In case of sub repositories are used mercurial prompts the user in
1134 # In case of sub repositories are used mercurial prompts the user in
1116 # case of merge conflicts or different sub repository sources. By
1135 # case of merge conflicts or different sub repository sources. By
1117 # setting the interactive flag to `False` mercurial doesn't prompt the
1136 # setting the interactive flag to `False` mercurial doesn't prompt the
1118 # used but instead uses a default value.
1137 # used but instead uses a default value.
1119 repo.ui.setconfig(b'ui', b'interactive', b'false')
1138 repo.ui.setconfig(b'ui', b'interactive', b'false')
1120 ms = hg_merge.mergestate(repo)
1139 ms = hg_merge.mergestate(repo)
1121 return [x for x in ms.unresolved()]
1140 return [x for x in ms.unresolved()]
1122
1141
1123 @reraise_safe_exceptions
1142 @reraise_safe_exceptions
1124 def commit(self, wire, message, username, close_branch=False):
1143 def commit(self, wire, message, username, close_branch=False):
1125 repo = self._factory.repo(wire)
1144 repo = self._factory.repo(wire)
1126 baseui = self._factory._create_config(wire['config'])
1145 baseui = self._factory._create_config(wire['config'])
1127 repo.ui.setconfig(b'ui', b'username', safe_bytes(username))
1146 repo.ui.setconfig(b'ui', b'username', safe_bytes(username))
1128 commands.commit(baseui, repo, message=safe_bytes(message), close_branch=close_branch)
1147 commands.commit(baseui, repo, message=safe_bytes(message), close_branch=close_branch)
1129
1148
1130 @reraise_safe_exceptions
1149 @reraise_safe_exceptions
1131 def rebase(self, wire, source='', dest='', abort=False):
1150 def rebase(self, wire, source='', dest='', abort=False):
1132
1151
1133 repo = self._factory.repo(wire)
1152 repo = self._factory.repo(wire)
1134 baseui = self._factory._create_config(wire['config'])
1153 baseui = self._factory._create_config(wire['config'])
1135 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1154 repo.ui.setconfig(b'ui', b'merge', b'internal:dump')
1136 # In case of sub repositories are used mercurial prompts the user in
1155 # In case of sub repositories are used mercurial prompts the user in
1137 # case of merge conflicts or different sub repository sources. By
1156 # case of merge conflicts or different sub repository sources. By
1138 # setting the interactive flag to `False` mercurial doesn't prompt the
1157 # setting the interactive flag to `False` mercurial doesn't prompt the
1139 # used but instead uses a default value.
1158 # used but instead uses a default value.
1140 repo.ui.setconfig(b'ui', b'interactive', b'false')
1159 repo.ui.setconfig(b'ui', b'interactive', b'false')
1141
1160
1142 rebase_kws = dict(
1161 rebase_kws = dict(
1143 keep=not abort,
1162 keep=not abort,
1144 abort=abort
1163 abort=abort
1145 )
1164 )
1146
1165
1147 if source:
1166 if source:
1148 source = repo[source]
1167 source = repo[source]
1149 rebase_kws['base'] = [source.hex()]
1168 rebase_kws['base'] = [source.hex()]
1150 if dest:
1169 if dest:
1151 dest = repo[dest]
1170 dest = repo[dest]
1152 rebase_kws['dest'] = dest.hex()
1171 rebase_kws['dest'] = dest.hex()
1153
1172
1154 rebase.rebase(baseui, repo, **rebase_kws)
1173 rebase.rebase(baseui, repo, **rebase_kws)
1155
1174
1156 @reraise_safe_exceptions
1175 @reraise_safe_exceptions
1157 def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone):
1176 def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone):
1158 repo = self._factory.repo(wire)
1177 repo = self._factory.repo(wire)
1159 ctx = self._get_ctx(repo, revision)
1178 ctx = self._get_ctx(repo, revision)
1160 node = ctx.node()
1179 node = ctx.node()
1161
1180
1162 date = (tag_time, tag_timezone)
1181 date = (tag_time, tag_timezone)
1163 try:
1182 try:
1164 hg_tag.tag(repo, safe_bytes(name), node, safe_bytes(message), local, safe_bytes(user), date)
1183 hg_tag.tag(repo, safe_bytes(name), node, safe_bytes(message), local, safe_bytes(user), date)
1165 except Abort as e:
1184 except Abort as e:
1166 log.exception("Tag operation aborted")
1185 log.exception("Tag operation aborted")
1167 # Exception can contain unicode which we convert
1186 # Exception can contain unicode which we convert
1168 raise exceptions.AbortException(e)(repr(e))
1187 raise exceptions.AbortException(e)(repr(e))
1169
1188
1170 @reraise_safe_exceptions
1189 @reraise_safe_exceptions
1171 def bookmark(self, wire, bookmark, revision=''):
1190 def bookmark(self, wire, bookmark, revision=''):
1172 repo = self._factory.repo(wire)
1191 repo = self._factory.repo(wire)
1173 baseui = self._factory._create_config(wire['config'])
1192 baseui = self._factory._create_config(wire['config'])
1174 revision = revision or ''
1193 revision = revision or ''
1175 commands.bookmark(baseui, repo, safe_bytes(bookmark), rev=safe_bytes(revision), force=True)
1194 commands.bookmark(baseui, repo, safe_bytes(bookmark), rev=safe_bytes(revision), force=True)
1176
1195
1177 @reraise_safe_exceptions
1196 @reraise_safe_exceptions
1178 def install_hooks(self, wire, force=False):
1197 def install_hooks(self, wire, force=False):
1179 # we don't need any special hooks for Mercurial
1198 # we don't need any special hooks for Mercurial
1180 pass
1199 pass
1181
1200
1182 @reraise_safe_exceptions
1201 @reraise_safe_exceptions
1183 def get_hooks_info(self, wire):
1202 def get_hooks_info(self, wire):
1184 return {
1203 return {
1185 'pre_version': vcsserver.get_version(),
1204 'pre_version': vcsserver.get_version(),
1186 'post_version': vcsserver.get_version(),
1205 'post_version': vcsserver.get_version(),
1187 }
1206 }
1188
1207
1189 @reraise_safe_exceptions
1208 @reraise_safe_exceptions
1190 def set_head_ref(self, wire, head_name):
1209 def set_head_ref(self, wire, head_name):
1191 pass
1210 pass
1192
1211
1193 @reraise_safe_exceptions
1212 @reraise_safe_exceptions
1194 def archive_repo(self, wire, archive_name_key, kind, mtime, archive_at_path,
1213 def archive_repo(self, wire, archive_name_key, kind, mtime, archive_at_path,
1195 archive_dir_name, commit_id, cache_config):
1214 archive_dir_name, commit_id, cache_config):
1196
1215
1197 def file_walker(_commit_id, path):
1216 def file_walker(_commit_id, path):
1198 repo = self._factory.repo(wire)
1217 repo = self._factory.repo(wire)
1199 ctx = repo[_commit_id]
1218 ctx = repo[_commit_id]
1200 is_root = path in ['', '/']
1219 is_root = path in ['', '/']
1201 if is_root:
1220 if is_root:
1202 matcher = alwaysmatcher(badfn=None)
1221 matcher = alwaysmatcher(badfn=None)
1203 else:
1222 else:
1204 matcher = patternmatcher('', [(b'glob', safe_bytes(path)+b'/**', b'')], badfn=None)
1223 matcher = patternmatcher('', [(b'glob', safe_bytes(path)+b'/**', b'')], badfn=None)
1205 file_iter = ctx.manifest().walk(matcher)
1224 file_iter = ctx.manifest().walk(matcher)
1206
1225
1207 for fn in file_iter:
1226 for fn in file_iter:
1208 file_path = fn
1227 file_path = fn
1209 flags = ctx.flags(fn)
1228 flags = ctx.flags(fn)
1210 mode = b'x' in flags and 0o755 or 0o644
1229 mode = b'x' in flags and 0o755 or 0o644
1211 is_link = b'l' in flags
1230 is_link = b'l' in flags
1212
1231
1213 yield ArchiveNode(file_path, mode, is_link, ctx[fn].data)
1232 yield ArchiveNode(file_path, mode, is_link, ctx[fn].data)
1214
1233
1215 return store_archive_in_cache(
1234 return store_archive_in_cache(
1216 file_walker, archive_name_key, kind, mtime, archive_at_path, archive_dir_name, commit_id, cache_config=cache_config)
1235 file_walker, archive_name_key, kind, mtime, archive_at_path, archive_dir_name, commit_id, cache_config=cache_config)
1217
1236
General Comments 0
You need to be logged in to leave comments. Login now