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