changeset 8678:16a359ce1801

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.
author Mads Kiilerich <mads@kiilerich.com>
date Sun, 11 Oct 2020 11:52:22 +0200
parents a765b2961eda
children 0be48652ca48
files kallithea/controllers/compare.py kallithea/lib/vcs/backends/base.py kallithea/lib/vcs/backends/git/repository.py kallithea/lib/vcs/backends/hg/repository.py kallithea/model/pull_request.py
diffstat 5 files changed, 127 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/compare.py	Sun Oct 11 00:19:46 2020 +0200
+++ b/kallithea/controllers/compare.py	Sun Oct 11 11:52:22 2020 +0200
@@ -28,9 +28,7 @@
 
 
 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.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 @@
             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 @@
         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)
--- a/kallithea/lib/vcs/backends/base.py	Sun Oct 11 00:19:46 2020 +0200
+++ b/kallithea/lib/vcs/backends/base.py	Sun Oct 11 11:52:22 2020 +0200
@@ -170,6 +170,20 @@
         """
         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])
--- a/kallithea/lib/vcs/backends/git/repository.py	Sun Oct 11 00:19:46 2020 +0200
+++ b/kallithea/lib/vcs/backends/git/repository.py	Sun Oct 11 11:52:22 2020 +0200
@@ -20,6 +20,7 @@
 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.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 @@
 
         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):
         """
--- a/kallithea/lib/vcs/backends/hg/repository.py	Sun Oct 11 00:19:46 2020 +0200
+++ b/kallithea/lib/vcs/backends/hg/repository.py	Sun Oct 11 11:52:22 2020 +0200
@@ -33,12 +33,13 @@
 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 @@
 
         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.
--- a/kallithea/model/pull_request.py	Sun Oct 11 00:19:46 2020 +0200
+++ b/kallithea/model/pull_request.py	Sun Oct 11 11:52:22 2020 +0200
@@ -192,7 +192,6 @@
         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 @@
         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'))