diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -32,8 +32,7 @@ def includeme(config): name='repo_summary_commits', pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True) - # repo commits - + # Commits config.add_route( name='repo_commit', pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True) @@ -46,11 +45,6 @@ def includeme(config): name='repo_commit_parents', pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True) - # still working url for backward compat. - config.add_route( - name='repo_commit_raw_deprecated', - pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True) - config.add_route( name='repo_commit_raw', pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True) @@ -79,7 +73,12 @@ def includeme(config): name='repo_commit_comment_delete', pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True) - # repo files + # still working url for backward compat. + config.add_route( + name='repo_commit_raw_deprecated', + pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True) + + # Files config.add_route( name='repo_archivefile', pattern='/{repo_name:.*?[^/]}/archive/{fname}', repo_route=True) @@ -168,7 +167,7 @@ def includeme(config): pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}', repo_route=True) - # refs data + # Refs data config.add_route( name='repo_refs_data', pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True) @@ -192,6 +191,15 @@ def includeme(config): name='repo_changelog_elements', pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True) + # Compare + config.add_route( + name='repo_compare_select', + pattern='/{repo_name:.*?[^/]}/compare', repo_route=True) + + config.add_route( + name='repo_compare', + pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True) + # Tags config.add_route( name='tags_home', diff --git a/rhodecode/tests/functional/test_compare.py b/rhodecode/apps/repository/tests/test_repo_compare.py rename from rhodecode/tests/functional/test_compare.py rename to rhodecode/apps/repository/tests/test_repo_compare.py --- a/rhodecode/tests/functional/test_compare.py +++ b/rhodecode/apps/repository/tests/test_repo_compare.py @@ -23,12 +23,30 @@ import pytest import lxml.html from rhodecode.lib.vcs.exceptions import RepositoryRequirementError -from rhodecode.tests import url, assert_session_flash +from rhodecode.tests import assert_session_flash from rhodecode.tests.utils import AssertResponse, commit_change +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'repo_compare_select': '/{repo_name}/compare', + 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}', + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url + + @pytest.mark.usefixtures("autologin_user", "app") -class TestCompareController(object): +class TestCompareView(object): + + def test_compare_index_is_reached_at_least_once(self, backend): + repo = backend.repo + self.app.get( + route_path('repo_compare_select', repo_name=repo.repo_name)) @pytest.mark.xfail_backends("svn", reason="Requires pull") def test_compare_remote_with_different_commit_indexes(self, backend): @@ -85,14 +103,14 @@ class TestCompareController(object): # Comparing the revisions response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=origin.repo_name, source_ref_type="rev", source_ref=commit3.raw_id, - target_repo=fork.repo_name, target_ref_type="rev", target_ref=commit4.raw_id, - merge='1',)) + params=dict(merge='1', target_repo=fork.repo_name) + )) compare_page = ComparePage(response) compare_page.contains_commits([commit4]) @@ -123,14 +141,14 @@ class TestCompareController(object): commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo1.repo_name, source_ref_type="branch", source_ref=commit_id2, - target_repo=repo2.repo_name, target_ref_type="branch", target_ref=commit_id1, - merge='1',)) + params=dict(merge='1', target_repo=repo2.repo_name) + )) response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2)) response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1)) @@ -180,14 +198,14 @@ class TestCompareController(object): commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo1.repo_name, source_ref_type="branch", source_ref=commit_id2, - target_repo=repo2.repo_name, target_ref_type="branch", target_ref=commit_id1, - merge='1')) + params=dict(merge='1', target_repo=repo2.repo_name), + )) response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2)) response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1)) @@ -211,17 +229,16 @@ class TestCompareController(object): fork = backend.create_repo(number_of_commits=1) response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=orig.repo_name, - action="compare", source_ref_type="rev", source_ref="tip", target_ref_type="rev", target_ref="tip", - merge='1', - target_repo=fork.repo_name), - status=400) - + params=dict(merge='1', target_repo=fork.repo_name), + ), + status=302) + response = response.follow() response.mustcontain("Repositories unrelated.") @pytest.mark.xfail_backends("svn") @@ -271,15 +288,15 @@ class TestCompareController(object): message='commit6', vcs_type=backend.alias, parent=commit4) response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo2.repo_name, source_ref_type="rev", # parent of commit2, in target repo2 source_ref=commit1.raw_id, - target_repo=repo1.repo_name, target_ref_type="rev", target_ref=commit4.raw_id, - merge='1',)) + params=dict(merge='1', target_repo=repo1.repo_name), + )) response.mustcontain('%s@%s' % (repo2.repo_name, commit1.short_id)) response.mustcontain('%s@%s' % (repo1.repo_name, commit4.short_id)) @@ -337,14 +354,15 @@ class TestCompareController(object): message='commit6', vcs_type=backend.alias, parent=commit4) response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo1.repo_name, source_ref_type="rev", # parent of commit3, not in source repo2 source_ref=commit2.raw_id, target_ref_type="rev", target_ref=commit5.raw_id, - merge='1',)) + params=dict(merge='1'), + )) response.mustcontain('%s@%s' % (repo1.repo_name, commit2.short_id)) response.mustcontain('%s@%s' % (repo1.repo_name, commit5.short_id)) @@ -367,14 +385,14 @@ class TestCompareController(object): commit_id2 = repo1.get_commit(commit_idx=6).raw_id response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo1.repo_name, source_ref_type="rev", source_ref=commit_id1, target_ref_type="rev", target_ref=commit_id2, - target_repo=repo2.repo_name, - merge='1',)) + params=dict(merge='1', target_repo=repo2.repo_name), + )) response.mustcontain('%s@%s' % (repo1.repo_name, commit_id1)) response.mustcontain('%s@%s' % (repo2.repo_name, commit_id2)) @@ -432,14 +450,14 @@ class TestCompareController(object): commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=r2_name, source_ref_type="branch", source_ref=commit_id1, target_ref_type="branch", target_ref=commit_id2, - target_repo=r1_name, - merge='1',)) + params=dict(merge='1', target_repo=r1_name), + )) response.mustcontain('%s@%s' % (r2_name, commit_id1)) response.mustcontain('%s@%s' % (r1_name, commit_id2)) @@ -453,14 +471,14 @@ class TestCompareController(object): # compare ! response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=r2_name, source_ref_type="branch", source_ref=commit_id1, target_ref_type="branch", target_ref=commit_id2, - target_repo=r1_name, - merge='1',)) + params=dict(merge='1', target_repo=r1_name), + )) response.mustcontain('%s@%s' % (r2_name, commit_id1)) response.mustcontain('%s@%s' % (r1_name, commit_id2)) @@ -476,13 +494,14 @@ class TestCompareController(object): commit1 = backend.repo.get_commit(commit_idx=1) response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=backend.repo_name, source_ref_type="rev", source_ref=commit0.raw_id, target_ref_type="rev", target_ref=commit1.raw_id, - merge='1',), + params=dict(merge='1') + ), extra_environ=xhr_header,) # outgoing commits between those commits @@ -494,14 +513,14 @@ class TestCompareController(object): badrepo = 'badrepo' response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=badrepo, source_ref_type="rev", source_ref='tip', target_ref_type="rev", target_ref='tip', - target_repo=repo.repo_name, - merge='1',), + params=dict(merge='1', target_repo=repo.repo_name) + ), status=404) def test_errors_when_comparing_unknown_target_repo(self, backend): @@ -509,14 +528,14 @@ class TestCompareController(object): badrepo = 'badrepo' response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo.repo_name, source_ref_type="rev", source_ref='tip', target_ref_type="rev", target_ref='tip', - target_repo=badrepo, - merge='1',), + params=dict(merge='1', target_repo=badrepo), + ), status=302) redirected = response.follow() redirected.mustcontain( @@ -526,13 +545,14 @@ class TestCompareController(object): commit0 = backend_stub.repo.get_commit(commit_idx=0) commit1 = backend_stub.repo.get_commit(commit_idx=1) - response = self.app.get(url('compare_url', - repo_name=backend_stub.repo_name, - source_ref_type="rev", - source_ref=commit0.raw_id, - target_ref_type="rev", - target_ref=commit1.raw_id, - ),) + response = self.app.get( + route_path('repo_compare', + repo_name=backend_stub.repo_name, + source_ref_type="rev", + source_ref=commit0.raw_id, + target_ref_type="rev", + target_ref=commit1.raw_id, + )) # outgoing commits between those commits compare_page = ComparePage(response) @@ -554,15 +574,14 @@ class TestCompareController(object): compare_mock.side_effect = RepositoryRequirementError() response = self.app.get( - url('compare_url', + route_path('repo_compare', repo_name=orig.repo_name, - action="compare", source_ref_type="rev", source_ref="tip", target_ref_type="rev", target_ref="tip", - merge='1', - target_repo=fork.repo_name), + params=dict(merge='1', target_repo=fork.repo_name), + ), status=302) assert_session_flash( @@ -577,13 +596,14 @@ class TestCompareControllerSvn(object): repo = backend_svn['svn-simple-layout'] commit_id = repo.get_commit(commit_idx=-1).raw_id response = app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo.repo_name, source_ref_type="tag", source_ref="%s@%s" % ('tags/v0.1', commit_id), target_ref_type="tag", target_ref="%s@%s" % ('tags/v0.2', commit_id), - merge='1',), + params=dict(merge='1'), + ), status=200) # Expecting no commits, since both paths are at the same revision @@ -599,13 +619,14 @@ class TestCompareControllerSvn(object): source_id = repo.get_commit(commit_idx=-6).raw_id target_id = repo.get_commit(commit_idx=-1).raw_id response = app.get( - url('compare_url', + route_path('repo_compare', repo_name=repo.repo_name, source_ref_type="tag", source_ref="%s@%s" % ('tags/v0.1', source_id), target_ref_type="tag", target_ref="%s@%s" % ('tags/v0.2', target_id), - merge='1',), + params=dict(merge='1') + ), status=200) # It should show commits @@ -673,3 +694,4 @@ class ComparePage(AssertResponse): def target_source_are_enabled(self): response = self.response response.mustcontain("var enable_fields = true;") + diff --git a/rhodecode/tests/functional/test_compare_local.py b/rhodecode/apps/repository/tests/test_repo_compare_local.py rename from rhodecode/tests/functional/test_compare_local.py rename to rhodecode/apps/repository/tests/test_repo_compare_local.py --- a/rhodecode/tests/functional/test_compare_local.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_local.py @@ -20,20 +20,32 @@ import pytest -from rhodecode.tests import url -from rhodecode.tests.functional.test_compare import ComparePage +from .test_repo_compare import ComparePage + + +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'repo_compare_select': '/{repo_name}/compare', + 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}', + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url @pytest.mark.usefixtures("autologin_user", "app") -class TestCompareController: +class TestCompareView(object): @pytest.mark.xfail_backends("svn", msg="Depends on branch and tag support") def test_compare_tag(self, backend): tag1 = 'v0.1.2' tag2 = 'v0.1.3' response = self.app.get( - url( - 'compare_url', + route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type="tag", source_ref=tag1, @@ -90,8 +102,9 @@ class TestCompareController: # functional tests. data = revisions[backend.alias] - response = self.app.get(url( - 'compare_url', + response = self.app.get( + route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type='branch', source_ref=data['branch'], @@ -106,8 +119,9 @@ class TestCompareController: def test_index_branch(self, backend): head_id = backend.default_head_id - response = self.app.get(url( - 'compare_url', + response = self.app.get( + route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type="branch", source_ref=head_id, @@ -126,8 +140,9 @@ class TestCompareController: commit1 = repo.get_commit(commit_idx=0) commit2 = repo.get_commit(commit_idx=1) - response = self.app.get(url( - 'compare_url', + response = self.app.get( + route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type="rev", source_ref=commit1.raw_id, diff --git a/rhodecode/tests/functional/test_compare_on_single_file.py b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py rename from rhodecode/tests/functional/test_compare_on_single_file.py rename to rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py --- a/rhodecode/tests/functional/test_compare_on_single_file.py +++ b/rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py @@ -21,13 +21,25 @@ import pytest from rhodecode.lib.vcs import nodes -from rhodecode.tests import url from rhodecode.tests.fixture import Fixture from rhodecode.tests.utils import commit_change fixture = Fixture() +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'repo_compare_select': '/{repo_name}/compare', + 'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}', + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url + + @pytest.mark.usefixtures("autologin_user", "app") class TestSideBySideDiff(object): @@ -45,18 +57,15 @@ class TestSideBySideDiff(object): repo.repo_name, filename=f_path, content=commit2_content, message='B, child of A', vcs_type=backend.alias, parent=commit1) - compare_url = url( - 'compare_url', + response = self.app.get(route_path( + 'repo_compare', repo_name=repo.repo_name, source_ref_type='rev', source_ref=commit1.raw_id, - target_repo=repo.repo_name, target_ref_type='rev', target_ref=commit2.raw_id, - f_path=f_path, - diffmode='sidebyside') - - response = self.app.get(compare_url) + params=dict(f_path=f_path, target_repo=repo.repo_name, diffmode='sidebyside') + )) response.mustcontain('Expand 1 commit') response.mustcontain('1 file changed') @@ -78,18 +87,15 @@ class TestSideBySideDiff(object): commit1 = repo.get_commit(commit_idx=0) commit2 = repo.get_commit(commit_idx=1) - compare_url = url( - 'compare_url', + response = self.app.get(route_path( + 'repo_compare', repo_name=repo.repo_name, source_ref_type='rev', source_ref=commit1.raw_id, - target_repo=repo.repo_name, target_ref_type='rev', target_ref=commit2.raw_id, - f_path=f_path, - diffmode='sidebyside') - - response = self.app.get(compare_url) + params=dict(f_path=f_path, target_repo=repo.repo_name, diffmode='sidebyside') + )) response.mustcontain('Expand 1 commit') response.mustcontain('1 file changed') @@ -124,16 +130,16 @@ class TestSideBySideDiff(object): commit2, commit1 = commit_info['commits'] file_changes = commit_info['changes'] - compare_url = url( - 'compare_url', + response = self.app.get(route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type='rev', source_ref=commit2, target_repo=backend.repo_name, target_ref_type='rev', target_ref=commit1, - diffmode='sidebyside') - response = self.app.get(compare_url) + params=dict(target_repo=backend.repo_name, diffmode='sidebyside') + )) response.mustcontain('Expand 1 commit') response.mustcontain(file_changes) @@ -163,17 +169,15 @@ class TestSideBySideDiff(object): commit2, commit1 = commit_info['commits'] file_changes = commit_info['changes'] - compare_url = url( - 'compare_url', + response = self.app.get(route_path( + 'repo_compare', repo_name=backend.repo_name, source_ref_type='rev', source_ref=commit2, - target_repo=backend.repo_name, target_ref_type='rev', target_ref=commit1, - f_path=f_path, - diffmode='sidebyside') - response = self.app.get(compare_url) + params=dict(f_path=f_path, target_repo=backend.repo_name, diffmode='sidebyside') + )) response.mustcontain('Expand 1 commit') response.mustcontain(file_changes) diff --git a/rhodecode/apps/repository/views/repo_compare.py b/rhodecode/apps/repository/views/repo_compare.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/views/repo_compare.py @@ -0,0 +1,323 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2012-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + + +import logging + +from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPFound +from pyramid.view import view_config +from pyramid.renderers import render +from pyramid.response import Response + + +from rhodecode.apps._base import RepoAppView +from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name +from rhodecode.lib import helpers as h +from rhodecode.lib import diffs, codeblocks +from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator +from rhodecode.lib.utils import safe_str +from rhodecode.lib.utils2 import safe_unicode, str2bool +from rhodecode.lib.vcs.exceptions import ( + EmptyRepositoryError, RepositoryError, RepositoryRequirementError, + NodeDoesNotExistError) +from rhodecode.model.db import Repository, ChangesetStatus + +log = logging.getLogger(__name__) + + +class RepoCompareView(RepoAppView): + def load_default_context(self): + c = self._get_local_tmpl_context(include_app_defaults=True) + + # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead + c.repo_info = self.db_repo + c.rhodecode_repo = self.rhodecode_vcs_repo + + self._register_global_c(c) + return c + + def _get_commit_or_redirect( + self, ref, ref_type, repo, redirect_after=True, partial=False): + """ + This is a safe way to get a commit. If an error occurs it + redirects to a commit with a proper message. If partial is set + then it does not do redirect raise and throws an exception instead. + """ + _ = self.request.translate + try: + return get_commit_from_ref_name(repo, safe_str(ref), ref_type) + except EmptyRepositoryError: + if not redirect_after: + return repo.scm_instance().EMPTY_COMMIT + h.flash(h.literal(_('There are no commits yet')), + category='warning') + raise HTTPFound( + h.route_path('repo_summary', repo_name=repo.repo_name)) + + except RepositoryError as e: + log.exception(safe_str(e)) + h.flash(safe_str(h.escape(e)), category='warning') + if not partial: + raise HTTPFound( + h.route_path('repo_summary', repo_name=repo.repo_name)) + raise HTTPBadRequest() + + @LoginRequired() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='repo_compare_select', request_method='GET', + renderer='rhodecode:templates/compare/compare_diff.mako') + def compare_select(self): + _ = self.request.translate + c = self.load_default_context() + + source_repo = self.db_repo_name + target_repo = self.request.GET.get('target_repo', source_repo) + c.source_repo = Repository.get_by_repo_name(source_repo) + c.target_repo = Repository.get_by_repo_name(target_repo) + + if c.source_repo is None or c.target_repo is None: + raise HTTPNotFound() + + c.compare_home = True + c.commit_ranges = [] + c.collapse_all_commits = False + c.diffset = None + c.limited_diff = False + c.source_ref = c.target_ref = _('Select commit') + c.source_ref_type = "" + c.target_ref_type = "" + c.commit_statuses = ChangesetStatus.STATUSES + c.preview_mode = False + c.file_path = None + + return self._get_template_context(c) + + @LoginRequired() + @HasRepoPermissionAnyDecorator( + 'repository.read', 'repository.write', 'repository.admin') + @view_config( + route_name='repo_compare', request_method='GET', + renderer=None) + def compare(self): + _ = self.request.translate + c = self.load_default_context() + + source_ref_type = self.request.matchdict['source_ref_type'] + source_ref = self.request.matchdict['source_ref'] + target_ref_type = self.request.matchdict['target_ref_type'] + target_ref = self.request.matchdict['target_ref'] + + # source_ref will be evaluated in source_repo + source_repo_name = self.db_repo_name + source_path, source_id = parse_path_ref(source_ref) + + # target_ref will be evaluated in target_repo + target_repo_name = self.request.GET.get('target_repo', source_repo_name) + target_path, target_id = parse_path_ref( + target_ref, default_path=self.request.GET.get('f_path', '')) + + # if merge is True + # Show what changes since the shared ancestor commit of target/source + # the source would get if it was merged with target. Only commits + # which are in target but not in source will be shown. + merge = str2bool(self.request.GET.get('merge')) + # if merge is False + # Show a raw diff of source/target refs even if no ancestor exists + + # c.fulldiff disables cut_off_limit + c.fulldiff = str2bool(self.request.GET.get('fulldiff')) + + c.file_path = target_path + c.commit_statuses = ChangesetStatus.STATUSES + + # if partial, returns just compare_commits.html (commits log) + partial = self.request.is_xhr + + # swap url for compare_diff page + c.swap_url = h.route_path( + 'repo_compare', + repo_name=target_repo_name, + source_ref_type=target_ref_type, + source_ref=target_ref, + target_repo=source_repo_name, + target_ref_type=source_ref_type, + target_ref=source_ref, + _query=dict(merge=merge and '1' or '', f_path=target_path)) + + source_repo = Repository.get_by_repo_name(source_repo_name) + target_repo = Repository.get_by_repo_name(target_repo_name) + + if source_repo is None: + log.error('Could not find the source repo: {}' + .format(source_repo_name)) + h.flash(_('Could not find the source repo: `{}`') + .format(h.escape(source_repo_name)), category='error') + raise HTTPFound( + h.route_path('repo_compare_select', repo_name=self.db_repo_name)) + + if target_repo is None: + log.error('Could not find the target repo: {}' + .format(source_repo_name)) + h.flash(_('Could not find the target repo: `{}`') + .format(h.escape(target_repo_name)), category='error') + raise HTTPFound( + h.route_path('repo_compare_select', repo_name=self.db_repo_name)) + + source_scm = source_repo.scm_instance() + target_scm = target_repo.scm_instance() + + source_alias = source_scm.alias + target_alias = target_scm.alias + if source_alias != target_alias: + msg = _('The comparison of two different kinds of remote repos ' + 'is not available') + log.error(msg) + h.flash(msg, category='error') + raise HTTPFound( + h.route_path('repo_compare_select', repo_name=self.db_repo_name)) + + source_commit = self._get_commit_or_redirect( + ref=source_id, ref_type=source_ref_type, repo=source_repo, + partial=partial) + target_commit = self._get_commit_or_redirect( + ref=target_id, ref_type=target_ref_type, repo=target_repo, + partial=partial) + + c.compare_home = False + c.source_repo = source_repo + c.target_repo = target_repo + c.source_ref = source_ref + c.target_ref = target_ref + c.source_ref_type = source_ref_type + c.target_ref_type = target_ref_type + + pre_load = ["author", "branch", "date", "message"] + c.ancestor = None + + if c.file_path: + if source_commit == target_commit: + c.commit_ranges = [] + else: + c.commit_ranges = [target_commit] + else: + try: + c.commit_ranges = source_scm.compare( + source_commit.raw_id, target_commit.raw_id, + target_scm, merge, pre_load=pre_load) + if merge: + c.ancestor = source_scm.get_common_ancestor( + source_commit.raw_id, target_commit.raw_id, target_scm) + except RepositoryRequirementError: + msg = _('Could not compare repos with different ' + 'large file settings') + log.error(msg) + if partial: + return Response(msg) + h.flash(msg, category='error') + raise HTTPFound( + h.route_path('repo_compare_select', + repo_name=self.db_repo_name)) + + c.statuses = self.db_repo.statuses( + [x.raw_id for x in c.commit_ranges]) + + # auto collapse if we have more than limit + collapse_limit = diffs.DiffProcessor._collapse_commits_over + c.collapse_all_commits = len(c.commit_ranges) > collapse_limit + + if partial: # for PR ajax commits loader + if not c.ancestor: + return Response('') # cannot merge if there is no ancestor + + html = render( + 'rhodecode:templates/compare/compare_commits.mako', + self._get_template_context(c), self.request) + return Response(html) + + if c.ancestor: + # case we want a simple diff without incoming commits, + # previewing what will be merged. + # Make the diff on target repo (which is known to have target_ref) + log.debug('Using ancestor %s as source_ref instead of %s' + % (c.ancestor, source_ref)) + source_repo = target_repo + source_commit = target_repo.get_commit(commit_id=c.ancestor) + + # diff_limit will cut off the whole diff if the limit is applied + # otherwise it will just hide the big files from the front-end + diff_limit = c.visual.cut_off_limit_diff + file_limit = c.visual.cut_off_limit_file + + log.debug('calculating diff between ' + 'source_ref:%s and target_ref:%s for repo `%s`', + source_commit, target_commit, + safe_unicode(source_repo.scm_instance().path)) + + if source_commit.repository != target_commit.repository: + msg = _( + "Repositories unrelated. " + "Cannot compare commit %(commit1)s from repository %(repo1)s " + "with commit %(commit2)s from repository %(repo2)s.") % { + 'commit1': h.show_id(source_commit), + 'repo1': source_repo.repo_name, + 'commit2': h.show_id(target_commit), + 'repo2': target_repo.repo_name, + } + h.flash(msg, category='error') + raise HTTPFound( + h.route_path('repo_compare_select', + repo_name=self.db_repo_name)) + + txt_diff = source_repo.scm_instance().get_diff( + commit1=source_commit, commit2=target_commit, + path=target_path, path1=source_path) + + diff_processor = diffs.DiffProcessor( + txt_diff, format='newdiff', diff_limit=diff_limit, + file_limit=file_limit, show_full_diff=c.fulldiff) + _parsed = diff_processor.prepare() + + def _node_getter(commit): + """ Returns a function that returns a node for a commit or None """ + def get_node(fname): + try: + return commit.get_node(fname) + except NodeDoesNotExistError: + return None + return get_node + + diffset = codeblocks.DiffSet( + repo_name=source_repo.repo_name, + source_node_getter=_node_getter(source_commit), + target_node_getter=_node_getter(target_commit), + ) + c.diffset = diffset.render_patchset( + _parsed, source_ref, target_ref) + + c.preview_mode = merge + c.source_commit = source_commit + c.target_commit = target_commit + + html = render( + 'rhodecode:templates/compare/compare_diff.mako', + self._get_template_context(c), self.request) + return Response(html) \ No newline at end of file diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -392,14 +392,15 @@ class RepoFilesView(RepoAppView): c.action = self.request.GET.get('diff') if c.action not in ['download', 'raw']: - compare_url = h.url( - 'compare_url', repo_name=self.db_repo_name, + compare_url = h.route_path( + 'repo_compare', + repo_name=self.db_repo_name, source_ref_type='rev', source_ref=diff1, target_repo=self.db_repo_name, target_ref_type='rev', target_ref=diff2, - f_path=f_path) + _query=dict(f_path=f_path)) # redirect to new view if we render diff raise HTTPFound(compare_url) @@ -471,15 +472,15 @@ class RepoFilesView(RepoAppView): category='error') raise HTTPBadRequest() - compare_url = h.url( - 'compare_url', repo_name=self.db_repo_name, + compare_url = h.route_path( + 'repo_compare', + repo_name=self.db_repo_name, source_ref_type='rev', source_ref=diff1, - target_repo=self.db_repo_name, target_ref_type='rev', target_ref=diff2, - f_path=f_path, - diffmode='sideside') + _query=dict(f_path=f_path, diffmode='sideside', + target_repo=self.db_repo_name,)) raise HTTPFound(compare_url) @LoginRequired() diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -502,17 +502,6 @@ def make_map(config): conditions={'method': ['GET', 'POST'], 'function': check_repo}, requirements=URL_NAME_REQUIREMENTS) - rmap.connect('compare_home', - '/{repo_name}/compare', - controller='compare', action='index', - conditions={'function': check_repo}, - requirements=URL_NAME_REQUIREMENTS) - - rmap.connect('compare_url', - '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', - controller='compare', action='compare', - conditions={'function': check_repo}, - requirements=URL_NAME_REQUIREMENTS, jsroute=True) rmap.connect('pullrequest_home', '/{repo_name}/pull-request/new', controller='pullrequests', diff --git a/rhodecode/controllers/compare.py b/rhodecode/controllers/compare.py deleted file mode 100644 --- a/rhodecode/controllers/compare.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (C) 2012-2017 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# This program is dual-licensed. If you wish to learn more about the -# RhodeCode Enterprise Edition, including its added features, Support services, -# and proprietary license terms, please see https://rhodecode.com/licenses/ - -""" -Compare controller for showing differences between two commits/refs/tags etc. -""" - -import logging - -from webob.exc import HTTPBadRequest, HTTPNotFound -from pylons import request, tmpl_context as c, url -from pylons.controllers.util import redirect -from pylons.i18n.translation import _ - -from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name -from rhodecode.lib import helpers as h -from rhodecode.lib import diffs, codeblocks -from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator -from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.utils import safe_str -from rhodecode.lib.utils2 import safe_unicode, str2bool -from rhodecode.lib.vcs.exceptions import ( - EmptyRepositoryError, RepositoryError, RepositoryRequirementError, - NodeDoesNotExistError) -from rhodecode.model.db import Repository, ChangesetStatus - -log = logging.getLogger(__name__) - - -class CompareController(BaseRepoController): - - def __before__(self): - super(CompareController, self).__before__() - - def _get_commit_or_redirect( - self, ref, ref_type, repo, redirect_after=True, partial=False): - """ - This is a safe way to get a commit. If an error occurs it - redirects to a commit with a proper message. If partial is set - then it does not do redirect raise and throws an exception instead. - """ - try: - return get_commit_from_ref_name(repo, safe_str(ref), ref_type) - except EmptyRepositoryError: - if not redirect_after: - return repo.scm_instance().EMPTY_COMMIT - h.flash(h.literal(_('There are no commits yet')), - category='warning') - redirect(h.route_path('repo_summary', repo_name=repo.repo_name)) - - except RepositoryError as e: - log.exception(safe_str(e)) - h.flash(safe_str(h.escape(e)), category='warning') - if not partial: - redirect(h.route_path('repo_summary', repo_name=repo.repo_name)) - raise HTTPBadRequest() - - @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') - def index(self, repo_name): - c.compare_home = True - c.commit_ranges = [] - c.collapse_all_commits = False - c.diffset = None - c.limited_diff = False - source_repo = c.rhodecode_db_repo.repo_name - target_repo = request.GET.get('target_repo', source_repo) - c.source_repo = Repository.get_by_repo_name(source_repo) - c.target_repo = Repository.get_by_repo_name(target_repo) - - if c.source_repo is None or c.target_repo is None: - raise HTTPNotFound() - - c.source_ref = c.target_ref = _('Select commit') - c.source_ref_type = "" - c.target_ref_type = "" - c.commit_statuses = ChangesetStatus.STATUSES - c.preview_mode = False - c.file_path = None - return render('compare/compare_diff.mako') - - @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') - def compare(self, repo_name, source_ref_type, source_ref, - target_ref_type, target_ref): - # source_ref will be evaluated in source_repo - source_repo_name = c.rhodecode_db_repo.repo_name - source_path, source_id = parse_path_ref(source_ref) - - # target_ref will be evaluated in target_repo - target_repo_name = request.GET.get('target_repo', source_repo_name) - target_path, target_id = parse_path_ref( - target_ref, default_path=request.GET.get('f_path', '')) - - c.file_path = target_path - c.commit_statuses = ChangesetStatus.STATUSES - - # if merge is True - # Show what changes since the shared ancestor commit of target/source - # the source would get if it was merged with target. Only commits - # which are in target but not in source will be shown. - merge = str2bool(request.GET.get('merge')) - # if merge is False - # Show a raw diff of source/target refs even if no ancestor exists - - # c.fulldiff disables cut_off_limit - c.fulldiff = str2bool(request.GET.get('fulldiff')) - - # if partial, returns just compare_commits.html (commits log) - partial = request.is_xhr - - # swap url for compare_diff page - c.swap_url = h.url( - 'compare_url', - repo_name=target_repo_name, - source_ref_type=target_ref_type, - source_ref=target_ref, - target_repo=source_repo_name, - target_ref_type=source_ref_type, - target_ref=source_ref, - merge=merge and '1' or '', - f_path=target_path) - - source_repo = Repository.get_by_repo_name(source_repo_name) - target_repo = Repository.get_by_repo_name(target_repo_name) - - if source_repo is None: - log.error('Could not find the source repo: {}' - .format(source_repo_name)) - h.flash(_('Could not find the source repo: `{}`') - .format(h.escape(source_repo_name)), category='error') - return redirect(url('compare_home', repo_name=c.repo_name)) - - if target_repo is None: - log.error('Could not find the target repo: {}' - .format(source_repo_name)) - h.flash(_('Could not find the target repo: `{}`') - .format(h.escape(target_repo_name)), category='error') - return redirect(url('compare_home', repo_name=c.repo_name)) - - source_scm = source_repo.scm_instance() - target_scm = target_repo.scm_instance() - - source_alias = source_scm.alias - target_alias = target_scm.alias - if source_alias != target_alias: - msg = _('The comparison of two different kinds of remote repos ' - 'is not available') - log.error(msg) - h.flash(msg, category='error') - return redirect(url('compare_home', repo_name=c.repo_name)) - - source_commit = self._get_commit_or_redirect( - ref=source_id, ref_type=source_ref_type, repo=source_repo, - partial=partial) - target_commit = self._get_commit_or_redirect( - ref=target_id, ref_type=target_ref_type, repo=target_repo, - partial=partial) - - c.compare_home = False - c.source_repo = source_repo - c.target_repo = target_repo - c.source_ref = source_ref - c.target_ref = target_ref - c.source_ref_type = source_ref_type - c.target_ref_type = target_ref_type - - pre_load = ["author", "branch", "date", "message"] - c.ancestor = None - - if c.file_path: - if source_commit == target_commit: - c.commit_ranges = [] - else: - c.commit_ranges = [target_commit] - else: - try: - c.commit_ranges = source_scm.compare( - source_commit.raw_id, target_commit.raw_id, - target_scm, merge, pre_load=pre_load) - if merge: - c.ancestor = source_scm.get_common_ancestor( - source_commit.raw_id, target_commit.raw_id, target_scm) - except RepositoryRequirementError: - msg = _('Could not compare repos with different ' - 'large file settings') - log.error(msg) - if partial: - return msg - h.flash(msg, category='error') - return redirect(url('compare_home', repo_name=c.repo_name)) - - c.statuses = c.rhodecode_db_repo.statuses( - [x.raw_id for x in c.commit_ranges]) - - # auto collapse if we have more than limit - collapse_limit = diffs.DiffProcessor._collapse_commits_over - c.collapse_all_commits = len(c.commit_ranges) > collapse_limit - - if partial: # for PR ajax commits loader - if not c.ancestor: - return '' # cannot merge if there is no ancestor - return render('compare/compare_commits.mako') - - if c.ancestor: - # case we want a simple diff without incoming commits, - # previewing what will be merged. - # Make the diff on target repo (which is known to have target_ref) - log.debug('Using ancestor %s as source_ref instead of %s' - % (c.ancestor, source_ref)) - source_repo = target_repo - source_commit = target_repo.get_commit(commit_id=c.ancestor) - - # diff_limit will cut off the whole diff if the limit is applied - # otherwise it will just hide the big files from the front-end - diff_limit = self.cut_off_limit_diff - file_limit = self.cut_off_limit_file - - log.debug('calculating diff between ' - 'source_ref:%s and target_ref:%s for repo `%s`', - source_commit, target_commit, - safe_unicode(source_repo.scm_instance().path)) - - if source_commit.repository != target_commit.repository: - msg = _( - "Repositories unrelated. " - "Cannot compare commit %(commit1)s from repository %(repo1)s " - "with commit %(commit2)s from repository %(repo2)s.") % { - 'commit1': h.show_id(source_commit), - 'repo1': source_repo.repo_name, - 'commit2': h.show_id(target_commit), - 'repo2': target_repo.repo_name, - } - h.flash(msg, category='error') - raise HTTPBadRequest() - - txtdiff = source_repo.scm_instance().get_diff( - commit1=source_commit, commit2=target_commit, - path=target_path, path1=source_path) - - diff_processor = diffs.DiffProcessor( - txtdiff, format='newdiff', diff_limit=diff_limit, - file_limit=file_limit, show_full_diff=c.fulldiff) - _parsed = diff_processor.prepare() - - def _node_getter(commit): - """ Returns a function that returns a node for a commit or None """ - def get_node(fname): - try: - return commit.get_node(fname) - except NodeDoesNotExistError: - return None - return get_node - - diffset = codeblocks.DiffSet( - repo_name=source_repo.repo_name, - source_node_getter=_node_getter(source_commit), - target_node_getter=_node_getter(target_commit), - ) - c.diffset = diffset.render_patchset( - _parsed, source_ref, target_ref) - - c.preview_mode = merge - c.source_commit = source_commit - c.target_commit = target_commit - - return render('compare/compare_diff.mako') diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -15,7 +15,6 @@ function registerRCRoutes() { pyroutes.register('new_repo', '/_admin/create_repository', []); pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); - pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']); pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']); pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']); pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']); @@ -108,7 +107,6 @@ function registerRCRoutes() { pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']); - pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']); @@ -116,6 +114,7 @@ function registerRCRoutes() { pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']); pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']); + pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']); pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']); pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']); pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']); @@ -145,6 +144,8 @@ function registerRCRoutes() { pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']); pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']); + pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']); + pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']); pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']); pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']); pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']); diff --git a/rhodecode/public/js/src/rhodecode.js b/rhodecode/public/js/src/rhodecode.js --- a/rhodecode/public/js/src/rhodecode.js +++ b/rhodecode/public/js/src/rhodecode.js @@ -58,7 +58,7 @@ var compare_radio_buttons = function(rep target_ref_type: compare_ref_type, merge: 1 }; - window.location = pyroutes.url('compare_url', url_data); + window.location = pyroutes.url('repo_compare', url_data); } }); $('.compare-radio-button').on('click', function(e){ diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako --- a/rhodecode/templates/base/base.mako +++ b/rhodecode/templates/base/base.mako @@ -228,9 +228,7 @@
  • -
  • - -
  • +
  • ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()" %if c.rhodecode_db_repo.repo_type in ['git','hg']:
  • @@ -251,8 +249,19 @@
  • ${_('Settings')}
  • %endif %if c.rhodecode_db_repo.fork: -
  • - ${_('Compare fork')}
  • +
  • + + ${_('Compare fork')} + +
  • %endif
  • ${_('Search')}
  • diff --git a/rhodecode/templates/changelog/changelog.mako b/rhodecode/templates/changelog/changelog.mako --- a/rhodecode/templates/changelog/changelog.mako +++ b/rhodecode/templates/changelog/changelog.mako @@ -39,17 +39,15 @@ - - ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)} + ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)} %endif diff --git a/rhodecode/templates/changeset/changeset_range.mako b/rhodecode/templates/changeset/changeset_range.mako --- a/rhodecode/templates/changeset/changeset_range.mako +++ b/rhodecode/templates/changeset/changeset_range.mako @@ -55,7 +55,13 @@
    diff --git a/rhodecode/templates/compare/compare_diff.mako b/rhodecode/templates/compare/compare_diff.mako --- a/rhodecode/templates/compare/compare_diff.mako +++ b/rhodecode/templates/compare/compare_diff.mako @@ -288,7 +288,7 @@ target_ref: target.id, target_ref_type: target.type }; - window.location = pyroutes.url('compare_url', url_data); + window.location = pyroutes.url('repo_compare', url_data); } }); $('#compare_changeset_status_toggle').on('click', function(e) { diff --git a/rhodecode/templates/files/files.mako b/rhodecode/templates/files/files.mako --- a/rhodecode/templates/files/files.mako +++ b/rhodecode/templates/files/files.mako @@ -174,7 +174,7 @@ merge: 1, f_path: state.f_path }; - window.location = pyroutes.url('compare_url', url_data); + window.location = pyroutes.url('repo_compare', url_data); }); $('#show_at_commit').on('click', function(e) { diff --git a/rhodecode/templates/forks/forks_data.mako b/rhodecode/templates/forks/forks_data.mako --- a/rhodecode/templates/forks/forks_data.mako +++ b/rhodecode/templates/forks/forks_data.mako @@ -26,8 +26,17 @@ ${_('Compare fork')} + class="btn-link" + href="${h.route_path('repo_compare', + repo_name=c.repo_name, + source_ref_type=c.rhodecode_db_repo.landing_rev[0], + source_ref=c.rhodecode_db_repo.landing_rev[1], + target_ref_type=c.rhodecode_db_repo.landing_rev[0], + target_ref=c.rhodecode_db_repo.landing_rev[1], + _query=dict(merge=1, target_repo=f.repo_name))}" + > + ${_('Compare fork')} + % endfor diff --git a/rhodecode/templates/pullrequests/pullrequest.mako b/rhodecode/templates/pullrequests/pullrequest.mako --- a/rhodecode/templates/pullrequests/pullrequest.mako +++ b/rhodecode/templates/pullrequests/pullrequest.mako @@ -302,7 +302,7 @@ prButtonLock(true, "${_('Please select source and target')}"); return; } - var url = pyroutes.url('compare_url', url_data); + var url = pyroutes.url('repo_compare', url_data); // lock PR button, so we cannot send PR before it's calculated prButtonLock(true, "${_('Loading compare ...')}", 'compare'); diff --git a/rhodecode/tests/controllers/test_compare.py b/rhodecode/tests/controllers/test_compare.py deleted file mode 100644 --- a/rhodecode/tests/controllers/test_compare.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (C) 2010-2017 RhodeCode GmbH -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License, version 3 -# (only), as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -# This program is dual-licensed. If you wish to learn more about the -# RhodeCode Enterprise Edition, including its added features, Support services, -# and proprietary license terms, please see https://rhodecode.com/licenses/ - -from rhodecode.tests import url - - -def test_compare_index_is_reached_at_least_once( - app, backend): - repo = backend.repo - app.get(url( - controller="compare", action='index', repo_name=repo.repo_name))