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