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