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 |
|
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 |
|
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.r |
|
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 | » |
|
11 | » | |
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 | » |
|
13 | » | |
|
14 | %if c.file_history: | |||
|
15 | ${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))} | |||
|
16 | » | |||
|
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', |
|
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