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