diff --git a/vcsserver/git.py b/vcsserver/git.py --- a/vcsserver/git.py +++ b/vcsserver/git.py @@ -39,7 +39,7 @@ from dulwich.repo import Repo as Dulwich from dulwich.server import update_server_info from vcsserver import exceptions, settings, subprocessio -from vcsserver.utils import safe_str +from vcsserver.utils import safe_str, safe_int from vcsserver.base import RepoFactory, obfuscate_qs from vcsserver.hgcompat import ( hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler) @@ -128,6 +128,7 @@ class GitFactory(RepoFactory): class GitRemote(object): + EMPTY_COMMIT = '0' * 40 def __init__(self, factory): self._factory = factory @@ -190,15 +191,6 @@ class GitRemote(object): return True @reraise_safe_exceptions - def add_object(self, wire, content): - repo_init = self._factory.repo_libgit2(wire) - with repo_init as repo: - blob = objects.Blob() - blob.set_raw_string(content) - repo.object_store.add_object(blob) - return blob.id - - @reraise_safe_exceptions def assert_correct_path(self, wire): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) @@ -234,14 +226,14 @@ class GitRemote(object): def blob_raw_length(self, wire, sha): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _blob_raw_length(_context_uid, _repo_id, _sha): + def _blob_raw_length(_repo_id, _sha): repo_init = self._factory.repo_libgit2(wire) with repo_init as repo: blob = repo[sha] return blob.size - return _blob_raw_length(context_uid, repo_id, sha) + return _blob_raw_length(repo_id, sha) def _parse_lfs_pointer(self, raw_content): @@ -261,20 +253,20 @@ class GitRemote(object): return {} @reraise_safe_exceptions - def is_large_file(self, wire, sha): + def is_large_file(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _is_large_file(_context_uid, _repo_id, _sha): + def _is_large_file(_repo_id, _sha): repo_init = self._factory.repo_libgit2(wire) with repo_init as repo: - blob = repo[sha] + blob = repo[commit_id] if blob.is_binary: return {} return self._parse_lfs_pointer(blob.data) - return _is_large_file(context_uid, repo_id, sha) + return _is_large_file(repo_id, commit_id) @reraise_safe_exceptions def in_largefiles_store(self, wire, oid): @@ -310,7 +302,7 @@ class GitRemote(object): def bulk_request(self, wire, rev, pre_load): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _bulk_request(_context_uid, _repo_id, _rev, _pre_load): + def _bulk_request(_repo_id, _rev, _pre_load): result = {} for attr in pre_load: try: @@ -322,7 +314,7 @@ class GitRemote(object): "Unknown bulk attribute: %s" % attr) return result - return _bulk_request(context_uid, repo_id, rev, sorted(pre_load)) + return _bulk_request(repo_id, rev, sorted(pre_load)) def _build_opener(self, url): handlers = [] @@ -437,6 +429,15 @@ class GitRemote(object): return _commit_branches(context_uid, repo_id, commit_id) + @reraise_safe_exceptions + def add_object(self, wire, content): + repo_init = self._factory.repo_libgit2(wire) + with repo_init as repo: + blob = objects.Blob() + blob.set_raw_string(content) + repo.object_store.add_object(blob) + return blob.id + # TODO: this is quite complex, check if that can be simplified @reraise_safe_exceptions def commit(self, wire, commit_data, branch, commit_tree, updated, removed): @@ -783,40 +784,70 @@ class GitRemote(object): return _revision(context_uid, repo_id, rev) @reraise_safe_exceptions - def date(self, wire, rev): - repo_init = self._factory.repo_libgit2(wire) - with repo_init as repo: - commit = repo[rev] - # TODO(marcink): check dulwich difference of offset vs timezone - return [commit.commit_time, commit.commit_time_offset] + def date(self, wire, commit_id): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _date(_repo_id, _commit_id): + repo_init = self._factory.repo_libgit2(wire) + with repo_init as repo: + commit = repo[commit_id] + # TODO(marcink): check dulwich difference of offset vs timezone + return [commit.commit_time, commit.commit_time_offset] + return _date(repo_id, commit_id) + + @reraise_safe_exceptions + def author(self, wire, commit_id): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _author(_repo_id, _commit_id): + repo_init = self._factory.repo_libgit2(wire) + with repo_init as repo: + commit = repo[commit_id] + if commit.author.email: + return u"{} <{}>".format(commit.author.name, commit.author.email) + + return u"{}".format(commit.author.raw_name) + return _author(repo_id, commit_id) @reraise_safe_exceptions - def author(self, wire, rev): - repo_init = self._factory.repo_libgit2(wire) - with repo_init as repo: - commit = repo[rev] - if commit.author.email: - return u"{} <{}>".format(commit.author.name, commit.author.email) - - return u"{}".format(commit.author.raw_name) + def message(self, wire, commit_id): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _message(_repo_id, _commit_id): + repo_init = self._factory.repo_libgit2(wire) + with repo_init as repo: + commit = repo[commit_id] + return commit.message + return _message(repo_id, commit_id) @reraise_safe_exceptions - def message(self, wire, rev): - repo_init = self._factory.repo_libgit2(wire) - with repo_init as repo: - commit = repo[rev] - return commit.message + def parents(self, wire, commit_id): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _parents(_repo_id, _commit_id): + repo_init = self._factory.repo_libgit2(wire) + with repo_init as repo: + commit = repo[commit_id] + return [x.hex for x in commit.parent_ids] + return _parents(repo_id, commit_id) @reraise_safe_exceptions - def parents(self, wire, rev): + def children(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _parents(_context_uid, _repo_id, _rev): - repo_init = self._factory.repo_libgit2(wire) - with repo_init as repo: - commit = repo[rev] - return [x.hex for x in commit.parent_ids] - return _parents(context_uid, repo_id, rev) + def _children(_repo_id, _commit_id): + output, __ = self.run_git_command( + wire, ['rev-list', '--all', '--children']) + + child_ids = [] + pat = re.compile(r'^%s' % commit_id) + for l in output.splitlines(): + if pat.match(l): + found_ids = l.split(' ')[1:] + child_ids.extend(found_ids) + + return child_ids + return _children(repo_id, commit_id) @reraise_safe_exceptions def set_refs(self, wire, key, value): @@ -878,10 +909,9 @@ class GitRemote(object): @reraise_safe_exceptions def tree_items(self, wire, tree_id): - cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _tree_items(_context_uid, _repo_id, _tree_id): + def _tree_items(_repo_id, _tree_id): repo_init = self._factory.repo_libgit2(wire) with repo_init as repo: @@ -902,7 +932,75 @@ class GitRemote(object): result.append((item.name, item_mode, item_sha, item_type)) return result - return _tree_items(context_uid, repo_id, tree_id) + return _tree_items(repo_id, tree_id) + + @reraise_safe_exceptions + def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_ignorews, context): + + flags = [ + '-U%s' % context, '--full-index', '--binary', '-p', + '-M', '--abbrev=40'] + + if opt_ignorews: + flags.append('-w') + + if commit_id_1 == self.EMPTY_COMMIT: + cmd = ['show'] + flags + [commit_id_2] + else: + cmd = ['diff'] + flags + [commit_id_1, commit_id_2] + + if file_filter: + cmd.extend(['--', file_filter]) + + diff, __ = self.run_git_command(wire, cmd) + # If we used 'show' command, strip first few lines (until actual diff + # starts) + if commit_id_1 == self.EMPTY_COMMIT: + lines = diff.splitlines() + x = 0 + for line in lines: + if line.startswith('diff'): + break + x += 1 + # Append new line just like 'diff' command do + diff = '\n'.join(lines[x:]) + '\n' + return diff + + @reraise_safe_exceptions + def node_history(self, wire, commit_id, path, limit): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _node_history(_context_uid, _repo_id, _commit_id, _path, _limit): + # optimize for n==1, rev-list is much faster for that use-case + if limit == 1: + cmd = ['rev-list', '-1', commit_id, '--', path] + else: + cmd = ['log'] + if limit: + cmd.extend(['-n', str(safe_int(limit, 0))]) + cmd.extend(['--pretty=format: %H', '-s', commit_id, '--', path]) + + output, __ = self.run_git_command(wire, cmd) + commit_ids = re.findall(r'[0-9a-fA-F]{40}', output) + + return [x for x in commit_ids] + return _node_history(context_uid, repo_id, commit_id, path, limit) + + @reraise_safe_exceptions + def node_annotate(self, wire, commit_id, path): + + cmd = ['blame', '-l', '--root', '-r', commit_id, '--', path] + # -l ==> outputs long shas (and we need all 40 characters) + # --root ==> doesn't put '^' character for boundaries + # -r commit_id ==> blames for the given commit + output, __ = self.run_git_command(wire, cmd) + + result = [] + for i, blame_line in enumerate(output.split('\n')[:-1]): + line_no = i + 1 + commit_id, line = re.split(r' ', blame_line, 1) + result.append((line_no, commit_id, line)) + return result @reraise_safe_exceptions def update_server_info(self, wire): diff --git a/vcsserver/hg.py b/vcsserver/hg.py --- a/vcsserver/hg.py +++ b/vcsserver/hg.py @@ -241,99 +241,41 @@ class HgRemote(object): return _branches(context_uid, repo_id, normal, closed) @reraise_safe_exceptions - def bulk_request(self, wire, rev, pre_load): + def bulk_request(self, wire, commit_id, pre_load): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _bulk_request(_context_uid, _repo_id, _rev, _pre_load): + def _bulk_request(_repo_id, _commit_id, _pre_load): result = {} for attr in pre_load: try: method = self._bulk_methods[attr] - result[attr] = method(wire, rev) + result[attr] = method(wire, commit_id) except KeyError as e: raise exceptions.VcsException(e)( 'Unknown bulk attribute: "%s"' % attr) return result - return _bulk_request(context_uid, repo_id, rev, sorted(pre_load)) - - @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) + return _bulk_request(repo_id, commit_id, sorted(pre_load)) @reraise_safe_exceptions - def commitctx(self, wire, message, parents, commit_time, commit_timezone, - user, files, extra, removed, updated): - - 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): - """ - 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, - changectx=ctx, - path=node['path'], - data=node['content'], - islink=False, - isexec=bool(node['mode'] & stat.S_IXUSR), - copysource=False) - - raise exceptions.AbortException()( - "Given path haven't been marked as added, " - "changed or removed (%s)" % path) - - with repo.ui.configoverride({('phases', 'new-commit'): new_commit}): - - commit_ctx = memctx( - repo=repo, - parents=parents, - text=message, - files=files, - filectxfn=_filectxfn, - user=user, - date=(commit_time, commit_timezone), - extra=extra) - - n = repo.commitctx(commit_ctx) - new_id = hex(n) - - return new_id + def ctx_branch(self, wire, commit_id): + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _ctx_branch(_repo_id, _commit_id): + repo = self._factory.repo(wire) + ctx = self._get_ctx(repo, commit_id) + return ctx.branch() + return _ctx_branch(repo_id, commit_id) @reraise_safe_exceptions - def ctx_branch(self, wire, revision): - + def ctx_date(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_branch(_context_uid, _repo_id, _revision): + def _ctx_date(_repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) - return ctx.branch() - return _ctx_branch(context_uid, repo_id, revision) - - @reraise_safe_exceptions - def ctx_date(self, wire, revision): - repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) - return ctx.date() + ctx = self._get_ctx(repo, commit_id) + return ctx.date() + return _ctx_date(repo_id, commit_id) @reraise_safe_exceptions def ctx_description(self, wire, revision): @@ -342,16 +284,15 @@ class HgRemote(object): return ctx.description() @reraise_safe_exceptions - def ctx_files(self, wire, revision): - + def ctx_files(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_files(_context_uid, _repo_id, _revision): + def _ctx_files(_repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) return ctx.files() - return _ctx_files(context_uid, repo_id, revision) + return _ctx_files(repo_id, commit_id) @reraise_safe_exceptions def ctx_list(self, path, revision): @@ -360,59 +301,59 @@ class HgRemote(object): return list(ctx) @reraise_safe_exceptions - def ctx_parents(self, wire, revision): + def ctx_parents(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_parents(_context_uid, _repo_id, _revision): + def _ctx_parents(_repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) return [parent.rev() for parent in ctx.parents() if not (parent.hidden() or parent.obsolete())] - return _ctx_parents(context_uid, repo_id, revision) + return _ctx_parents(repo_id, commit_id) @reraise_safe_exceptions - def ctx_children(self, wire, revision): + def ctx_children(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_children(_context_uid, _repo_id, _revision): + def _ctx_children(_repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) return [child.rev() for child in ctx.children() if not (child.hidden() or child.obsolete())] - return _ctx_children(context_uid, repo_id, revision) + return _ctx_children(repo_id, commit_id) @reraise_safe_exceptions - def ctx_phase(self, wire, revision): + def ctx_phase(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_phase(_context_uid, _repo_path, _revision): + def _ctx_phase(_context_uid, _repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) # public=0, draft=1, secret=3 return ctx.phase() - return _ctx_phase(context_uid, repo_id, revision) + return _ctx_phase(context_uid, repo_id, commit_id) @reraise_safe_exceptions - def ctx_obsolete(self, wire, revision): + def ctx_obsolete(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_obsolete(_context_uid, _repo_path, _revision): + def _ctx_obsolete(_context_uid, _repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) return ctx.obsolete() - return _ctx_obsolete(context_uid, repo_id, revision) + return _ctx_obsolete(context_uid, repo_id, commit_id) @reraise_safe_exceptions - def ctx_hidden(self, wire, revision): + def ctx_hidden(self, wire, commit_id): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _ctx_hidden(_context_uid, _repo_path, _revision): + def _ctx_hidden(_context_uid, _repo_id, _commit_id): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) return ctx.hidden() - return _ctx_hidden(context_uid, repo_id, revision) + return _ctx_hidden(context_uid, repo_id, commit_id) @reraise_safe_exceptions def ctx_substate(self, wire, revision): @@ -502,7 +443,7 @@ class HgRemote(object): return True @reraise_safe_exceptions - def diff(self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews, context): + def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_git, opt_ignorews, context): repo = self._factory.repo(wire) if file_filter: @@ -513,16 +454,15 @@ class HgRemote(object): try: return "".join(patch.diff( - repo, node1=rev1, node2=rev2, match=match_filter, opts=opts)) + repo, node1=commit_id_1, node2=commit_id_2, match=match_filter, opts=opts)) except RepoLookupError as e: raise exceptions.LookupException(e)() @reraise_safe_exceptions def node_history(self, wire, revision, path, limit): - cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _node_history(_context_uid, _repo_path, _revision, _path, _limit): + def _node_history(_context_uid, _repo_id, _revision, _path, _limit): repo = self._factory.repo(wire) ctx = self._get_ctx(repo, revision) @@ -550,10 +490,9 @@ class HgRemote(object): @reraise_safe_exceptions def node_history_untill(self, wire, revision, path, limit): - cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _meth(_context_uid, _repo_path): + def _node_history_until(_context_uid, _repo_id): repo = self._factory.repo(wire) ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) @@ -564,7 +503,7 @@ class HgRemote(object): file_log = file_log[-limit:] return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)] - return _meth(context_uid, repo_id, revision, path, limit) + return _node_history_until(context_uid, repo_id, revision, path, limit) @reraise_safe_exceptions def fctx_annotate(self, wire, revision, path): @@ -588,28 +527,27 @@ class HgRemote(object): return fctx.data() @reraise_safe_exceptions - def fctx_flags(self, wire, revision, path): + def fctx_flags(self, wire, commit_id, path): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _fctx_flags(_context_uid, _repo_path, _revision, _path): + def _fctx_flags(_repo_id, _commit_id, _path): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) fctx = ctx.filectx(path) return fctx.flags() - return _fctx_flags(context_uid, repo_id, revision, path) + return _fctx_flags(repo_id, commit_id, path) @reraise_safe_exceptions - def fctx_size(self, wire, revision, path): - + def fctx_size(self, wire, commit_id, path): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _fctx_size(_context_uid, _repo_path, _revision, _path): + def _fctx_size(_repo_id, _revision, _path): repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) + ctx = self._get_ctx(repo, commit_id) fctx = ctx.filectx(path) return fctx.size() - return _fctx_size(context_uid, repo_id, revision, path) + return _fctx_size(repo_id, commit_id, path) @reraise_safe_exceptions def get_all_commit_ids(self, wire, name): @@ -628,16 +566,6 @@ class HgRemote(object): 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): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) @@ -705,22 +633,6 @@ class HgRemote(object): return _lookup(context_uid, repo_id, revision, both) @reraise_safe_exceptions - def pull(self, wire, url, commit_ids=None): - repo = self._factory.repo(wire) - # Disable any prompts for this repo - repo.ui.setconfig('ui', 'interactive', 'off', '-y') - - remote = peer(repo, {}, url) - # Disable any prompts for this remote - remote.ui.setconfig('ui', 'interactive', 'off', '-y') - - 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 def sync_push(self, wire, url): if not self.check_url(url, wire['config']): return @@ -785,13 +697,6 @@ class HgRemote(object): return list(repo.revs(rev_spec, *args)) @reraise_safe_exceptions - def strip(self, wire, revision, update, backup): - repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) - hgext_strip( - repo.baseui, repo, ctx.node(), update=update, backup=backup) - - @reraise_safe_exceptions def verify(self, wire,): repo = self._factory.repo(wire) baseui = self._factory._create_config(wire['config']) @@ -807,20 +712,6 @@ class HgRemote(object): return output.getvalue() @reraise_safe_exceptions - def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone): - repo = self._factory.repo(wire) - ctx = self._get_ctx(repo, revision) - node = ctx.node() - - date = (tag_time, tag_timezone) - try: - hg_tag.tag(repo, name, node, message, local, user, date) - except Abort as e: - log.exception("Tag operation aborted") - # Exception can contain unicode which we convert - raise exceptions.AbortException(e)(repr(e)) - - @reraise_safe_exceptions def tags(self, wire): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) @@ -849,23 +740,6 @@ class HgRemote(object): 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']) @@ -892,6 +766,99 @@ class HgRemote(object): return hex(a) @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): + + 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): + """ + 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, + changectx=ctx, + path=node['path'], + data=node['content'], + islink=False, + isexec=bool(node['mode'] & stat.S_IXUSR), + copysource=False) + + raise exceptions.AbortException()( + "Given path haven't been marked as added, " + "changed or removed (%s)" % path) + + with repo.ui.configoverride({('phases', 'new-commit'): new_commit}): + + commit_ctx = memctx( + repo=repo, + parents=parents, + text=message, + files=files, + filectxfn=_filectxfn, + user=user, + date=(commit_time, commit_timezone), + extra=extra) + + n = repo.commitctx(commit_ctx) + new_id = hex(n) + + return new_id + + @reraise_safe_exceptions + def pull(self, wire, url, commit_ids=None): + repo = self._factory.repo(wire) + # Disable any prompts for this repo + repo.ui.setconfig('ui', 'interactive', 'off', '-y') + + remote = peer(repo, {}, url) + # Disable any prompts for this remote + remote.ui.setconfig('ui', 'interactive', 'off', '-y') + + 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 + 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 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) @@ -899,6 +866,13 @@ class HgRemote(object): new_branch=push_branches) @reraise_safe_exceptions + def strip(self, wire, revision, update, backup): + repo = self._factory.repo(wire) + ctx = self._get_ctx(repo, revision) + hgext_strip( + repo.baseui, repo, ctx.node(), update=update, backup=backup) + + @reraise_safe_exceptions def merge(self, wire, revision): repo = self._factory.repo(wire) baseui = self._factory._create_config(wire['config']) @@ -940,6 +914,20 @@ class HgRemote(object): baseui, repo, base=source, dest=dest, abort=abort, keep=not abort) @reraise_safe_exceptions + def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone): + repo = self._factory.repo(wire) + ctx = self._get_ctx(repo, revision) + node = ctx.node() + + date = (tag_time, tag_timezone) + try: + hg_tag.tag(repo, name, node, message, local, user, date) + except Abort as e: + log.exception("Tag operation aborted") + # Exception can contain unicode which we convert + raise exceptions.AbortException(e)(repr(e)) + + @reraise_safe_exceptions def bookmark(self, wire, bookmark, revision=None): repo = self._factory.repo(wire) baseui = self._factory._create_config(wire['config']) diff --git a/vcsserver/svn.py b/vcsserver/svn.py --- a/vcsserver/svn.py +++ b/vcsserver/svn.py @@ -215,11 +215,11 @@ class SvnRemote(object): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _revision_properties(_context_uid, _repo_id, _revision): + def _revision_properties(_repo_id, _revision): repo = self._factory.repo(wire) fs_ptr = svn.repos.fs(repo) return svn.fs.revision_proplist(fs_ptr, revision) - return _revision_properties(context_uid, repo_id, revision) + return _revision_properties(repo_id, revision) def revision_changes(self, wire, revision): @@ -288,10 +288,14 @@ class SvnRemote(object): return _assert_correct_path(context_uid, repo_id, path, revision, limit) def node_properties(self, wire, path, revision): - repo = self._factory.repo(wire) - fsobj = svn.repos.fs(repo) - rev_root = svn.fs.revision_root(fsobj, revision) - return svn.fs.node_proplist(rev_root, path) + cache_on, context_uid, repo_id = self._cache_on(wire) + @self.region.conditional_cache_on_arguments(condition=cache_on) + def _node_properties(_repo_id, _path, _revision): + repo = self._factory.repo(wire) + fsobj = svn.repos.fs(repo) + rev_root = svn.fs.revision_root(fsobj, revision) + return svn.fs.node_proplist(rev_root, path) + return _node_properties(repo_id, path, revision) def file_annotate(self, wire, path, revision): abs_path = 'file://' + urllib.pathname2url( @@ -324,7 +328,7 @@ class SvnRemote(object): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _get_node_type(_context_uid, _repo_id, _path, _revision): + def _get_node_type(_repo_id, _path, _revision): repo = self._factory.repo(wire) fs_ptr = svn.repos.fs(repo) if _revision is None: @@ -332,13 +336,13 @@ class SvnRemote(object): root = svn.fs.revision_root(fs_ptr, _revision) node = svn.fs.check_path(root, path) return NODE_TYPE_MAPPING.get(node, None) - return _get_node_type(context_uid, repo_id, path, revision) + return _get_node_type(repo_id, path, revision) def get_nodes(self, wire, path, revision=None): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _get_nodes(_context_uid, _repo_id, _path, _revision): + def _get_nodes(_repo_id, _path, _revision): repo = self._factory.repo(wire) fsobj = svn.repos.fs(repo) if _revision is None: @@ -350,7 +354,7 @@ class SvnRemote(object): result.append( (entry_path, NODE_TYPE_MAPPING.get(entry_info.kind, None))) return result - return _get_nodes(context_uid, repo_id, path, revision) + return _get_nodes(repo_id, path, revision) def get_file_content(self, wire, path, rev=None): repo = self._factory.repo(wire) @@ -365,7 +369,7 @@ class SvnRemote(object): cache_on, context_uid, repo_id = self._cache_on(wire) @self.region.conditional_cache_on_arguments(condition=cache_on) - def _get_file_size(_context_uid, _repo_id, _path, _revision): + def _get_file_size(_repo_id, _path, _revision): repo = self._factory.repo(wire) fsobj = svn.repos.fs(repo) if _revision is None: @@ -373,7 +377,7 @@ class SvnRemote(object): root = svn.fs.revision_root(fsobj, _revision) size = svn.fs.file_length(root, path) return size - return _get_file_size(context_uid, repo_id, path, revision) + return _get_file_size(repo_id, path, revision) def create_repository(self, wire, compatible_version=None): log.info('Creating Subversion repository in path "%s"', wire['path']) @@ -691,7 +695,6 @@ class SvnDiffer(object): return content.splitlines(True) - class DiffChangeEditor(svn.delta.Editor): """ Records changes between two given revisions diff --git a/vcsserver/tests/test_hg.py b/vcsserver/tests/test_hg.py --- a/vcsserver/tests/test_hg.py +++ b/vcsserver/tests/test_hg.py @@ -36,7 +36,7 @@ class TestDiff(object): 'deadbeef', 'index', 'message') with pytest.raises(Exception) as exc_info: hg_remote.diff( - wire={}, rev1='deadbeef', rev2='deadbee1', + wire={}, commit_id_1='deadbeef', commit_id_2='deadbee1', file_filter=None, opt_git=True, opt_ignorews=True, context=3) assert type(exc_info.value) == Exception