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