# HG changeset patch # User Mads Kiilerich # Date 2020-10-11 11:52:22 # Node ID 16a359ce180157bcba844ffab6da7e5b55877437 # Parent a765b2961eda92e78e451e4dcb2567c72a39ce68 vcs: move changeset diff from controller to vcs Remove unfortunate model dependency on controller ... and put the VCS details where they belong, with less VCS specific knowledge in the controllers. 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'))