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