aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Kuzminski <marcin@python-works.com>2012-11-28 01:27:21 +0100
committerMarcin Kuzminski <marcin@python-works.com>2012-11-28 01:27:21 +0100
commit56c76f2a84d0f0487b40da954e672241479431ec (patch)
tree5e6f97be2543fc767c6e941bcbf5c4b5d4c75753
parentb77a593e2cd5af764d7c29e4e84f75e488fadf13 (diff)
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 --HG-- branch : beta
-rw-r--r--rhodecode/config/routing.py4
-rw-r--r--rhodecode/controllers/shortlog.py44
-rw-r--r--rhodecode/lib/vcs/backends/git/changeset.py8
-rw-r--r--rhodecode/lib/vcs/backends/hg/changeset.py2
-rw-r--r--rhodecode/public/css/style.css7
-rw-r--r--rhodecode/templates/files/files_history_box.html1
-rw-r--r--rhodecode/templates/shortlog/shortlog.html8
-rw-r--r--rhodecode/templates/shortlog/shortlog_data.html4
-rw-r--r--rhodecode/tests/functional/test_shortlog.py61
9 files changed, 125 insertions, 14 deletions
diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
index bf537690..d268bff9 100644
--- 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
index 1c20caa1..4348c9e5 100644
--- 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(BaseRepoController):
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
index 3887b388..b05087b8 100644
--- 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
index bf85b2e9..2ce654b1 100644
--- 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
index e049f61e..94b681c9 100644
--- 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
index b00405b3..22839c13 100644
--- 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()}
</div>
diff --git a/rhodecode/templates/shortlog/shortlog.html b/rhodecode/templates/shortlog/shortlog.html
index 2ec3f6e2..91ab9b57 100644
--- a/rhodecode/templates/shortlog/shortlog.html
+++ b/rhodecode/templates/shortlog/shortlog.html
@@ -11,7 +11,13 @@
&raquo;
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
&raquo;
- ${_('shortlog')}
+ %if c.file_history:
+ ${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
+ &raquo;
+ ${c.file_history}
+ %else:
+ ${_('shortlog')}
+ %endif
</%def>
<%def name="page_nav()">
diff --git a/rhodecode/templates/shortlog/shortlog_data.html b/rhodecode/templates/shortlog/shortlog_data.html
index 439ed1cd..1f40b2d6 100644
--- a/rhodecode/templates/shortlog/shortlog_data.html
+++ b/rhodecode/templates/shortlog/shortlog_data.html
@@ -30,9 +30,7 @@
</div>
</td>
<td>
- ${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))}
</td>
<td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
${h.age(cs.date)}</span>
diff --git a/rhodecode/tests/functional/test_shortlog.py b/rhodecode/tests/functional/test_shortlog.py
index 0edbe691..49e84280 100644
--- 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))
+ 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',
+ 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')