##// END OF EJS Templates
error-handlig: pass in Abort exception info to RhodeCode from vcsserver.
marcink -
r123:28e4c7cf stable
parent child Browse files
Show More
@@ -1,580 +1,581 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 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 logging
19 19 import os
20 20 import posixpath as vcspath
21 21 import re
22 22 import stat
23 23 import urllib
24 24 import urllib2
25 25 from functools import wraps
26 26
27 27 from dulwich import index, objects
28 28 from dulwich.client import HttpGitClient, LocalGitClient
29 29 from dulwich.errors import (
30 30 NotGitRepository, ChecksumMismatch, WrongObjectException,
31 31 MissingCommitError, ObjectMissing, HangupException,
32 32 UnexpectedCommandError)
33 33 from dulwich.repo import Repo as DulwichRepo, Tag
34 34 from dulwich.server import update_server_info
35 35
36 36 from vcsserver import exceptions, settings, subprocessio
37 37 from vcsserver.utils import safe_str
38 38 from vcsserver.base import RepoFactory, obfuscate_qs
39 39 from vcsserver.hgcompat import (
40 40 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
41 41
42 42
43 43 DIR_STAT = stat.S_IFDIR
44 44 FILE_MODE = stat.S_IFMT
45 45 GIT_LINK = objects.S_IFGITLINK
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 def reraise_safe_exceptions(func):
51 51 """Converts Dulwich exceptions to something neutral."""
52 52 @wraps(func)
53 53 def wrapper(*args, **kwargs):
54 54 try:
55 55 return func(*args, **kwargs)
56 56 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
57 57 ObjectMissing) as e:
58 58 raise exceptions.LookupException(e.message)
59 59 except (HangupException, UnexpectedCommandError) as e:
60 60 raise exceptions.VcsException(e.message)
61 61 return wrapper
62 62
63 63
64 64 class Repo(DulwichRepo):
65 65 """
66 66 A wrapper for dulwich Repo class.
67 67
68 68 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
69 69 "Too many open files" error. We need to close all opened file descriptors
70 70 once the repo object is destroyed.
71 71
72 72 TODO: mikhail: please check if we need this wrapper after updating dulwich
73 73 to 0.12.0 +
74 74 """
75 75 def __del__(self):
76 76 if hasattr(self, 'object_store'):
77 77 self.close()
78 78
79 79
80 80 class GitFactory(RepoFactory):
81 81
82 82 def _create_repo(self, wire, create):
83 83 repo_path = str_to_dulwich(wire['path'])
84 84 return Repo(repo_path)
85 85
86 86
87 87 class GitRemote(object):
88 88
89 89 def __init__(self, factory):
90 90 self._factory = factory
91 91
92 92 self._bulk_methods = {
93 93 "author": self.commit_attribute,
94 94 "date": self.get_object_attrs,
95 95 "message": self.commit_attribute,
96 96 "parents": self.commit_attribute,
97 97 "_commit": self.revision,
98 98 }
99 99
100 100 def _assign_ref(self, wire, ref, commit_id):
101 101 repo = self._factory.repo(wire)
102 102 repo[ref] = commit_id
103 103
104 104 @reraise_safe_exceptions
105 105 def add_object(self, wire, content):
106 106 repo = self._factory.repo(wire)
107 107 blob = objects.Blob()
108 108 blob.set_raw_string(content)
109 109 repo.object_store.add_object(blob)
110 110 return blob.id
111 111
112 112 @reraise_safe_exceptions
113 113 def assert_correct_path(self, wire):
114 114 try:
115 115 self._factory.repo(wire)
116 116 except NotGitRepository as e:
117 117 # Exception can contain unicode which we convert
118 118 raise exceptions.AbortException(repr(e))
119 119
120 120 @reraise_safe_exceptions
121 121 def bare(self, wire):
122 122 repo = self._factory.repo(wire)
123 123 return repo.bare
124 124
125 125 @reraise_safe_exceptions
126 126 def blob_as_pretty_string(self, wire, sha):
127 127 repo = self._factory.repo(wire)
128 128 return repo[sha].as_pretty_string()
129 129
130 130 @reraise_safe_exceptions
131 131 def blob_raw_length(self, wire, sha):
132 132 repo = self._factory.repo(wire)
133 133 blob = repo[sha]
134 134 return blob.raw_length()
135 135
136 136 @reraise_safe_exceptions
137 137 def bulk_request(self, wire, rev, pre_load):
138 138 result = {}
139 139 for attr in pre_load:
140 140 try:
141 141 method = self._bulk_methods[attr]
142 142 args = [wire, rev]
143 143 if attr == "date":
144 144 args.extend(["commit_time", "commit_timezone"])
145 145 elif attr in ["author", "message", "parents"]:
146 146 args.append(attr)
147 147 result[attr] = method(*args)
148 148 except KeyError:
149 149 raise exceptions.VcsException(
150 150 "Unknown bulk attribute: %s" % attr)
151 151 return result
152 152
153 153 def _build_opener(self, url):
154 154 handlers = []
155 155 url_obj = url_parser(url)
156 156 _, authinfo = url_obj.authinfo()
157 157
158 158 if authinfo:
159 159 # create a password manager
160 160 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
161 161 passmgr.add_password(*authinfo)
162 162
163 163 handlers.extend((httpbasicauthhandler(passmgr),
164 164 httpdigestauthhandler(passmgr)))
165 165
166 166 return urllib2.build_opener(*handlers)
167 167
168 168 @reraise_safe_exceptions
169 169 def check_url(self, url, config):
170 170 url_obj = url_parser(url)
171 171 test_uri, _ = url_obj.authinfo()
172 172 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
173 173 url_obj.query = obfuscate_qs(url_obj.query)
174 174 cleaned_uri = str(url_obj)
175 175 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
176 176
177 177 if not test_uri.endswith('info/refs'):
178 178 test_uri = test_uri.rstrip('/') + '/info/refs'
179 179
180 180 o = self._build_opener(url)
181 181 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
182 182
183 183 q = {"service": 'git-upload-pack'}
184 184 qs = '?%s' % urllib.urlencode(q)
185 185 cu = "%s%s" % (test_uri, qs)
186 186 req = urllib2.Request(cu, None, {})
187 187
188 188 try:
189 189 log.debug("Trying to open URL %s", cleaned_uri)
190 190 resp = o.open(req)
191 191 if resp.code != 200:
192 192 raise exceptions.URLError('Return Code is not 200')
193 193 except Exception as e:
194 194 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
195 195 # means it cannot be cloned
196 196 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
197 197
198 198 # now detect if it's proper git repo
199 199 gitdata = resp.read()
200 200 if 'service=git-upload-pack' in gitdata:
201 201 pass
202 202 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
203 203 # old style git can return some other format !
204 204 pass
205 205 else:
206 206 raise exceptions.URLError(
207 207 "url [%s] does not look like an git" % (cleaned_uri,))
208 208
209 209 return True
210 210
211 211 @reraise_safe_exceptions
212 212 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
213 213 remote_refs = self.fetch(wire, url, apply_refs=False)
214 214 repo = self._factory.repo(wire)
215 215 if isinstance(valid_refs, list):
216 216 valid_refs = tuple(valid_refs)
217 217
218 218 for k in remote_refs:
219 219 # only parse heads/tags and skip so called deferred tags
220 220 if k.startswith(valid_refs) and not k.endswith(deferred):
221 221 repo[k] = remote_refs[k]
222 222
223 223 if update_after_clone:
224 224 # we want to checkout HEAD
225 225 repo["HEAD"] = remote_refs["HEAD"]
226 226 index.build_index_from_tree(repo.path, repo.index_path(),
227 227 repo.object_store, repo["HEAD"].tree)
228 228
229 229 # TODO: this is quite complex, check if that can be simplified
230 230 @reraise_safe_exceptions
231 231 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
232 232 repo = self._factory.repo(wire)
233 233 object_store = repo.object_store
234 234
235 235 # Create tree and populates it with blobs
236 236 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
237 237
238 238 for node in updated:
239 239 # Compute subdirs if needed
240 240 dirpath, nodename = vcspath.split(node['path'])
241 241 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
242 242 parent = commit_tree
243 243 ancestors = [('', parent)]
244 244
245 245 # Tries to dig for the deepest existing tree
246 246 while dirnames:
247 247 curdir = dirnames.pop(0)
248 248 try:
249 249 dir_id = parent[curdir][1]
250 250 except KeyError:
251 251 # put curdir back into dirnames and stops
252 252 dirnames.insert(0, curdir)
253 253 break
254 254 else:
255 255 # If found, updates parent
256 256 parent = repo[dir_id]
257 257 ancestors.append((curdir, parent))
258 258 # Now parent is deepest existing tree and we need to create
259 259 # subtrees for dirnames (in reverse order)
260 260 # [this only applies for nodes from added]
261 261 new_trees = []
262 262
263 263 blob = objects.Blob.from_string(node['content'])
264 264
265 265 if dirnames:
266 266 # If there are trees which should be created we need to build
267 267 # them now (in reverse order)
268 268 reversed_dirnames = list(reversed(dirnames))
269 269 curtree = objects.Tree()
270 270 curtree[node['node_path']] = node['mode'], blob.id
271 271 new_trees.append(curtree)
272 272 for dirname in reversed_dirnames[:-1]:
273 273 newtree = objects.Tree()
274 274 newtree[dirname] = (DIR_STAT, curtree.id)
275 275 new_trees.append(newtree)
276 276 curtree = newtree
277 277 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
278 278 else:
279 279 parent.add(
280 280 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
281 281
282 282 new_trees.append(parent)
283 283 # Update ancestors
284 284 reversed_ancestors = reversed(
285 285 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
286 286 for parent, tree, path in reversed_ancestors:
287 287 parent[path] = (DIR_STAT, tree.id)
288 288 object_store.add_object(tree)
289 289
290 290 object_store.add_object(blob)
291 291 for tree in new_trees:
292 292 object_store.add_object(tree)
293 293
294 294 for node_path in removed:
295 295 paths = node_path.split('/')
296 296 tree = commit_tree
297 297 trees = [tree]
298 298 # Traverse deep into the forest...
299 299 for path in paths:
300 300 try:
301 301 obj = repo[tree[path][1]]
302 302 if isinstance(obj, objects.Tree):
303 303 trees.append(obj)
304 304 tree = obj
305 305 except KeyError:
306 306 break
307 307 # Cut down the blob and all rotten trees on the way back...
308 308 for path, tree in reversed(zip(paths, trees)):
309 309 del tree[path]
310 310 if tree:
311 311 # This tree still has elements - don't remove it or any
312 312 # of it's parents
313 313 break
314 314
315 315 object_store.add_object(commit_tree)
316 316
317 317 # Create commit
318 318 commit = objects.Commit()
319 319 commit.tree = commit_tree.id
320 320 for k, v in commit_data.iteritems():
321 321 setattr(commit, k, v)
322 322 object_store.add_object(commit)
323 323
324 324 ref = 'refs/heads/%s' % branch
325 325 repo.refs[ref] = commit.id
326 326
327 327 return commit.id
328 328
329 329 @reraise_safe_exceptions
330 330 def fetch(self, wire, url, apply_refs=True, refs=None):
331 331 if url != 'default' and '://' not in url:
332 332 client = LocalGitClient(url)
333 333 else:
334 334 url_obj = url_parser(url)
335 335 o = self._build_opener(url)
336 336 url, _ = url_obj.authinfo()
337 337 client = HttpGitClient(base_url=url, opener=o)
338 338 repo = self._factory.repo(wire)
339 339
340 340 determine_wants = repo.object_store.determine_wants_all
341 341 if refs:
342 342 def determine_wants_requested(references):
343 343 return [references[r] for r in references if r in refs]
344 344 determine_wants = determine_wants_requested
345 345
346 346 try:
347 347 remote_refs = client.fetch(
348 348 path=url, target=repo, determine_wants=determine_wants)
349 except NotGitRepository:
349 except NotGitRepository as e:
350 350 log.warning(
351 351 'Trying to fetch from "%s" failed, not a Git repository.', url)
352 raise exceptions.AbortException()
352 # Exception can contain unicode which we convert
353 raise exceptions.AbortException(repr(e))
353 354
354 355 # mikhail: client.fetch() returns all the remote refs, but fetches only
355 356 # refs filtered by `determine_wants` function. We need to filter result
356 357 # as well
357 358 if refs:
358 359 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
359 360
360 361 if apply_refs:
361 362 # TODO: johbo: Needs proper test coverage with a git repository
362 363 # that contains a tag object, so that we would end up with
363 364 # a peeled ref at this point.
364 365 PEELED_REF_MARKER = '^{}'
365 366 for k in remote_refs:
366 367 if k.endswith(PEELED_REF_MARKER):
367 368 log.info("Skipping peeled reference %s", k)
368 369 continue
369 370 repo[k] = remote_refs[k]
370 371
371 372 if refs:
372 373 # mikhail: explicitly set the head to the last ref.
373 374 repo['HEAD'] = remote_refs[refs[-1]]
374 375
375 376 # TODO: mikhail: should we return remote_refs here to be
376 377 # consistent?
377 378 else:
378 379 return remote_refs
379 380
380 381 @reraise_safe_exceptions
381 382 def get_remote_refs(self, wire, url):
382 383 repo = Repo(url)
383 384 return repo.get_refs()
384 385
385 386 @reraise_safe_exceptions
386 387 def get_description(self, wire):
387 388 repo = self._factory.repo(wire)
388 389 return repo.get_description()
389 390
390 391 @reraise_safe_exceptions
391 392 def get_file_history(self, wire, file_path, commit_id, limit):
392 393 repo = self._factory.repo(wire)
393 394 include = [commit_id]
394 395 paths = [file_path]
395 396
396 397 walker = repo.get_walker(include, paths=paths, max_entries=limit)
397 398 return [x.commit.id for x in walker]
398 399
399 400 @reraise_safe_exceptions
400 401 def get_missing_revs(self, wire, rev1, rev2, path2):
401 402 repo = self._factory.repo(wire)
402 403 LocalGitClient(thin_packs=False).fetch(path2, repo)
403 404
404 405 wire_remote = wire.copy()
405 406 wire_remote['path'] = path2
406 407 repo_remote = self._factory.repo(wire_remote)
407 408 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
408 409
409 410 revs = [
410 411 x.commit.id
411 412 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
412 413 return revs
413 414
414 415 @reraise_safe_exceptions
415 416 def get_object(self, wire, sha):
416 417 repo = self._factory.repo(wire)
417 418 obj = repo.get_object(sha)
418 419 commit_id = obj.id
419 420
420 421 if isinstance(obj, Tag):
421 422 commit_id = obj.object[1]
422 423
423 424 return {
424 425 'id': obj.id,
425 426 'type': obj.type_name,
426 427 'commit_id': commit_id
427 428 }
428 429
429 430 @reraise_safe_exceptions
430 431 def get_object_attrs(self, wire, sha, *attrs):
431 432 repo = self._factory.repo(wire)
432 433 obj = repo.get_object(sha)
433 434 return list(getattr(obj, a) for a in attrs)
434 435
435 436 @reraise_safe_exceptions
436 437 def get_refs(self, wire):
437 438 repo = self._factory.repo(wire)
438 439 result = {}
439 440 for ref, sha in repo.refs.as_dict().items():
440 441 peeled_sha = repo.get_peeled(ref)
441 442 result[ref] = peeled_sha
442 443 return result
443 444
444 445 @reraise_safe_exceptions
445 446 def get_refs_path(self, wire):
446 447 repo = self._factory.repo(wire)
447 448 return repo.refs.path
448 449
449 450 @reraise_safe_exceptions
450 451 def head(self, wire):
451 452 repo = self._factory.repo(wire)
452 453 return repo.head()
453 454
454 455 @reraise_safe_exceptions
455 456 def init(self, wire):
456 457 repo_path = str_to_dulwich(wire['path'])
457 458 self.repo = Repo.init(repo_path)
458 459
459 460 @reraise_safe_exceptions
460 461 def init_bare(self, wire):
461 462 repo_path = str_to_dulwich(wire['path'])
462 463 self.repo = Repo.init_bare(repo_path)
463 464
464 465 @reraise_safe_exceptions
465 466 def revision(self, wire, rev):
466 467 repo = self._factory.repo(wire)
467 468 obj = repo[rev]
468 469 obj_data = {
469 470 'id': obj.id,
470 471 }
471 472 try:
472 473 obj_data['tree'] = obj.tree
473 474 except AttributeError:
474 475 pass
475 476 return obj_data
476 477
477 478 @reraise_safe_exceptions
478 479 def commit_attribute(self, wire, rev, attr):
479 480 repo = self._factory.repo(wire)
480 481 obj = repo[rev]
481 482 return getattr(obj, attr)
482 483
483 484 @reraise_safe_exceptions
484 485 def set_refs(self, wire, key, value):
485 486 repo = self._factory.repo(wire)
486 487 repo.refs[key] = value
487 488
488 489 @reraise_safe_exceptions
489 490 def remove_ref(self, wire, key):
490 491 repo = self._factory.repo(wire)
491 492 del repo.refs[key]
492 493
493 494 @reraise_safe_exceptions
494 495 def tree_changes(self, wire, source_id, target_id):
495 496 repo = self._factory.repo(wire)
496 497 source = repo[source_id].tree if source_id else None
497 498 target = repo[target_id].tree
498 499 result = repo.object_store.tree_changes(source, target)
499 500 return list(result)
500 501
501 502 @reraise_safe_exceptions
502 503 def tree_items(self, wire, tree_id):
503 504 repo = self._factory.repo(wire)
504 505 tree = repo[tree_id]
505 506
506 507 result = []
507 508 for item in tree.iteritems():
508 509 item_sha = item.sha
509 510 item_mode = item.mode
510 511
511 512 if FILE_MODE(item_mode) == GIT_LINK:
512 513 item_type = "link"
513 514 else:
514 515 item_type = repo[item_sha].type_name
515 516
516 517 result.append((item.path, item_mode, item_sha, item_type))
517 518 return result
518 519
519 520 @reraise_safe_exceptions
520 521 def update_server_info(self, wire):
521 522 repo = self._factory.repo(wire)
522 523 update_server_info(repo)
523 524
524 525 @reraise_safe_exceptions
525 526 def discover_git_version(self):
526 527 stdout, _ = self.run_git_command(
527 528 {}, ['--version'], _bare=True, _safe=True)
528 529 prefix = 'git version'
529 530 if stdout.startswith(prefix):
530 531 stdout = stdout[len(prefix):]
531 532 return stdout.strip()
532 533
533 534 @reraise_safe_exceptions
534 535 def run_git_command(self, wire, cmd, **opts):
535 536 path = wire.get('path', None)
536 537
537 538 if path and os.path.isdir(path):
538 539 opts['cwd'] = path
539 540
540 541 if '_bare' in opts:
541 542 _copts = []
542 543 del opts['_bare']
543 544 else:
544 545 _copts = ['-c', 'core.quotepath=false', ]
545 546 safe_call = False
546 547 if '_safe' in opts:
547 548 # no exc on failure
548 549 del opts['_safe']
549 550 safe_call = True
550 551
551 552 gitenv = os.environ.copy()
552 553 gitenv.update(opts.pop('extra_env', {}))
553 554 # need to clean fix GIT_DIR !
554 555 if 'GIT_DIR' in gitenv:
555 556 del gitenv['GIT_DIR']
556 557 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
557 558
558 559 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
559 560
560 561 try:
561 562 _opts = {'env': gitenv, 'shell': False}
562 563 _opts.update(opts)
563 564 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
564 565
565 566 return ''.join(p), ''.join(p.error)
566 567 except (EnvironmentError, OSError) as err:
567 568 tb_err = ("Couldn't run git command (%s).\n"
568 569 "Original error was:%s\n" % (cmd, err))
569 570 log.exception(tb_err)
570 571 if safe_call:
571 572 return '', err
572 573 else:
573 574 raise exceptions.VcsException(tb_err)
574 575
575 576
576 577 def str_to_dulwich(value):
577 578 """
578 579 Dulwich 0.10.1a requires `unicode` objects to be passed in.
579 580 """
580 581 return value.decode(settings.WIRE_ENCODING)
@@ -1,724 +1,725 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 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 filter = match(file_filter[0], '', [file_filter[1]])
393 393 else:
394 394 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=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 except Abort:
610 except Abort as e:
611 611 log.exception("Tag operation aborted")
612 raise exceptions.AbortException()
612 # Exception can contain unicode which we convert
613 raise exceptions.AbortException(repr(e))
613 614
614 615 @reraise_safe_exceptions
615 616 def tags(self, wire):
616 617 repo = self._factory.repo(wire)
617 618 return repo.tags()
618 619
619 620 @reraise_safe_exceptions
620 621 def update(self, wire, node=None, clean=False):
621 622 repo = self._factory.repo(wire)
622 623 baseui = self._factory._create_config(wire['config'])
623 624 commands.update(baseui, repo, node=node, clean=clean)
624 625
625 626 @reraise_safe_exceptions
626 627 def identify(self, wire):
627 628 repo = self._factory.repo(wire)
628 629 baseui = self._factory._create_config(wire['config'])
629 630 output = io.BytesIO()
630 631 baseui.write = output.write
631 632 # This is required to get a full node id
632 633 baseui.debugflag = True
633 634 commands.identify(baseui, repo, id=True)
634 635
635 636 return output.getvalue()
636 637
637 638 @reraise_safe_exceptions
638 639 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
639 640 hooks=True):
640 641 repo = self._factory.repo(wire)
641 642 baseui = self._factory._create_config(wire['config'], hooks=hooks)
642 643
643 644 # Mercurial internally has a lot of logic that checks ONLY if
644 645 # option is defined, we just pass those if they are defined then
645 646 opts = {}
646 647 if bookmark:
647 648 opts['bookmark'] = bookmark
648 649 if branch:
649 650 opts['branch'] = branch
650 651 if revision:
651 652 opts['rev'] = revision
652 653
653 654 commands.pull(baseui, repo, source, **opts)
654 655
655 656 @reraise_safe_exceptions
656 657 def heads(self, wire, branch=None):
657 658 repo = self._factory.repo(wire)
658 659 baseui = self._factory._create_config(wire['config'])
659 660 output = io.BytesIO()
660 661
661 662 def write(data, **unused_kwargs):
662 663 output.write(data)
663 664
664 665 baseui.write = write
665 666 if branch:
666 667 args = [branch]
667 668 else:
668 669 args = []
669 670 commands.heads(baseui, repo, template='{node} ', *args)
670 671
671 672 return output.getvalue()
672 673
673 674 @reraise_safe_exceptions
674 675 def ancestor(self, wire, revision1, revision2):
675 676 repo = self._factory.repo(wire)
676 677 baseui = self._factory._create_config(wire['config'])
677 678 output = io.BytesIO()
678 679 baseui.write = output.write
679 680 commands.debugancestor(baseui, repo, revision1, revision2)
680 681
681 682 return output.getvalue()
682 683
683 684 @reraise_safe_exceptions
684 685 def push(self, wire, revisions, dest_path, hooks=True,
685 686 push_branches=False):
686 687 repo = self._factory.repo(wire)
687 688 baseui = self._factory._create_config(wire['config'], hooks=hooks)
688 689 commands.push(baseui, repo, dest=dest_path, rev=revisions,
689 690 new_branch=push_branches)
690 691
691 692 @reraise_safe_exceptions
692 693 def merge(self, wire, revision):
693 694 repo = self._factory.repo(wire)
694 695 baseui = self._factory._create_config(wire['config'])
695 696 repo.ui.setconfig('ui', 'merge', 'internal:dump')
696 697
697 698 # In case of sub repositories are used mercurial prompts the user in
698 699 # case of merge conflicts or different sub repository sources. By
699 700 # setting the interactive flag to `False` mercurial doesn't prompt the
700 701 # used but instead uses a default value.
701 702 repo.ui.setconfig('ui', 'interactive', False)
702 703
703 704 commands.merge(baseui, repo, rev=revision)
704 705
705 706 @reraise_safe_exceptions
706 707 def commit(self, wire, message, username):
707 708 repo = self._factory.repo(wire)
708 709 baseui = self._factory._create_config(wire['config'])
709 710 repo.ui.setconfig('ui', 'username', username)
710 711 commands.commit(baseui, repo, message=message)
711 712
712 713 @reraise_safe_exceptions
713 714 def rebase(self, wire, source=None, dest=None, abort=False):
714 715 repo = self._factory.repo(wire)
715 716 baseui = self._factory._create_config(wire['config'])
716 717 repo.ui.setconfig('ui', 'merge', 'internal:dump')
717 718 rebase.rebase(
718 719 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
719 720
720 721 @reraise_safe_exceptions
721 722 def bookmark(self, wire, bookmark, revision=None):
722 723 repo = self._factory.repo(wire)
723 724 baseui = self._factory._create_config(wire['config'])
724 725 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
General Comments 0
You need to be logged in to leave comments. Login now