##// END OF EJS Templates
Implemented file history page for showing detailed changelog for a given file...
marcink -
r3039:a520d542 beta
parent child Browse files
Show More
@@ -514,6 +514,10 b' def make_map(config):'
514 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
514 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
515 controller='shortlog', conditions=dict(function=check_repo))
515 controller='shortlog', conditions=dict(function=check_repo))
516
516
517 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
518 controller='shortlog', f_path=None,
519 conditions=dict(function=check_repo))
520
517 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
521 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
518 controller='branches', conditions=dict(function=check_repo))
522 controller='branches', conditions=dict(function=check_repo))
519
523
@@ -26,12 +26,16 b''
26 import logging
26 import logging
27
27
28 from pylons import tmpl_context as c, request, url
28 from pylons import tmpl_context as c, request, url
29 from pylons.i18n.translation import _
29
30
31 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.base import BaseRepoController, render
33 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.lib.helpers import RepoPage
34 from rhodecode.lib.helpers import RepoPage
33 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
34 from rhodecode.lib.utils2 import safe_int
36 from rhodecode.lib.utils2 import safe_int
37 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError, ChangesetError,\
38 RepositoryError
35
39
36 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
37
41
@@ -44,19 +48,53 b' class ShortlogController(BaseRepoControl'
44 def __before__(self):
48 def __before__(self):
45 super(ShortlogController, self).__before__()
49 super(ShortlogController, self).__before__()
46
50
47 def index(self, repo_name):
51 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
52 """
53 Safe way to get changeset if error occur it redirects to tip with
54 proper message
55
56 :param rev: revision to fetch
57 :param repo_name: repo name to redirect after
58 """
59
60 try:
61 return c.rhodecode_repo.get_changeset(rev)
62 except RepositoryError, e:
63 h.flash(str(e), category='warning')
64 redirect(h.url('shortlog_home', repo_name=repo_name))
65
66 def index(self, repo_name, revision=None, f_path=None):
48 p = safe_int(request.params.get('page', 1), 1)
67 p = safe_int(request.params.get('page', 1), 1)
49 size = safe_int(request.params.get('size', 20), 20)
68 size = safe_int(request.params.get('size', 20), 20)
50
69
51 def url_generator(**kw):
70 def url_generator(**kw):
52 return url('shortlog_home', repo_name=repo_name, size=size, **kw)
71 return url('shortlog_home', repo_name=repo_name, size=size, **kw)
53
72
54 c.repo_changesets = RepoPage(c.rhodecode_repo, page=p,
73 collection = c.rhodecode_repo
74 c.file_history = f_path
75 if f_path:
76 f_path = f_path.lstrip('/')
77 # get the history for the file !
78 tip_cs = c.rhodecode_repo.get_changeset()
79 try:
80 collection = tip_cs.get_file_history(f_path)
81 except (NodeDoesNotExistError, ChangesetError):
82 #this node is not present at tip !
83 try:
84 cs = self.__get_cs_or_redirect(revision, repo_name)
85 collection = cs.get_file_history(f_path)
86 except RepositoryError, e:
87 h.flash(str(e), category='warning')
88 redirect(h.url('shortlog_home', repo_name=repo_name))
89 collection = list(reversed(collection))
90
91 c.repo_changesets = RepoPage(collection, page=p,
55 items_per_page=size, url=url_generator)
92 items_per_page=size, url=url_generator)
56 page_revisions = [x.raw_id for x in list(c.repo_changesets)]
93 page_revisions = [x.raw_id for x in list(collection)]
57 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
94 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
58
95
59 if not c.repo_changesets:
96 if not c.repo_changesets:
97 h.flash(_('There are no changesets yet'), category='warning')
60 return redirect(url('summary_home', repo_name=repo_name))
98 return redirect(url('summary_home', repo_name=repo_name))
61
99
62 c.shortlog_data = render('shortlog/shortlog_data.html')
100 c.shortlog_data = render('shortlog/shortlog_data.html')
@@ -162,6 +162,13 b' class GitChangeset(BaseChangeset):'
162 elif isinstance(obj, objects.Tree):
162 elif isinstance(obj, objects.Tree):
163 return NodeKind.DIR
163 return NodeKind.DIR
164
164
165 def _get_filectx(self, path):
166 path = self._fix_path(path)
167 if self._get_kind(path) != NodeKind.FILE:
168 raise ChangesetError("File does not exist for revision %r at "
169 " %r" % (self.raw_id, path))
170 return path
171
165 def _get_file_nodes(self):
172 def _get_file_nodes(self):
166 return chain(*(t[2] for t in self.walk()))
173 return chain(*(t[2] for t in self.walk()))
167
174
@@ -264,6 +271,7 b' class GitChangeset(BaseChangeset):'
264 which is generally not good. Should be replaced with algorithm
271 which is generally not good. Should be replaced with algorithm
265 iterating commits.
272 iterating commits.
266 """
273 """
274 self._get_filectx(path)
267 cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % (
275 cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % (
268 self.id, path
276 self.id, path
269 )
277 )
@@ -181,7 +181,7 b' class MercurialChangeset(BaseChangeset):'
181 path = self._fix_path(path)
181 path = self._fix_path(path)
182 if self._get_kind(path) != NodeKind.FILE:
182 if self._get_kind(path) != NodeKind.FILE:
183 raise ChangesetError("File does not exist for revision %r at "
183 raise ChangesetError("File does not exist for revision %r at "
184 " %r" % (self.revision, path))
184 " %r" % (self.raw_id, path))
185 return self._ctx.filectx(path)
185 return self._ctx.filectx(path)
186
186
187 def _extract_submodules(self):
187 def _extract_submodules(self):
@@ -2650,15 +2650,14 b' h3.files_location {'
2650 #graph_content .container .mid .message a:hover{
2650 #graph_content .container .mid .message a:hover{
2651 text-decoration: none;
2651 text-decoration: none;
2652 }
2652 }
2653 #content #graph_content .message .revision-link,
2653
2654 #changeset_content .container .message .revision-link
2654 .revision-link
2655 {
2655 {
2656 color:#3F6F9F;
2656 color:#3F6F9F;
2657 font-weight: bold !important;
2657 font-weight: bold !important;
2658 }
2658 }
2659
2659
2660 #content #graph_content .message .issue-tracker-link,
2660 .issue-tracker-link{
2661 #changeset_content .container .message .issue-tracker-link{
2662 color:#3F6F9F;
2661 color:#3F6F9F;
2663 font-weight: bold !important;
2662 font-weight: bold !important;
2664 }
2663 }
@@ -8,6 +8,7 b''
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
11 ${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")}
11 ${h.hidden('annotate', c.annotate)}
12 ${h.hidden('annotate', c.annotate)}
12 ${h.end_form()}
13 ${h.end_form()}
13 </div>
14 </div>
@@ -11,7 +11,13 b''
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 %if c.file_history:
15 ${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
16 &raquo;
17 ${c.file_history}
18 %else:
14 ${_('shortlog')}
19 ${_('shortlog')}
20 %endif
15 </%def>
21 </%def>
16
22
17 <%def name="page_nav()">
23 <%def name="page_nav()">
@@ -30,9 +30,7 b''
30 </div>
30 </div>
31 </td>
31 </td>
32 <td>
32 <td>
33 ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
33 ${h.urlify_commit(h.truncate(cs.message,50),c.repo_name, h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
34 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
35 title=cs.message)}
36 </td>
34 </td>
37 <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
35 <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
38 ${h.age(cs.date)}</span>
36 ${h.age(cs.date)}</span>
@@ -1,8 +1,65 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3
3 class TestShortlogController(TestController):
4 class TestShortlogController(TestController):
4
5
5 def test_index(self):
6 def test_index_hg(self):
7 self.log_user()
8 response = self.app.get(url(controller='shortlog', action='index',
9 repo_name=HG_REPO))
10 # Test response...
11
12 def test_index_git(self):
13 self.log_user()
14 response = self.app.get(url(controller='shortlog', action='index',
15 repo_name=GIT_REPO))
16 # Test response...
17
18 def test_index_hg_with_filenode(self):
19 self.log_user()
20 response = self.app.get(url(controller='shortlog', action='index',
21 revision='tip', f_path='/vcs/exceptions.py',
22 repo_name=HG_REPO))
23 #history commits messages
24 response.mustcontain('Added exceptions module, this time for real')
25 response.mustcontain('Added not implemented hg backend test case')
26 response.mustcontain('Added BaseChangeset class')
27 # Test response...
28
29 def test_index_git_with_filenode(self):
6 self.log_user()
30 self.log_user()
7 response = self.app.get(url(controller='shortlog', action='index',repo_name=HG_REPO))
31 response = self.app.get(url(controller='shortlog', action='index',
8 # Test response...
32 revision='tip', f_path='/vcs/exceptions.py',
33 repo_name=GIT_REPO))
34 #history commits messages
35 response.mustcontain('Added exceptions module, this time for real')
36 response.mustcontain('Added not implemented hg backend test case')
37 response.mustcontain('Added BaseChangeset class')
38
39 def test_index_hg_with_filenode_that_is_dirnode(self):
40 self.log_user()
41 response = self.app.get(url(controller='shortlog', action='index',
42 revision='tip', f_path='/tests',
43 repo_name=HG_REPO))
44 self.assertEqual(response.status, '302 Found')
45
46 def test_index_git_with_filenode_that_is_dirnode(self):
47 self.log_user()
48 response = self.app.get(url(controller='shortlog', action='index',
49 revision='tip', f_path='/tests',
50 repo_name=GIT_REPO))
51 self.assertEqual(response.status, '302 Found')
52
53 def test_index_hg_with_filenode_not_existing(self):
54 self.log_user()
55 response = self.app.get(url(controller='shortlog', action='index',
56 revision='tip', f_path='/wrong_path',
57 repo_name=HG_REPO))
58 self.assertEqual(response.status, '302 Found')
59
60 def test_index_git_with_filenode_not_existing(self):
61 self.log_user()
62 response = self.app.get(url(controller='shortlog', action='index',
63 revision='tip', f_path='/wrong_path',
64 repo_name=GIT_REPO))
65 self.assertEqual(response.status, '302 Found')
General Comments 0
You need to be logged in to leave comments. Login now