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