# HG changeset patch # User Marcin Kuzminski # Date 2012-11-28 00:27:21 # Node ID a520d542697e4ef9f0670d898a7dfa00de901e32 # Parent 16456e7b2231ea023ebbfe008e6e3c40fd8fe732 Implemented file history page for showing detailed changelog for a given file - fixed git detection of filenode history when executed on directory - shortlog uses urlify_commit function now diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -514,6 +514,10 @@ def make_map(config): rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog', controller='shortlog', conditions=dict(function=check_repo)) + rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}', + controller='shortlog', f_path=None, + conditions=dict(function=check_repo)) + rmap.connect('branches_home', '/{repo_name:.*?}/branches', controller='branches', conditions=dict(function=check_repo)) diff --git a/rhodecode/controllers/shortlog.py b/rhodecode/controllers/shortlog.py --- a/rhodecode/controllers/shortlog.py +++ b/rhodecode/controllers/shortlog.py @@ -26,12 +26,16 @@ import logging from pylons import tmpl_context as c, request, url +from pylons.i18n.translation import _ +from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController, render from rhodecode.lib.helpers import RepoPage from pylons.controllers.util import redirect from rhodecode.lib.utils2 import safe_int +from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError, ChangesetError,\ + RepositoryError log = logging.getLogger(__name__) @@ -44,19 +48,53 @@ class ShortlogController(BaseRepoControl def __before__(self): super(ShortlogController, self).__before__() - def index(self, repo_name): + def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True): + """ + Safe way to get changeset if error occur it redirects to tip with + proper message + + :param rev: revision to fetch + :param repo_name: repo name to redirect after + """ + + try: + return c.rhodecode_repo.get_changeset(rev) + except RepositoryError, e: + h.flash(str(e), category='warning') + redirect(h.url('shortlog_home', repo_name=repo_name)) + + def index(self, repo_name, revision=None, f_path=None): p = safe_int(request.params.get('page', 1), 1) size = safe_int(request.params.get('size', 20), 20) def url_generator(**kw): return url('shortlog_home', repo_name=repo_name, size=size, **kw) - c.repo_changesets = RepoPage(c.rhodecode_repo, page=p, + collection = c.rhodecode_repo + c.file_history = f_path + if f_path: + f_path = f_path.lstrip('/') + # get the history for the file ! + tip_cs = c.rhodecode_repo.get_changeset() + try: + collection = tip_cs.get_file_history(f_path) + except (NodeDoesNotExistError, ChangesetError): + #this node is not present at tip ! + try: + cs = self.__get_cs_or_redirect(revision, repo_name) + collection = cs.get_file_history(f_path) + except RepositoryError, e: + h.flash(str(e), category='warning') + redirect(h.url('shortlog_home', repo_name=repo_name)) + collection = list(reversed(collection)) + + c.repo_changesets = RepoPage(collection, page=p, items_per_page=size, url=url_generator) - page_revisions = [x.raw_id for x in list(c.repo_changesets)] + page_revisions = [x.raw_id for x in list(collection)] c.statuses = c.rhodecode_db_repo.statuses(page_revisions) if not c.repo_changesets: + h.flash(_('There are no changesets yet'), category='warning') return redirect(url('summary_home', repo_name=repo_name)) c.shortlog_data = render('shortlog/shortlog_data.html') diff --git a/rhodecode/lib/vcs/backends/git/changeset.py b/rhodecode/lib/vcs/backends/git/changeset.py --- a/rhodecode/lib/vcs/backends/git/changeset.py +++ b/rhodecode/lib/vcs/backends/git/changeset.py @@ -162,6 +162,13 @@ class GitChangeset(BaseChangeset): elif isinstance(obj, objects.Tree): return NodeKind.DIR + def _get_filectx(self, path): + path = self._fix_path(path) + if self._get_kind(path) != NodeKind.FILE: + raise ChangesetError("File does not exist for revision %r at " + " %r" % (self.raw_id, path)) + return path + def _get_file_nodes(self): return chain(*(t[2] for t in self.walk())) @@ -264,6 +271,7 @@ class GitChangeset(BaseChangeset): which is generally not good. Should be replaced with algorithm iterating commits. """ + self._get_filectx(path) cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % ( self.id, path ) diff --git a/rhodecode/lib/vcs/backends/hg/changeset.py b/rhodecode/lib/vcs/backends/hg/changeset.py --- a/rhodecode/lib/vcs/backends/hg/changeset.py +++ b/rhodecode/lib/vcs/backends/hg/changeset.py @@ -181,7 +181,7 @@ class MercurialChangeset(BaseChangeset): path = self._fix_path(path) if self._get_kind(path) != NodeKind.FILE: raise ChangesetError("File does not exist for revision %r at " - " %r" % (self.revision, path)) + " %r" % (self.raw_id, path)) return self._ctx.filectx(path) def _extract_submodules(self): diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css +++ b/rhodecode/public/css/style.css @@ -2650,15 +2650,14 @@ h3.files_location { #graph_content .container .mid .message a:hover{ text-decoration: none; } -#content #graph_content .message .revision-link, -#changeset_content .container .message .revision-link + +.revision-link { color:#3F6F9F; font-weight: bold !important; } -#content #graph_content .message .issue-tracker-link, -#changeset_content .container .message .issue-tracker-link{ +.issue-tracker-link{ color:#3F6F9F; font-weight: bold !important; } diff --git a/rhodecode/templates/files/files_history_box.html b/rhodecode/templates/files/files_history_box.html --- a/rhodecode/templates/files/files_history_box.html +++ b/rhodecode/templates/files/files_history_box.html @@ -8,6 +8,7 @@ ${h.select('diff1',c.file_changeset.raw_id,c.file_history)} ${h.submit('diff',_('diff to revision'),class_="ui-btn")} ${h.submit('show_rev',_('show at revision'),class_="ui-btn")} + ${h.link_to(_('show full history'),h.url('shortlog_file_home',repo_name=c.repo_name, revision=c.file_changeset.raw_id, f_path=c.f_path),class_="ui-btn")} ${h.hidden('annotate', c.annotate)} ${h.end_form()} diff --git a/rhodecode/templates/shortlog/shortlog.html b/rhodecode/templates/shortlog/shortlog.html --- a/rhodecode/templates/shortlog/shortlog.html +++ b/rhodecode/templates/shortlog/shortlog.html @@ -11,7 +11,13 @@ » ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))} » - ${_('shortlog')} + %if c.file_history: + ${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))} + » + ${c.file_history} + %else: + ${_('shortlog')} + %endif <%def name="page_nav()"> diff --git a/rhodecode/templates/shortlog/shortlog_data.html b/rhodecode/templates/shortlog/shortlog_data.html --- a/rhodecode/templates/shortlog/shortlog_data.html +++ b/rhodecode/templates/shortlog/shortlog_data.html @@ -30,9 +30,7 @@ - ${h.link_to(h.truncate(cs.message,50) or _('No commit message'), - h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id), - title=cs.message)} + ${h.urlify_commit(h.truncate(cs.message,50),c.repo_name, h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))} ${h.age(cs.date)} diff --git a/rhodecode/tests/functional/test_shortlog.py b/rhodecode/tests/functional/test_shortlog.py --- a/rhodecode/tests/functional/test_shortlog.py +++ b/rhodecode/tests/functional/test_shortlog.py @@ -1,8 +1,65 @@ from rhodecode.tests import * + class TestShortlogController(TestController): - def test_index(self): + def test_index_hg(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + repo_name=HG_REPO)) + # Test response... + + def test_index_git(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + repo_name=GIT_REPO)) + # Test response... + + def test_index_hg_with_filenode(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/vcs/exceptions.py', + repo_name=HG_REPO)) + #history commits messages + response.mustcontain('Added exceptions module, this time for real') + response.mustcontain('Added not implemented hg backend test case') + response.mustcontain('Added BaseChangeset class') + # Test response... + + def test_index_git_with_filenode(self): self.log_user() - response = self.app.get(url(controller='shortlog', action='index',repo_name=HG_REPO)) - # Test response... + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/vcs/exceptions.py', + repo_name=GIT_REPO)) + #history commits messages + response.mustcontain('Added exceptions module, this time for real') + response.mustcontain('Added not implemented hg backend test case') + response.mustcontain('Added BaseChangeset class') + + def test_index_hg_with_filenode_that_is_dirnode(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/tests', + repo_name=HG_REPO)) + self.assertEqual(response.status, '302 Found') + + def test_index_git_with_filenode_that_is_dirnode(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/tests', + repo_name=GIT_REPO)) + self.assertEqual(response.status, '302 Found') + + def test_index_hg_with_filenode_not_existing(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/wrong_path', + repo_name=HG_REPO)) + self.assertEqual(response.status, '302 Found') + + def test_index_git_with_filenode_not_existing(self): + self.log_user() + response = self.app.get(url(controller='shortlog', action='index', + revision='tip', f_path='/wrong_path', + repo_name=GIT_REPO)) + self.assertEqual(response.status, '302 Found')