diff --git a/kallithea/controllers/compare.py b/kallithea/controllers/compare.py --- a/kallithea/controllers/compare.py +++ b/kallithea/controllers/compare.py @@ -28,9 +28,7 @@ Original author and date, and relevant c import logging -import re -import mercurial.unionrepo from tg import request from tg import tmpl_context as c from tg.i18n import ugettext as _ @@ -42,7 +40,6 @@ from kallithea.lib import helpers as h from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired from kallithea.lib.base import BaseRepoController, render from kallithea.lib.graphmod import graph_data -from kallithea.lib.utils2 import ascii_bytes, ascii_str, safe_bytes from kallithea.model.db import Repository @@ -74,104 +71,6 @@ class CompareController(BaseRepoControll h.flash(msg, category='error') raise HTTPFound(location=url('compare_home', repo_name=c.a_repo.repo_name)) - @staticmethod - def _get_changesets(alias, org_repo, org_rev, other_repo, other_rev): - """ - Returns lists of changesets that can be merged from org_repo@org_rev - to other_repo@other_rev - ... and the other way - ... and the ancestors that would be used for merge - - :param org_repo: repo object, that is most likely the original repo we forked from - :param org_rev: the revision we want our compare to be made - :param other_repo: repo object, most likely the fork of org_repo. It has - all changesets that we need to obtain - :param other_rev: revision we want out compare to be made on other_repo - """ - ancestors = None - if org_rev == other_rev: - org_changesets = [] - other_changesets = [] - - elif alias == 'hg': - # case two independent repos - if org_repo != other_repo: - hgrepo = mercurial.unionrepo.makeunionrepository(other_repo.baseui, - safe_bytes(other_repo.path), - safe_bytes(org_repo.path)) - # all ancestors of other_rev will be in other_repo and - # rev numbers from hgrepo can be used in other_repo - org_rev ancestors cannot - - # no remote compare do it on the same repository - else: - hgrepo = other_repo._repo - - ancestors = [ascii_str(hgrepo[ancestor].hex()) for ancestor in - hgrepo.revs(b"id(%s) & ::id(%s)", ascii_bytes(other_rev), ascii_bytes(org_rev))] - if ancestors: - log.debug("shortcut found: %s is already an ancestor of %s", other_rev, org_rev) - else: - log.debug("no shortcut found: %s is not an ancestor of %s", other_rev, org_rev) - ancestors = [ascii_str(hgrepo[ancestor].hex()) for ancestor in - hgrepo.revs(b"heads(::id(%s) & ::id(%s))", ascii_bytes(org_rev), ascii_bytes(other_rev))] # FIXME: expensive! - - other_changesets = [ - other_repo.get_changeset(rev) - for rev in hgrepo.revs( - b"ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)", - ascii_bytes(other_rev), ascii_bytes(org_rev), ascii_bytes(org_rev)) - ] - org_changesets = [ - org_repo.get_changeset(ascii_str(hgrepo[rev].hex())) - for rev in hgrepo.revs( - b"ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)", - ascii_bytes(org_rev), ascii_bytes(other_rev), ascii_bytes(other_rev)) - ] - - elif alias == 'git': - if org_repo != other_repo: - from dulwich.client import SubprocessGitClient - from dulwich.repo import Repo - - gitrepo = Repo(org_repo.path) - SubprocessGitClient(thin_packs=False).fetch(other_repo.path, gitrepo) - - gitrepo_remote = Repo(other_repo.path) - SubprocessGitClient(thin_packs=False).fetch(org_repo.path, gitrepo_remote) - - revs = [ - ascii_str(x.commit.id) - for x in gitrepo_remote.get_walker(include=[ascii_bytes(other_rev)], - exclude=[ascii_bytes(org_rev)]) - ] - other_changesets = [other_repo.get_changeset(rev) for rev in reversed(revs)] - if other_changesets: - ancestors = [other_changesets[0].parents[0].raw_id] - else: - # no changesets from other repo, ancestor is the other_rev - ancestors = [other_rev] - - gitrepo.close() - gitrepo_remote.close() - - else: - so = org_repo.run_git_command( - ['log', '--reverse', '--pretty=format:%H', - '-s', '%s..%s' % (org_rev, other_rev)] - ) - other_changesets = [org_repo.get_changeset(cs) - for cs in re.findall(r'[0-9a-fA-F]{40}', so)] - so = org_repo.run_git_command( - ['merge-base', org_rev, other_rev] - ) - ancestors = [re.findall(r'[0-9a-fA-F]{40}', so)[0]] - org_changesets = [] - - else: - raise Exception('Bad alias only git and hg is allowed') - - return other_changesets, org_changesets, ancestors - @LoginRequired(allow_default_user=True) @HasRepoPermissionLevelDecorator('read') def index(self, repo_name): @@ -220,9 +119,8 @@ class CompareController(BaseRepoControll c.cs_ref_name = other_ref_name c.cs_ref_type = other_ref_type - c.cs_ranges, c.cs_ranges_org, c.ancestors = self._get_changesets( - c.a_repo.scm_instance.alias, c.a_repo.scm_instance, c.a_rev, - c.cs_repo.scm_instance, c.cs_rev) + c.cs_ranges, c.cs_ranges_org, c.ancestors = c.a_repo.scm_instance.get_diff_changesets( + c.a_rev, c.cs_repo.scm_instance, c.cs_rev) raw_ids = [x.raw_id for x in c.cs_ranges] c.cs_comments = c.cs_repo.get_comments(raw_ids) c.cs_statuses = c.cs_repo.statuses(raw_ids) diff --git a/kallithea/lib/vcs/backends/base.py b/kallithea/lib/vcs/backends/base.py --- a/kallithea/lib/vcs/backends/base.py +++ b/kallithea/lib/vcs/backends/base.py @@ -170,6 +170,20 @@ class BaseRepository(object): """ raise NotImplementedError + def get_diff_changesets(self, org_rev, other_repo, other_rev): + """ + Returns lists of changesets that can be merged from this repo @org_rev + to other_repo @other_rev + ... and the other way + ... and the ancestors that would be used for merge + + :param org_rev: the revision we want our compare to be made + :param other_repo: repo object, most likely the fork of org_repo. It has + all changesets that we need to obtain + :param other_rev: revision we want out compare to be made on other_repo + """ + raise NotImplementedError + def __getitem__(self, key): if isinstance(key, slice): return (self.get_changeset(rev) for rev in self.revisions[key]) diff --git a/kallithea/lib/vcs/backends/git/repository.py b/kallithea/lib/vcs/backends/git/repository.py --- a/kallithea/lib/vcs/backends/git/repository.py +++ b/kallithea/lib/vcs/backends/git/repository.py @@ -20,6 +20,7 @@ import urllib.request from collections import OrderedDict import mercurial.util # import url as hg_url +from dulwich.client import SubprocessGitClient from dulwich.config import ConfigFile from dulwich.objects import Tag from dulwich.repo import NotGitRepository, Repo @@ -30,7 +31,7 @@ from kallithea.lib.vcs.backends.base imp from kallithea.lib.vcs.conf import settings from kallithea.lib.vcs.exceptions import (BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, TagAlreadyExistError, TagDoesNotExistError) -from kallithea.lib.vcs.utils import ascii_str, date_fromtimestamp, makedate, safe_bytes, safe_str +from kallithea.lib.vcs.utils import ascii_bytes, ascii_str, date_fromtimestamp, makedate, safe_bytes, safe_str from kallithea.lib.vcs.utils.helpers import get_urllib_request_handlers from kallithea.lib.vcs.utils.lazy import LazyProperty from kallithea.lib.vcs.utils.paths import abspath, get_user_home @@ -544,6 +545,58 @@ class GitRepository(BaseRepository): return CollectionGenerator(self, revs) + def get_diff_changesets(self, org_rev, other_repo, other_rev): + """ + Returns lists of changesets that can be merged from this repo @org_rev + to other_repo @other_rev + ... and the other way + ... and the ancestors that would be used for merge + + :param org_rev: the revision we want our compare to be made + :param other_repo: repo object, most likely the fork of org_repo. It has + all changesets that we need to obtain + :param other_rev: revision we want out compare to be made on other_repo + """ + org_changesets = [] + ancestors = None + if org_rev == other_rev: + other_changesets = [] + elif self != other_repo: + gitrepo = Repo(self.path) + SubprocessGitClient(thin_packs=False).fetch(other_repo.path, gitrepo) + + gitrepo_remote = Repo(other_repo.path) + SubprocessGitClient(thin_packs=False).fetch(self.path, gitrepo_remote) + + revs = [ + ascii_str(x.commit.id) + for x in gitrepo_remote.get_walker(include=[ascii_bytes(other_rev)], + exclude=[ascii_bytes(org_rev)]) + ] + other_changesets = [other_repo.get_changeset(rev) for rev in reversed(revs)] + if other_changesets: + ancestors = [other_changesets[0].parents[0].raw_id] + else: + # no changesets from other repo, ancestor is the other_rev + ancestors = [other_rev] + + gitrepo.close() + gitrepo_remote.close() + + else: + so = self.run_git_command( + ['log', '--reverse', '--pretty=format:%H', + '-s', '%s..%s' % (org_rev, other_rev)] + ) + other_changesets = [self.get_changeset(cs) + for cs in re.findall(r'[0-9a-fA-F]{40}', so)] + so = self.run_git_command( + ['merge-base', org_rev, other_rev] + ) + ancestors = [re.findall(r'[0-9a-fA-F]{40}', so)[0]] + + return other_changesets, org_changesets, ancestors + def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False, context=3): """ diff --git a/kallithea/lib/vcs/backends/hg/repository.py b/kallithea/lib/vcs/backends/hg/repository.py --- a/kallithea/lib/vcs/backends/hg/repository.py +++ b/kallithea/lib/vcs/backends/hg/repository.py @@ -33,12 +33,13 @@ import mercurial.scmutil import mercurial.sshpeer import mercurial.tags import mercurial.ui +import mercurial.unionrepo import mercurial.util from kallithea.lib.vcs.backends.base import BaseRepository, CollectionGenerator from kallithea.lib.vcs.exceptions import (BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, TagAlreadyExistError, TagDoesNotExistError, VCSError) -from kallithea.lib.vcs.utils import ascii_str, author_email, author_name, date_fromtimestamp, makedate, safe_bytes, safe_str +from kallithea.lib.vcs.utils import ascii_bytes, ascii_str, author_email, author_name, date_fromtimestamp, makedate, safe_bytes, safe_str from kallithea.lib.vcs.utils.helpers import get_urllib_request_handlers from kallithea.lib.vcs.utils.lazy import LazyProperty from kallithea.lib.vcs.utils.paths import abspath @@ -545,6 +546,60 @@ class MercurialRepository(BaseRepository return CollectionGenerator(self, revs) + def get_diff_changesets(self, org_rev, other_repo, other_rev): + """ + Returns lists of changesets that can be merged from this repo @org_rev + to other_repo @other_rev + ... and the other way + ... and the ancestors that would be used for merge + + :param org_rev: the revision we want our compare to be made + :param other_repo: repo object, most likely the fork of org_repo. It has + all changesets that we need to obtain + :param other_rev: revision we want out compare to be made on other_repo + """ + ancestors = None + if org_rev == other_rev: + org_changesets = [] + other_changesets = [] + + else: + # case two independent repos + if self != other_repo: + hgrepo = mercurial.unionrepo.makeunionrepository(other_repo.baseui, + safe_bytes(other_repo.path), + safe_bytes(self.path)) + # all ancestors of other_rev will be in other_repo and + # rev numbers from hgrepo can be used in other_repo - org_rev ancestors cannot + + # no remote compare do it on the same repository + else: + hgrepo = other_repo._repo + + ancestors = [ascii_str(hgrepo[ancestor].hex()) for ancestor in + hgrepo.revs(b"id(%s) & ::id(%s)", ascii_bytes(other_rev), ascii_bytes(org_rev))] + if ancestors: + log.debug("shortcut found: %s is already an ancestor of %s", other_rev, org_rev) + else: + log.debug("no shortcut found: %s is not an ancestor of %s", other_rev, org_rev) + ancestors = [ascii_str(hgrepo[ancestor].hex()) for ancestor in + hgrepo.revs(b"heads(::id(%s) & ::id(%s))", ascii_bytes(org_rev), ascii_bytes(other_rev))] # FIXME: expensive! + + other_changesets = [ + other_repo.get_changeset(rev) + for rev in hgrepo.revs( + b"ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)", + ascii_bytes(other_rev), ascii_bytes(org_rev), ascii_bytes(org_rev)) + ] + org_changesets = [ + self.get_changeset(ascii_str(hgrepo[rev].hex())) + for rev in hgrepo.revs( + b"ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)", + ascii_bytes(org_rev), ascii_bytes(other_rev), ascii_bytes(other_rev)) + ] + + return other_changesets, org_changesets, ancestors + def pull(self, url): """ Tries to pull changes from external location. diff --git a/kallithea/model/pull_request.py b/kallithea/model/pull_request.py --- a/kallithea/model/pull_request.py +++ b/kallithea/model/pull_request.py @@ -192,7 +192,6 @@ class CreatePullRequestAction(object): return False def __init__(self, org_repo, other_repo, org_ref, other_ref, title, description, owner, reviewers): - from kallithea.controllers.compare import CompareController reviewers = set(reviewers) _assert_valid_reviewers(reviewers) @@ -214,10 +213,7 @@ class CreatePullRequestAction(object): other_display = h.short_ref(other_ref_type, other_ref_name) cs_ranges, _cs_ranges_not, ancestor_revs = \ - CompareController._get_changesets(org_repo.scm_instance.alias, - other_repo.scm_instance, other_rev, # org and other "swapped" - org_repo.scm_instance, org_rev, - ) + org_repo.scm_instance.get_diff_changesets(other_rev, org_repo.scm_instance, org_rev) # org and other "swapped" if not cs_ranges: raise self.Empty(_('Cannot create empty pull request'))