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