diff --git a/rhodecode/controllers/changeset.py b/rhodecode/controllers/changeset.py --- a/rhodecode/controllers/changeset.py +++ b/rhodecode/controllers/changeset.py @@ -40,7 +40,7 @@ from rhodecode.lib.vcs.nodes import File import rhodecode.lib.helpers as h from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.utils import EmptyChangeset, action_logger +from rhodecode.lib.utils import action_logger from rhodecode.lib.compat import OrderedDict from rhodecode.lib import diffs from rhodecode.model.db import ChangesetComment, ChangesetStatus @@ -50,6 +50,7 @@ from rhodecode.model.meta import Session from rhodecode.lib.diffs import wrapped_diff from rhodecode.model.repo import RepoModel from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError +from rhodecode.lib.vcs.backends.base import EmptyChangeset log = logging.getLogger(__name__) diff --git a/rhodecode/controllers/compare.py b/rhodecode/controllers/compare.py --- a/rhodecode/controllers/compare.py +++ b/rhodecode/controllers/compare.py @@ -71,6 +71,7 @@ class CompareController(BaseRepoControll redirect(url('summary_home', repo_name=repo.repo_name)) except RepositoryError, e: + log.error(traceback.format_exc()) h.flash(str(e), category='warning') redirect(h.url('summary_home', repo_name=repo.repo_name)) diff --git a/rhodecode/controllers/files.py b/rhodecode/controllers/files.py --- a/rhodecode/controllers/files.py +++ b/rhodecode/controllers/files.py @@ -40,7 +40,7 @@ from rhodecode.lib.compat import Ordered from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.utils import EmptyChangeset +from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.exceptions import RepositoryError, \ ChangesetDoesNotExistError, EmptyRepositoryError, \ diff --git a/rhodecode/controllers/summary.py b/rhodecode/controllers/summary.py --- a/rhodecode/controllers/summary.py +++ b/rhodecode/controllers/summary.py @@ -45,7 +45,7 @@ from rhodecode.model.db import Statistic from rhodecode.lib.utils2 import safe_unicode from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.utils import EmptyChangeset +from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.markup_renderer import MarkupRenderer from rhodecode.lib.celerylib import run_task from rhodecode.lib.celerylib.tasks import get_commits_stats diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -34,15 +34,16 @@ from itertools import tee, imap from mercurial import patch from mercurial.mdiff import diffopts from mercurial.bundlerepo import bundlerepository -from mercurial import localrepo from pylons.i18n.translation import _ from rhodecode.lib.compat import BytesIO +from rhodecode.lib.vcs.utils.hgcompat import localrepo from rhodecode.lib.vcs.exceptions import VCSError from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode +from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.helpers import escape -from rhodecode.lib.utils import EmptyChangeset, make_ui +from rhodecode.lib.utils import make_ui def wrap_to_table(str_): @@ -599,9 +600,9 @@ def differ(org_repo, org_ref, other_repo if org_repo != other_repo: common, incoming, rheads = discovery_data - + other_repo_peer = localrepo.locallegacypeer(other_repo.local()) # create a bundle (uncompressed if other repo is not local) - if other_repo.capable('getbundle') and incoming: + if other_repo_peer.capable('getbundle') and incoming: # disable repo hooks here since it's just bundle ! # patch and reset hooks section of UI config to not run any # hooks on fetching archives with subrepos diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -346,50 +346,6 @@ def invalidate_cache(cache_key, *args): ScmModel().mark_for_invalidation(name) -class EmptyChangeset(BaseChangeset): - """ - An dummy empty changeset. It's possible to pass hash when creating - an EmptyChangeset - """ - - def __init__(self, cs='0' * 40, repo=None, requested_revision=None, - alias=None): - self._empty_cs = cs - self.revision = -1 - self.message = '' - self.author = '' - self.date = '' - self.repository = repo - self.requested_revision = requested_revision - self.alias = alias - - @LazyProperty - def raw_id(self): - """ - Returns raw string identifying this changeset, useful for web - representation. - """ - - return self._empty_cs - - @LazyProperty - def branch(self): - return get_backend(self.alias).DEFAULT_BRANCH_NAME - - @LazyProperty - def short_id(self): - return self.raw_id[:12] - - def get_file_changeset(self, path): - return self - - def get_file_content(self, path): - return u'' - - def get_file_size(self, path): - return 0 - - def map_groups(path): """ Given a full path to a repository, create all nested groups that this diff --git a/rhodecode/lib/utils2.py b/rhodecode/lib/utils2.py --- a/rhodecode/lib/utils2.py +++ b/rhodecode/lib/utils2.py @@ -417,6 +417,7 @@ def get_changeset_safe(repo, rev): """ from rhodecode.lib.vcs.backends.base import BaseRepository from rhodecode.lib.vcs.exceptions import RepositoryError + from rhodecode.lib.vcs.backends.base import EmptyChangeset if not isinstance(repo, BaseRepository): raise Exception('You must pass an Repository ' 'object as first argument got %s', type(repo)) @@ -424,7 +425,6 @@ def get_changeset_safe(repo, rev): try: cs = repo.get_changeset(rev) except RepositoryError: - from rhodecode.lib.utils import EmptyChangeset cs = EmptyChangeset(requested_revision=rev) return cs diff --git a/rhodecode/lib/vcs/utils/hgcompat.py b/rhodecode/lib/vcs/utils/hgcompat.py --- a/rhodecode/lib/vcs/utils/hgcompat.py +++ b/rhodecode/lib/vcs/utils/hgcompat.py @@ -13,3 +13,5 @@ from mercurial.mdiff import diffopts from mercurial.node import hex from mercurial.encoding import tolocal from mercurial import discovery +from mercurial import localrepo +from mercurial import scmutil \ No newline at end of file diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -36,7 +36,7 @@ from rhodecode.model.db import PullReque from rhodecode.model.notification import NotificationModel from rhodecode.lib.utils2 import safe_unicode -from rhodecode.lib.vcs.utils.hgcompat import discovery +from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil log = logging.getLogger(__name__) @@ -150,13 +150,9 @@ class PullRequestModel(BaseModel): """ changesets = [] #case two independent repos - if org_repo != other_repo: - common, incoming, rheads = discovery_data - - if not incoming: - revs = [] - else: - revs = org_repo._repo.changelog.findmissing(common, rheads) + common, incoming, rheads = discovery_data + if org_repo != other_repo and incoming: + revs = org_repo._repo.changelog.findmissing(common, rheads) for cs in reversed(map(binascii.hexlify, revs)): changesets.append(org_repo.get_changeset(cs)) @@ -175,7 +171,6 @@ class PullRequestModel(BaseModel): ) ] - from mercurial import scmutil out = scmutil.revrange(org_repo._repo, revs) for cs in reversed(out): changesets.append(org_repo.get_changeset(cs)) @@ -197,17 +192,22 @@ class PullRequestModel(BaseModel): :type other_ref: """ - other = org_repo._repo - repo = other_repo._repo - tip = other[org_ref[1]] + _org_repo = org_repo._repo + org_rev_type, org_rev = org_ref + + _other_repo = other_repo._repo + other_rev_type, other_rev = other_ref + log.debug('Doing discovery for %s@%s vs %s@%s' % ( org_repo, org_ref, other_repo, other_ref) ) - log.debug('Filter heads are %s[%s]' % (tip, org_ref[1])) + #log.debug('Filter heads are %s[%s]' % ('', org_ref[1])) + org_peer = localrepo.locallegacypeer(_org_repo.local()) tmp = discovery.findcommonincoming( - repo=repo, # other_repo we check for incoming - remote=other, # org_repo source for incoming - heads=[tip.node()], + repo=_other_repo, # other_repo we check for incoming + remote=org_peer, # org_repo source for incoming + heads=[_other_repo[other_rev].node(), + _org_repo[org_rev].node()], force=False ) return tmp @@ -237,8 +237,9 @@ class PullRequestModel(BaseModel): other_repo.scm_instance, other_ref) cs_ranges = self._get_changesets(org_repo.scm_instance, - org_ref, - other_repo.scm_instance, - other_ref, - discovery_data) + org_ref, + other_repo.scm_instance, + other_ref, + discovery_data) + return cs_ranges, discovery_data diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -40,13 +40,14 @@ from rhodecode.lib.vcs import get_backen from rhodecode.lib.vcs.exceptions import RepositoryError from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.nodes import FileNode +from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode import BACKENDS from rhodecode.lib import helpers as h from rhodecode.lib.utils2 import safe_str, safe_unicode from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \ - action_logger, EmptyChangeset, REMOVED_REPO_PAT + action_logger, REMOVED_REPO_PAT from rhodecode.model import BaseModel from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ UserFollowing, UserLog, User, RepoGroup, PullRequest @@ -414,6 +415,12 @@ class ScmModel(BaseModel): def commit_change(self, repo, repo_name, cs, user, author, message, content, f_path): + """ + Commits changes + + :param repo: SCM instance + + """ if repo.alias == 'hg': from rhodecode.lib.vcs.backends.hg import \ @@ -439,6 +446,7 @@ class ScmModel(BaseModel): action = 'push_local:%s' % tip.raw_id action_logger(user, action, repo_name) self.mark_for_invalidation(repo_name) + return tip def create_node(self, repo, repo_name, cs, user, author, message, content, f_path): @@ -477,6 +485,7 @@ class ScmModel(BaseModel): action = 'push_local:%s' % tip.raw_id action_logger(user, action, repo_name) self.mark_for_invalidation(repo_name) + return tip def get_nodes(self, repo_name, revision, root_path='/', flat=True): """ diff --git a/rhodecode/tests/functional/test_compare.py b/rhodecode/tests/functional/test_compare.py --- a/rhodecode/tests/functional/test_compare.py +++ b/rhodecode/tests/functional/test_compare.py @@ -1,12 +1,17 @@ from rhodecode.tests import * +from rhodecode.model.repo import RepoModel +from rhodecode.model.meta import Session +from rhodecode.model.db import Repository +from rhodecode.model.scm import ScmModel +from rhodecode.lib.vcs.backends.base import EmptyChangeset class TestCompareController(TestController): def test_index_tag(self): self.log_user() - tag1='0.1.3' - tag2='0.1.2' + tag1 = '0.1.3' + tag2 = '0.1.2' response = self.app.get(url(controller='compare', action='index', repo_name=HG_REPO, org_ref_type="tag", @@ -50,3 +55,135 @@ class TestCompareController(TestControll response.mustcontain('%s@default -> %s@default' % (HG_REPO, HG_REPO)) # branch are equal response.mustcontain('No changesets') + + def test_compare_revisions(self): + self.log_user() + rev1 = '3d8f361e72ab' + rev2 = 'b986218ba1c9' + response = self.app.get(url(controller='compare', action='index', + repo_name=HG_REPO, + org_ref_type="rev", + org_ref=rev1, + other_ref_type="rev", + other_ref=rev2, + )) + response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_REPO, rev2)) + ## outgoing changesets between those revisions + response.mustcontain("""r1:%s""" % (HG_REPO, rev1)) + + ## files + response.mustcontain(""".hgignore""" % (HG_REPO, rev1, rev2)) + + def test_compare_remote_repos(self): + self.log_user() + + form_data = dict( + repo_name=HG_FORK, + repo_name_full=HG_FORK, + repo_group=None, + repo_type='hg', + description='', + private=False, + copy_permissions=False, + landing_rev='tip', + update_after_clone=False, + fork_parent_id=Repository.get_by_repo_name(HG_REPO), + ) + RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN) + + Session().commit() + + rev1 = '7d4bc8ec6be5' + rev2 = '56349e29c2af' + + response = self.app.get(url(controller='compare', action='index', + repo_name=HG_REPO, + org_ref_type="rev", + org_ref=rev1, + other_ref_type="rev", + other_ref=rev2, + repo=HG_FORK + )) + + try: + response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2)) + ## outgoing changesets between those revisions + + response.mustcontain("""r6:%s""" % (HG_REPO, rev1)) + response.mustcontain("""r5:6fff84722075""" % (HG_REPO)) + response.mustcontain("""r4:2dda4e345fac""" % (HG_REPO)) + + ## files + response.mustcontain("""vcs/backends/hg.py""" % (HG_REPO, rev1, rev2)) + response.mustcontain("""vcs/backends/__init__.py""" % (HG_REPO, rev1, rev2)) + response.mustcontain("""vcs/backends/base.py""" % (HG_REPO, rev1, rev2)) + finally: + RepoModel().delete(HG_FORK) + + def test_compare_extra_commits(self): + self.log_user() + + repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', + description='diff-test', + owner=TEST_USER_ADMIN_LOGIN) + + repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg', + description='diff-test', + owner=TEST_USER_ADMIN_LOGIN) + + Session().commit() + r1_id = repo1.repo_id + r1_name = repo1.repo_name + r2_id = repo2.repo_id + r2_name = repo2.repo_name + + #commit something ! + cs0 = ScmModel().create_node( + repo=repo1.scm_instance, repo_name=r1_name, + cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit1', + content='line1', + f_path='file1' + ) + + cs0_prim = ScmModel().create_node( + repo=repo2.scm_instance, repo_name=r2_name, + cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit1', + content='line1', + f_path='file1' + ) + + cs1 = ScmModel().commit_change( + repo=repo2.scm_instance, repo_name=r2_name, + cs=cs0_prim, user=TEST_USER_ADMIN_LOGIN, author=TEST_USER_ADMIN_LOGIN, + message='commit2', + content='line1\nline2', + f_path='file1' + ) + + rev1 = 'default' + rev2 = 'default' + response = self.app.get(url(controller='compare', action='index', + repo_name=r2_name, + org_ref_type="branch", + org_ref=rev1, + other_ref_type="branch", + other_ref=rev2, + repo=r1_name + )) + + try: + response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) + + response.mustcontain("""
commit2
""") + response.mustcontain("""r1:%s""" % (r2_name, cs1.raw_id, cs1.short_id)) + ## files + response.mustcontain("""file1""" % (r2_name, rev1, rev2)) + + + finally: + RepoModel().delete(r1_id) + RepoModel().delete(r2_id)