hg.py
856 lines
| 29.3 KiB
| text/x-python
|
PythonLexer
/ vcsserver / hg.py
r0 | # RhodeCode VCSServer provides access to different vcs backends via network. | |||
r620 | # Copyright (C) 2014-2019 RhodeCode GmbH | |||
r0 | # | |||
# This program is free software; you can redistribute it and/or modify | ||||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation; either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program; if not, write to the Free Software Foundation, | ||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
import io | ||||
import logging | ||||
import stat | ||||
import urllib | ||||
import urllib2 | ||||
r657 | import traceback | |||
r0 | ||||
from hgext import largefiles, rebase | ||||
from hgext.strip import strip as hgext_strip | ||||
from mercurial import commands | ||||
from mercurial import unionrepo | ||||
r179 | from mercurial import verify | |||
r0 | ||||
r623 | import vcsserver | |||
r0 | from vcsserver import exceptions | |||
r171 | from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original | |||
r0 | from vcsserver.hgcompat import ( | |||
r660 | archival, bin, clone, config as hgconfig, diffopts, hex, get_ctx, | |||
r105 | hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler, | |||
r668 | makepeer, instance, match, memctx, exchange, memfilectx, nullrev, hg_merge, | |||
r274 | patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError, | |||
r660 | RepoLookupError, InterventionRequired, RequirementError) | |||
r0 | ||||
log = logging.getLogger(__name__) | ||||
def make_ui_from_config(repo_config): | ||||
r668 | ||||
class LoggingUI(ui.ui): | ||||
def status(self, *msg, **opts): | ||||
log.info(' '.join(msg).rstrip('\n')) | ||||
super(LoggingUI, self).status(*msg, **opts) | ||||
def warn(self, *msg, **opts): | ||||
log.warn(' '.join(msg).rstrip('\n')) | ||||
super(LoggingUI, self).warn(*msg, **opts) | ||||
def error(self, *msg, **opts): | ||||
log.error(' '.join(msg).rstrip('\n')) | ||||
super(LoggingUI, self).error(*msg, **opts) | ||||
def note(self, *msg, **opts): | ||||
log.info(' '.join(msg).rstrip('\n')) | ||||
super(LoggingUI, self).note(*msg, **opts) | ||||
def debug(self, *msg, **opts): | ||||
log.debug(' '.join(msg).rstrip('\n')) | ||||
super(LoggingUI, self).debug(*msg, **opts) | ||||
baseui = LoggingUI() | ||||
r0 | ||||
# clean the baseui object | ||||
baseui._ocfg = hgconfig.config() | ||||
baseui._ucfg = hgconfig.config() | ||||
baseui._tcfg = hgconfig.config() | ||||
for section, option, value in repo_config: | ||||
baseui.setconfig(section, option, value) | ||||
# make our hgweb quiet so it doesn't print output | ||||
baseui.setconfig('ui', 'quiet', 'true') | ||||
r349 | baseui.setconfig('ui', 'paginate', 'never') | |||
r657 | # for better Error reporting of Mercurial | |||
baseui.setconfig('ui', 'message-output', 'stderr') | ||||
r0 | # force mercurial to only use 1 thread, otherwise it may try to set a | |||
# signal in a non-main thread, thus generating a ValueError. | ||||
baseui.setconfig('worker', 'numcpus', 1) | ||||
Martin Bornhold
|
r36 | # If there is no config for the largefiles extension, we explicitly disable | ||
# it here. This overrides settings from repositories hgrc file. Recent | ||||
# mercurial versions enable largefiles in hgrc on clone from largefile | ||||
# repo. | ||||
if not baseui.hasconfig('extensions', 'largefiles'): | ||||
log.debug('Explicitly disable largefiles extension for repo.') | ||||
baseui.setconfig('extensions', 'largefiles', '!') | ||||
r0 | return baseui | |||
def reraise_safe_exceptions(func): | ||||
"""Decorator for converting mercurial exceptions to something neutral.""" | ||||
def wrapper(*args, **kwargs): | ||||
try: | ||||
return func(*args, **kwargs) | ||||
r490 | except (Abort, InterventionRequired) as e: | |||
raise_from_original(exceptions.AbortException(e)) | ||||
except RepoLookupError as e: | ||||
raise_from_original(exceptions.LookupException(e)) | ||||
except RequirementError as e: | ||||
raise_from_original(exceptions.RequirementException(e)) | ||||
except RepoError as e: | ||||
raise_from_original(exceptions.VcsException(e)) | ||||
except LookupError as e: | ||||
raise_from_original(exceptions.LookupException(e)) | ||||
r0 | except Exception as e: | |||
if not hasattr(e, '_vcs_kind'): | ||||
log.exception("Unhandled exception in hg remote call") | ||||
r490 | raise_from_original(exceptions.UnhandledException(e)) | |||
r0 | raise | |||
return wrapper | ||||
class MercurialFactory(RepoFactory): | ||||
r483 | repo_type = 'hg' | |||
r0 | ||||
def _create_config(self, config, hooks=True): | ||||
if not hooks: | ||||
hooks_to_clean = frozenset(( | ||||
'changegroup.repo_size', 'preoutgoing.pre_pull', | ||||
'outgoing.pull_logger', 'prechangegroup.pre_push')) | ||||
new_config = [] | ||||
for section, option, value in config: | ||||
if section == 'hooks' and option in hooks_to_clean: | ||||
continue | ||||
new_config.append((section, option, value)) | ||||
config = new_config | ||||
baseui = make_ui_from_config(config) | ||||
return baseui | ||||
def _create_repo(self, wire, create): | ||||
baseui = self._create_config(wire["config"]) | ||||
r656 | return instance(baseui, wire["path"], create) | |||
r0 | ||||
class HgRemote(object): | ||||
def __init__(self, factory): | ||||
self._factory = factory | ||||
self._bulk_methods = { | ||||
"affected_files": self.ctx_files, | ||||
"author": self.ctx_user, | ||||
"branch": self.ctx_branch, | ||||
"children": self.ctx_children, | ||||
"date": self.ctx_date, | ||||
"message": self.ctx_description, | ||||
"parents": self.ctx_parents, | ||||
"status": self.ctx_status, | ||||
r275 | "obsolete": self.ctx_obsolete, | |||
"phase": self.ctx_phase, | ||||
"hidden": self.ctx_hidden, | ||||
r0 | "_file_paths": self.ctx_list, | |||
} | ||||
r660 | def _get_ctx(self, repo, ref): | |||
return get_ctx(repo, ref) | ||||
r0 | @reraise_safe_exceptions | |||
r101 | def discover_hg_version(self): | |||
from mercurial import util | ||||
return util.version() | ||||
@reraise_safe_exceptions | ||||
r698 | def is_empty(self, wire): | |||
repo = self._factory.repo(wire) | ||||
try: | ||||
return len(repo) == 0 | ||||
except Exception: | ||||
log.exception("failed to read object_store") | ||||
return False | ||||
@reraise_safe_exceptions | ||||
r0 | def archive_repo(self, archive_path, mtime, file_info, kind): | |||
if kind == "tgz": | ||||
archiver = archival.tarit(archive_path, mtime, "gz") | ||||
elif kind == "tbz2": | ||||
archiver = archival.tarit(archive_path, mtime, "bz2") | ||||
elif kind == 'zip': | ||||
archiver = archival.zipit(archive_path, mtime) | ||||
else: | ||||
r490 | raise exceptions.ArchiveException()( | |||
r0 | 'Remote does not support: "%s".' % kind) | |||
for f_path, f_mode, f_is_link, f_content in file_info: | ||||
archiver.addfile(f_path, f_mode, f_is_link, f_content) | ||||
archiver.done() | ||||
@reraise_safe_exceptions | ||||
def bookmarks(self, wire): | ||||
repo = self._factory.repo(wire) | ||||
return dict(repo._bookmarks) | ||||
@reraise_safe_exceptions | ||||
def branches(self, wire, normal, closed): | ||||
repo = self._factory.repo(wire) | ||||
iter_branches = repo.branchmap().iterbranches() | ||||
bt = {} | ||||
for branch_name, _heads, tip, is_closed in iter_branches: | ||||
if normal and not is_closed: | ||||
bt[branch_name] = tip | ||||
if closed and is_closed: | ||||
bt[branch_name] = tip | ||||
return bt | ||||
@reraise_safe_exceptions | ||||
def bulk_request(self, wire, rev, pre_load): | ||||
result = {} | ||||
for attr in pre_load: | ||||
try: | ||||
method = self._bulk_methods[attr] | ||||
result[attr] = method(wire, rev) | ||||
r490 | except KeyError as e: | |||
raise exceptions.VcsException(e)( | ||||
r0 | 'Unknown bulk attribute: "%s"' % attr) | |||
return result | ||||
@reraise_safe_exceptions | ||||
def clone(self, wire, source, dest, update_after_clone=False, hooks=True): | ||||
baseui = self._factory._create_config(wire["config"], hooks=hooks) | ||||
clone(baseui, source, dest, noupdate=not update_after_clone) | ||||
@reraise_safe_exceptions | ||||
def commitctx( | ||||
self, wire, message, parents, commit_time, commit_timezone, | ||||
user, files, extra, removed, updated): | ||||
r659 | repo = self._factory.repo(wire) | |||
baseui = self._factory._create_config(wire['config']) | ||||
publishing = baseui.configbool('phases', 'publish') | ||||
if publishing: | ||||
new_commit = 'public' | ||||
else: | ||||
new_commit = 'draft' | ||||
def _filectxfn(_repo, ctx, path): | ||||
r0 | """ | |||
Marks given path as added/changed/removed in a given _repo. This is | ||||
for internal mercurial commit function. | ||||
""" | ||||
# check if this path is removed | ||||
if path in removed: | ||||
# returning None is a way to mark node for removal | ||||
return None | ||||
# check if this path is added | ||||
for node in updated: | ||||
if node['path'] == path: | ||||
return memfilectx( | ||||
_repo, | ||||
r659 | changectx=ctx, | |||
r0 | path=node['path'], | |||
data=node['content'], | ||||
islink=False, | ||||
isexec=bool(node['mode'] & stat.S_IXUSR), | ||||
r432 | copied=False) | |||
r0 | ||||
r490 | raise exceptions.AbortException()( | |||
r0 | "Given path haven't been marked as added, " | |||
"changed or removed (%s)" % path) | ||||
r659 | with repo.ui.configoverride({('phases', 'new-commit'): new_commit}): | |||
r0 | ||||
r659 | commit_ctx = memctx( | |||
repo=repo, | ||||
parents=parents, | ||||
text=message, | ||||
files=files, | ||||
filectxfn=_filectxfn, | ||||
user=user, | ||||
date=(commit_time, commit_timezone), | ||||
extra=extra) | ||||
r0 | ||||
r659 | n = repo.commitctx(commit_ctx) | |||
new_id = hex(n) | ||||
r0 | ||||
r659 | return new_id | |||
r0 | ||||
@reraise_safe_exceptions | ||||
def ctx_branch(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.branch() | |||
@reraise_safe_exceptions | ||||
def ctx_children(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return [child.rev() for child in ctx.children()] | |||
@reraise_safe_exceptions | ||||
def ctx_date(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.date() | |||
@reraise_safe_exceptions | ||||
def ctx_description(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.description() | |||
@reraise_safe_exceptions | ||||
def ctx_files(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.files() | |||
@reraise_safe_exceptions | ||||
def ctx_list(self, path, revision): | ||||
repo = self._factory.repo(path) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return list(ctx) | |||
@reraise_safe_exceptions | ||||
def ctx_parents(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return [parent.rev() for parent in ctx.parents()] | |||
@reraise_safe_exceptions | ||||
r215 | def ctx_phase(self, wire, revision): | |||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r215 | # public=0, draft=1, secret=3 | |||
return ctx.phase() | ||||
@reraise_safe_exceptions | ||||
r219 | def ctx_obsolete(self, wire, revision): | |||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r219 | return ctx.obsolete() | |||
@reraise_safe_exceptions | ||||
def ctx_hidden(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r219 | return ctx.hidden() | |||
@reraise_safe_exceptions | ||||
r0 | def ctx_substate(self, wire, revision): | |||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.substate | |||
@reraise_safe_exceptions | ||||
def ctx_status(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | status = repo[ctx.p1().node()].status(other=ctx.node()) | |||
# object of status (odd, custom named tuple in mercurial) is not | ||||
r213 | # correctly serializable, we make it a list, as the underling | |||
r0 | # API expects this to be a list | |||
return list(status) | ||||
@reraise_safe_exceptions | ||||
def ctx_user(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | return ctx.user() | |||
@reraise_safe_exceptions | ||||
def check_url(self, url, config): | ||||
_proto = None | ||||
if '+' in url[:url.find('://')]: | ||||
_proto = url[0:url.find('+')] | ||||
url = url[url.find('+') + 1:] | ||||
handlers = [] | ||||
r105 | url_obj = url_parser(url) | |||
r0 | test_uri, authinfo = url_obj.authinfo() | |||
r114 | url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd | |||
r106 | url_obj.query = obfuscate_qs(url_obj.query) | |||
r0 | cleaned_uri = str(url_obj) | |||
r105 | log.info("Checking URL for remote cloning/import: %s", cleaned_uri) | |||
r0 | ||||
if authinfo: | ||||
# create a password manager | ||||
passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() | ||||
passmgr.add_password(*authinfo) | ||||
handlers.extend((httpbasicauthhandler(passmgr), | ||||
httpdigestauthhandler(passmgr))) | ||||
o = urllib2.build_opener(*handlers) | ||||
o.addheaders = [('Content-Type', 'application/mercurial-0.1'), | ||||
('Accept', 'application/mercurial-0.1')] | ||||
q = {"cmd": 'between'} | ||||
q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)}) | ||||
qs = '?%s' % urllib.urlencode(q) | ||||
cu = "%s%s" % (test_uri, qs) | ||||
req = urllib2.Request(cu, None, {}) | ||||
try: | ||||
r105 | log.debug("Trying to open URL %s", cleaned_uri) | |||
r0 | resp = o.open(req) | |||
if resp.code != 200: | ||||
r490 | raise exceptions.URLError()('Return Code is not 200') | |||
r0 | except Exception as e: | |||
r105 | log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True) | |||
r0 | # means it cannot be cloned | |||
r490 | raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e)) | |||
r0 | ||||
# now check if it's a proper hg repo, but don't do it for svn | ||||
try: | ||||
if _proto == 'svn': | ||||
pass | ||||
else: | ||||
# check for pure hg repos | ||||
r64 | log.debug( | |||
r105 | "Verifying if URL is a Mercurial repository: %s", | |||
cleaned_uri) | ||||
r457 | ui = make_ui_from_config(config) | |||
peer_checker = makepeer(ui, url) | ||||
peer_checker.lookup('tip') | ||||
r0 | except Exception as e: | |||
r105 | log.warning("URL is not a valid Mercurial repository: %s", | |||
cleaned_uri) | ||||
r490 | raise exceptions.URLError(e)( | |||
r0 | "url [%s] does not look like an hg repo org_exc: %s" | |||
% (cleaned_uri, e)) | ||||
r105 | log.info("URL is a valid Mercurial repository: %s", cleaned_uri) | |||
r0 | return True | |||
@reraise_safe_exceptions | ||||
def diff( | ||||
self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews, | ||||
context): | ||||
repo = self._factory.repo(wire) | ||||
if file_filter: | ||||
r119 | match_filter = match(file_filter[0], '', [file_filter[1]]) | |||
r0 | else: | |||
r119 | match_filter = file_filter | |||
r0 | opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context) | |||
try: | ||||
return "".join(patch.diff( | ||||
r119 | repo, node1=rev1, node2=rev2, match=match_filter, opts=opts)) | |||
r490 | except RepoLookupError as e: | |||
raise exceptions.LookupException(e)() | ||||
r0 | ||||
@reraise_safe_exceptions | ||||
r593 | def node_history(self, wire, revision, path, limit): | |||
r0 | repo = self._factory.repo(wire) | |||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
def history_iter(): | ||||
limit_rev = fctx.rev() | ||||
for obj in reversed(list(fctx.filelog())): | ||||
obj = fctx.filectx(obj) | ||||
r692 | ctx = obj.changectx() | |||
if ctx.hidden() or ctx.obsolete(): | ||||
continue | ||||
r0 | if limit_rev >= obj.rev(): | |||
yield obj | ||||
history = [] | ||||
for cnt, obj in enumerate(history_iter()): | ||||
if limit and cnt >= limit: | ||||
break | ||||
history.append(hex(obj.node())) | ||||
return [x for x in history] | ||||
@reraise_safe_exceptions | ||||
r593 | def node_history_untill(self, wire, revision, path, limit): | |||
r0 | repo = self._factory.repo(wire) | |||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
file_log = list(fctx.filelog()) | ||||
if limit: | ||||
# Limit to the last n items | ||||
file_log = file_log[-limit:] | ||||
return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)] | ||||
@reraise_safe_exceptions | ||||
def fctx_annotate(self, wire, revision, path): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
result = [] | ||||
r432 | for i, annotate_obj in enumerate(fctx.annotate(), 1): | |||
ln_no = i | ||||
sha = hex(annotate_obj.fctx.node()) | ||||
content = annotate_obj.text | ||||
r114 | result.append((ln_no, sha, content)) | |||
r0 | return result | |||
@reraise_safe_exceptions | ||||
def fctx_data(self, wire, revision, path): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
return fctx.data() | ||||
@reraise_safe_exceptions | ||||
def fctx_flags(self, wire, revision, path): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
return fctx.flags() | ||||
@reraise_safe_exceptions | ||||
def fctx_size(self, wire, revision, path): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | fctx = ctx.filectx(path) | |||
return fctx.size() | ||||
@reraise_safe_exceptions | ||||
def get_all_commit_ids(self, wire, name): | ||||
repo = self._factory.repo(wire) | ||||
r657 | repo = repo.filtered(name) | |||
revs = map(lambda x: hex(x[7]), repo.changelog.index) | ||||
return revs | ||||
r0 | ||||
@reraise_safe_exceptions | ||||
def get_config_value(self, wire, section, name, untrusted=False): | ||||
repo = self._factory.repo(wire) | ||||
return repo.ui.config(section, name, untrusted=untrusted) | ||||
@reraise_safe_exceptions | ||||
def get_config_bool(self, wire, section, name, untrusted=False): | ||||
repo = self._factory.repo(wire) | ||||
return repo.ui.configbool(section, name, untrusted=untrusted) | ||||
@reraise_safe_exceptions | ||||
def get_config_list(self, wire, section, name, untrusted=False): | ||||
repo = self._factory.repo(wire) | ||||
return repo.ui.configlist(section, name, untrusted=untrusted) | ||||
@reraise_safe_exceptions | ||||
def is_large_file(self, wire, path): | ||||
return largefiles.lfutil.isstandin(path) | ||||
@reraise_safe_exceptions | ||||
r182 | def in_largefiles_store(self, wire, sha): | |||
r0 | repo = self._factory.repo(wire) | |||
return largefiles.lfutil.instore(repo, sha) | ||||
@reraise_safe_exceptions | ||||
def in_user_cache(self, wire, sha): | ||||
repo = self._factory.repo(wire) | ||||
return largefiles.lfutil.inusercache(repo.ui, sha) | ||||
@reraise_safe_exceptions | ||||
def store_path(self, wire, sha): | ||||
repo = self._factory.repo(wire) | ||||
return largefiles.lfutil.storepath(repo, sha) | ||||
@reraise_safe_exceptions | ||||
def link(self, wire, sha, path): | ||||
repo = self._factory.repo(wire) | ||||
largefiles.lfutil.link( | ||||
largefiles.lfutil.usercachepath(repo.ui, sha), path) | ||||
@reraise_safe_exceptions | ||||
def localrepository(self, wire, create=False): | ||||
self._factory.repo(wire, create=create) | ||||
@reraise_safe_exceptions | ||||
def lookup(self, wire, revision, both): | ||||
r432 | ||||
r0 | repo = self._factory.repo(wire) | |||
r432 | ||||
if isinstance(revision, int): | ||||
# NOTE(marcink): | ||||
r657 | # since Mercurial doesn't support negative indexes properly | |||
r432 | # we need to shift accordingly by one to get proper index, e.g | |||
# repo[-1] => repo[-2] | ||||
# repo[0] => repo[-1] | ||||
if revision <= 0: | ||||
revision = revision + -1 | ||||
r0 | try: | |||
r660 | ctx = self._get_ctx(repo, revision) | |||
r657 | except (TypeError, RepoLookupError) as e: | |||
e._org_exc_tb = traceback.format_exc() | ||||
r490 | raise exceptions.LookupException(e)(revision) | |||
r0 | except LookupError as e: | |||
r657 | e._org_exc_tb = traceback.format_exc() | |||
r490 | raise exceptions.LookupException(e)(e.name) | |||
r0 | ||||
if not both: | ||||
return ctx.hex() | ||||
ctx = repo[ctx.hex()] | ||||
return ctx.hex(), ctx.rev() | ||||
@reraise_safe_exceptions | ||||
def pull(self, wire, url, commit_ids=None): | ||||
repo = self._factory.repo(wire) | ||||
r381 | # Disable any prompts for this repo | |||
repo.ui.setconfig('ui', 'interactive', 'off', '-y') | ||||
r0 | remote = peer(repo, {}, url) | |||
r381 | # Disable any prompts for this remote | |||
remote.ui.setconfig('ui', 'interactive', 'off', '-y') | ||||
r0 | if commit_ids: | |||
commit_ids = [bin(commit_id) for commit_id in commit_ids] | ||||
return exchange.pull( | ||||
repo, remote, heads=commit_ids, force=None).cgresult | ||||
@reraise_safe_exceptions | ||||
r351 | def sync_push(self, wire, url): | |||
r549 | if not self.check_url(url, wire['config']): | |||
return | ||||
r381 | ||||
r549 | repo = self._factory.repo(wire) | |||
# Disable any prompts for this repo | ||||
repo.ui.setconfig('ui', 'interactive', 'off', '-y') | ||||
r381 | ||||
r549 | bookmarks = dict(repo._bookmarks).keys() | |||
remote = peer(repo, {}, url) | ||||
# Disable any prompts for this remote | ||||
remote.ui.setconfig('ui', 'interactive', 'off', '-y') | ||||
r381 | ||||
r549 | return exchange.push( | |||
repo, remote, newbranch=True, bookmarks=bookmarks).cgresult | ||||
r351 | ||||
@reraise_safe_exceptions | ||||
r0 | def revision(self, wire, rev): | |||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, rev) | |||
r0 | return ctx.rev() | |||
@reraise_safe_exceptions | ||||
def rev_range(self, wire, filter): | ||||
repo = self._factory.repo(wire) | ||||
revisions = [rev for rev in revrange(repo, filter)] | ||||
return revisions | ||||
@reraise_safe_exceptions | ||||
def rev_range_hash(self, wire, node): | ||||
repo = self._factory.repo(wire) | ||||
def get_revs(repo, rev_opt): | ||||
if rev_opt: | ||||
revs = revrange(repo, rev_opt) | ||||
if len(revs) == 0: | ||||
return (nullrev, nullrev) | ||||
return max(revs), min(revs) | ||||
else: | ||||
return len(repo) - 1, 0 | ||||
stop, start = get_revs(repo, [node + ':']) | ||||
revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)] | ||||
return revs | ||||
@reraise_safe_exceptions | ||||
def revs_from_revspec(self, wire, rev_spec, *args, **kwargs): | ||||
other_path = kwargs.pop('other_path', None) | ||||
# case when we want to compare two independent repositories | ||||
if other_path and other_path != wire["path"]: | ||||
baseui = self._factory._create_config(wire["config"]) | ||||
r656 | repo = unionrepo.makeunionrepository(baseui, other_path, wire["path"]) | |||
r0 | else: | |||
repo = self._factory.repo(wire) | ||||
return list(repo.revs(rev_spec, *args)) | ||||
@reraise_safe_exceptions | ||||
def strip(self, wire, revision, update, backup): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | hgext_strip( | |||
repo.baseui, repo, ctx.node(), update=update, backup=backup) | ||||
@reraise_safe_exceptions | ||||
r179 | def verify(self, wire,): | |||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
baseui.setconfig('ui', 'quiet', 'false') | ||||
output = io.BytesIO() | ||||
def write(data, **unused_kwargs): | ||||
output.write(data) | ||||
baseui.write = write | ||||
repo.ui = baseui | ||||
verify.verify(repo) | ||||
return output.getvalue() | ||||
@reraise_safe_exceptions | ||||
r0 | def tag(self, wire, name, revision, message, local, user, | |||
tag_time, tag_timezone): | ||||
repo = self._factory.repo(wire) | ||||
r660 | ctx = self._get_ctx(repo, revision) | |||
r0 | node = ctx.node() | |||
date = (tag_time, tag_timezone) | ||||
try: | ||||
r274 | hg_tag.tag(repo, name, node, message, local, user, date) | |||
r123 | except Abort as e: | |||
r0 | log.exception("Tag operation aborted") | |||
r123 | # Exception can contain unicode which we convert | |||
r490 | raise exceptions.AbortException(e)(repr(e)) | |||
r0 | ||||
@reraise_safe_exceptions | ||||
def tags(self, wire): | ||||
repo = self._factory.repo(wire) | ||||
return repo.tags() | ||||
@reraise_safe_exceptions | ||||
def update(self, wire, node=None, clean=False): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
commands.update(baseui, repo, node=node, clean=clean) | ||||
@reraise_safe_exceptions | ||||
def identify(self, wire): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
output = io.BytesIO() | ||||
baseui.write = output.write | ||||
# This is required to get a full node id | ||||
baseui.debugflag = True | ||||
commands.identify(baseui, repo, id=True) | ||||
return output.getvalue() | ||||
@reraise_safe_exceptions | ||||
def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None, | ||||
hooks=True): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config'], hooks=hooks) | ||||
# Mercurial internally has a lot of logic that checks ONLY if | ||||
# option is defined, we just pass those if they are defined then | ||||
opts = {} | ||||
if bookmark: | ||||
opts['bookmark'] = bookmark | ||||
if branch: | ||||
opts['branch'] = branch | ||||
if revision: | ||||
opts['rev'] = revision | ||||
commands.pull(baseui, repo, source, **opts) | ||||
@reraise_safe_exceptions | ||||
def heads(self, wire, branch=None): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
output = io.BytesIO() | ||||
def write(data, **unused_kwargs): | ||||
output.write(data) | ||||
baseui.write = write | ||||
if branch: | ||||
args = [branch] | ||||
else: | ||||
args = [] | ||||
commands.heads(baseui, repo, template='{node} ', *args) | ||||
return output.getvalue() | ||||
@reraise_safe_exceptions | ||||
def ancestor(self, wire, revision1, revision2): | ||||
repo = self._factory.repo(wire) | ||||
r163 | changelog = repo.changelog | |||
lookup = repo.lookup | ||||
a = changelog.ancestor(lookup(revision1), lookup(revision2)) | ||||
return hex(a) | ||||
r0 | ||||
@reraise_safe_exceptions | ||||
def push(self, wire, revisions, dest_path, hooks=True, | ||||
push_branches=False): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config'], hooks=hooks) | ||||
commands.push(baseui, repo, dest=dest_path, rev=revisions, | ||||
new_branch=push_branches) | ||||
@reraise_safe_exceptions | ||||
def merge(self, wire, revision): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
repo.ui.setconfig('ui', 'merge', 'internal:dump') | ||||
Martin Bornhold
|
r99 | |||
# In case of sub repositories are used mercurial prompts the user in | ||||
# case of merge conflicts or different sub repository sources. By | ||||
# setting the interactive flag to `False` mercurial doesn't prompt the | ||||
# used but instead uses a default value. | ||||
repo.ui.setconfig('ui', 'interactive', False) | ||||
r669 | commands.merge(baseui, repo, rev=revision) | |||
Martin Bornhold
|
r99 | |||
r669 | @reraise_safe_exceptions | |||
def merge_state(self, wire): | ||||
repo = self._factory.repo(wire) | ||||
repo.ui.setconfig('ui', 'merge', 'internal:dump') | ||||
# In case of sub repositories are used mercurial prompts the user in | ||||
# case of merge conflicts or different sub repository sources. By | ||||
# setting the interactive flag to `False` mercurial doesn't prompt the | ||||
# used but instead uses a default value. | ||||
repo.ui.setconfig('ui', 'interactive', False) | ||||
ms = hg_merge.mergestate(repo) | ||||
return [x for x in ms.unresolved()] | ||||
r0 | ||||
@reraise_safe_exceptions | ||||
Mathieu Cantin
|
r270 | def commit(self, wire, message, username, close_branch=False): | ||
r0 | repo = self._factory.repo(wire) | |||
baseui = self._factory._create_config(wire['config']) | ||||
repo.ui.setconfig('ui', 'username', username) | ||||
Mathieu Cantin
|
r270 | commands.commit(baseui, repo, message=message, close_branch=close_branch) | ||
r0 | ||||
r669 | ||||
r0 | @reraise_safe_exceptions | |||
def rebase(self, wire, source=None, dest=None, abort=False): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
repo.ui.setconfig('ui', 'merge', 'internal:dump') | ||||
rebase.rebase( | ||||
baseui, repo, base=source, dest=dest, abort=abort, keep=not abort) | ||||
@reraise_safe_exceptions | ||||
def bookmark(self, wire, bookmark, revision=None): | ||||
repo = self._factory.repo(wire) | ||||
baseui = self._factory._create_config(wire['config']) | ||||
commands.bookmark(baseui, repo, bookmark, rev=revision, force=True) | ||||
r407 | ||||
@reraise_safe_exceptions | ||||
def install_hooks(self, wire, force=False): | ||||
# we don't need any special hooks for Mercurial | ||||
pass | ||||
r623 | ||||
@reraise_safe_exceptions | ||||
def get_hooks_info(self, wire): | ||||
return { | ||||
'pre_version': vcsserver.__version__, | ||||
'post_version': vcsserver.__version__, | ||||
} | ||||