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