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