##// END OF EJS Templates
Optimized look of compare view of changesets....
marcink -
r3011:25d77aef beta
parent child Browse files
Show More
@@ -1,153 +1,161 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.compare
3 rhodecode.controllers.compare
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 compare controller for pylons showoing differences between two
6 compare controller for pylons showoing differences between two
7 repos, branches, bookmarks or tips
7 repos, branches, bookmarks or tips
8
8
9 :created_on: May 6, 2012
9 :created_on: May 6, 2012
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from webob.exc import HTTPNotFound
29 from webob.exc import HTTPNotFound
30 from pylons import request, response, session, tmpl_context as c, url
30 from pylons import request, response, session, tmpl_context as c, url
31 from pylons.controllers.util import abort, redirect
31 from pylons.controllers.util import abort, redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
34 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib import diffs
38 from rhodecode.lib import diffs
39
39
40 from rhodecode.model.db import Repository
40 from rhodecode.model.db import Repository
41 from rhodecode.model.pull_request import PullRequestModel
41 from rhodecode.model.pull_request import PullRequestModel
42 from webob.exc import HTTPBadRequest
42 from webob.exc import HTTPBadRequest
43 from rhodecode.lib.utils2 import str2bool
43 from rhodecode.lib.utils2 import str2bool
44 from rhodecode.lib.diffs import LimitedDiffContainer
44
45
45 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
46
47
47
48
48 class CompareController(BaseRepoController):
49 class CompareController(BaseRepoController):
49
50
50 @LoginRequired()
51 @LoginRequired()
51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 'repository.admin')
53 'repository.admin')
53 def __before__(self):
54 def __before__(self):
54 super(CompareController, self).__before__()
55 super(CompareController, self).__before__()
55
56
56 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
57 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
57 partial=False):
58 partial=False):
58 """
59 """
59 Safe way to get changeset if error occur it redirects to changeset with
60 Safe way to get changeset if error occur it redirects to changeset with
60 proper message. If partial is set then don't do redirect raise Exception
61 proper message. If partial is set then don't do redirect raise Exception
61 instead
62 instead
62
63
63 :param rev: revision to fetch
64 :param rev: revision to fetch
64 :param repo: repo instance
65 :param repo: repo instance
65 """
66 """
66
67
67 try:
68 try:
68 type_, rev = rev
69 type_, rev = rev
69 return repo.scm_instance.get_changeset(rev)
70 return repo.scm_instance.get_changeset(rev)
70 except EmptyRepositoryError, e:
71 except EmptyRepositoryError, e:
71 if not redirect_after:
72 if not redirect_after:
72 return None
73 return None
73 h.flash(h.literal(_('There are no changesets yet')),
74 h.flash(h.literal(_('There are no changesets yet')),
74 category='warning')
75 category='warning')
75 redirect(url('summary_home', repo_name=repo.repo_name))
76 redirect(url('summary_home', repo_name=repo.repo_name))
76
77
77 except RepositoryError, e:
78 except RepositoryError, e:
78 log.error(traceback.format_exc())
79 log.error(traceback.format_exc())
79 h.flash(str(e), category='warning')
80 h.flash(str(e), category='warning')
80 if not partial:
81 if not partial:
81 redirect(h.url('summary_home', repo_name=repo.repo_name))
82 redirect(h.url('summary_home', repo_name=repo.repo_name))
82 raise HTTPBadRequest()
83 raise HTTPBadRequest()
83
84
84 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
85 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
85
86
86 org_repo = c.rhodecode_db_repo.repo_name
87 org_repo = c.rhodecode_db_repo.repo_name
87 org_ref = (org_ref_type, org_ref)
88 org_ref = (org_ref_type, org_ref)
88 other_ref = (other_ref_type, other_ref)
89 other_ref = (other_ref_type, other_ref)
89 other_repo = request.GET.get('repo', org_repo)
90 other_repo = request.GET.get('repo', org_repo)
90 bundle_compare = str2bool(request.GET.get('bundle', True))
91 bundle_compare = str2bool(request.GET.get('bundle', True))
92 c.fulldiff = fulldiff = request.GET.get('fulldiff')
91
93
92 c.swap_url = h.url('compare_url', repo_name=other_repo,
94 c.swap_url = h.url('compare_url', repo_name=other_repo,
93 org_ref_type=other_ref[0], org_ref=other_ref[1],
95 org_ref_type=other_ref[0], org_ref=other_ref[1],
94 other_ref_type=org_ref[0], other_ref=org_ref[1],
96 other_ref_type=org_ref[0], other_ref=org_ref[1],
95 repo=org_repo, as_form=request.GET.get('as_form'),
97 repo=org_repo, as_form=request.GET.get('as_form'),
96 bundle=bundle_compare)
98 bundle=bundle_compare)
97
99
98 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
100 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
99 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
101 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
100
102
101 if c.org_repo is None or c.other_repo is None:
103 if c.org_repo is None or c.other_repo is None:
102 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
104 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
103 raise HTTPNotFound
105 raise HTTPNotFound
104
106
105 if c.org_repo != c.other_repo and h.is_git(c.rhodecode_repo):
107 if c.org_repo != c.other_repo and h.is_git(c.rhodecode_repo):
106 log.error('compare of two remote repos not available for GIT REPOS')
108 log.error('compare of two remote repos not available for GIT REPOS')
107 raise HTTPNotFound
109 raise HTTPNotFound
108
110
109 if c.org_repo.scm_instance.alias != c.other_repo.scm_instance.alias:
111 if c.org_repo.scm_instance.alias != c.other_repo.scm_instance.alias:
110 log.error('compare of two different kind of remote repos not available')
112 log.error('compare of two different kind of remote repos not available')
111 raise HTTPNotFound
113 raise HTTPNotFound
112
114
113 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
115 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
114 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
116 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
115 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
117 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
116
118
117 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
119 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
118 org_repo, org_ref, other_repo, other_ref
120 org_repo, org_ref, other_repo, other_ref
119 )
121 )
120
122
121 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
123 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
122 c.cs_ranges])
124 c.cs_ranges])
123 c.target_repo = c.repo_name
125 c.target_repo = c.repo_name
124 # defines that we need hidden inputs with changesets
126 # defines that we need hidden inputs with changesets
125 c.as_form = request.GET.get('as_form', False)
127 c.as_form = request.GET.get('as_form', False)
126 if partial:
128 if partial:
127 return render('compare/compare_cs.html')
129 return render('compare/compare_cs.html')
128
130
129 if not bundle_compare and c.cs_ranges:
131 if not bundle_compare and c.cs_ranges:
130 # case we want a simple diff without incoming changesets, just
132 # case we want a simple diff without incoming changesets, just
131 # for review purposes. Make the diff on the forked repo, with
133 # for review purposes. Make the diff on the forked repo, with
132 # revision that is common ancestor
134 # revision that is common ancestor
133 other_ref = ('rev', c.cs_ranges[-1].parents[0].raw_id)
135 other_ref = ('rev', c.cs_ranges[-1].parents[0].raw_id)
134 other_repo = org_repo
136 other_repo = org_repo
135
137
136 c.org_ref = org_ref[1]
138 c.org_ref = org_ref[1]
137 c.other_ref = other_ref[1]
139 c.other_ref = other_ref[1]
138
140
139 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
141 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
140 discovery_data, bundle_compare=bundle_compare)
142 discovery_data, bundle_compare=bundle_compare)
141 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
143 diff_limit = self.cut_off_limit if not fulldiff else None
144 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff',
145 diff_limit=diff_limit)
142 _parsed = diff_processor.prepare()
146 _parsed = diff_processor.prepare()
143
147
148 c.limited_diff = False
149 if isinstance(_parsed, LimitedDiffContainer):
150 c.limited_diff = True
151
144 c.files = []
152 c.files = []
145 c.changes = {}
153 c.changes = {}
146
154
147 for f in _parsed:
155 for f in _parsed:
148 fid = h.FID('', f['filename'])
156 fid = h.FID('', f['filename'])
149 c.files.append([fid, f['operation'], f['filename'], f['stats']])
157 c.files.append([fid, f['operation'], f['filename'], f['stats']])
150 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
158 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
151 c.changes[fid] = [f['operation'], f['filename'], diff]
159 c.changes[fid] = [f['operation'], f['filename'], diff]
152
160
153 return render('compare/compare_diff.html')
161 return render('compare/compare_diff.html')
@@ -1,27 +1,28 b''
1 ## Changesets table !
1 ## Changesets table !
2 <div class="container">
2 <div class="container">
3 <table class="compare_view_commits noborder">
3 <table class="compare_view_commits noborder">
4 %if not c.cs_ranges:
4 %if not c.cs_ranges:
5 <tr><td>${_('No changesets')}</td></tr>
5 <tr><td>${_('No changesets')}</td></tr>
6 %else:
6 %else:
7 %for cnt, cs in enumerate(c.cs_ranges):
7 %for cnt, cs in enumerate(c.cs_ranges):
8 <tr>
8 <tr>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
10 <td>
10 <td>
11 %if cs.raw_id in c.statuses:
11 %if cs.raw_id in c.statuses:
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
13 %endif
13 %endif
14 </td>
14 </td>
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.target_repo,revision=cs.raw_id))}
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.target_repo,revision=cs.raw_id))}
16 %if c.as_form:
16 %if c.as_form:
17 ${h.hidden('revisions',cs.raw_id)}
17 ${h.hidden('revisions',cs.raw_id)}
18 %endif
18 %endif
19 </td>
19 </td>
20 <td><div class="author">${h.person(cs.author)}</div></td>
20 <td><div class="author">${h.person(cs.author)}</div></td>
21 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
21 <td><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td>
22 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
22 <td><div class="message tooltip" title="${h.tooltip(cs.message)}" style="white-space:normal">${h.urlify_commit(h.shorter(cs.message, 60),c.repo_name)}</div></td>
23 </tr>
23 </tr>
24 %endfor
24 %endfor
25
25 %endif
26 %endif
26 </table>
27 </table>
27 </div>
28 </div>
@@ -1,77 +1,82 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_(u'Home'),h.url('/'))}
9 ${h.link_to(_(u'Home'),h.url('/'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
12 &raquo;
13 ${_('Compare')}
13 ${_('Compare')}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('changelog')}
17 ${self.menu('changelog')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <div class="table">
26 <div class="table">
27 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
28 <div class="code-header cv">
28 <div class="code-header cv">
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 <div>
30 <div>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div id="changeset_compare_view_content">
35 <div id="changeset_compare_view_content">
36 ##CS
36 ##CS
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Outgoing changesets')}</div>
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Outgoing changesets')}</div>
38 <%include file="compare_cs.html" />
38 <%include file="compare_cs.html" />
39
39
40 ## FILES
40 ## FILES
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
42 <div class="cs_files">
42 <div class="cs_files">
43 %for fid, change, f, stat in c.files:
43 %for fid, change, f, stat in c.files:
44 <div class="cs_${change}">
44 <div class="cs_${change}">
45 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
45 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
46 <div class="changes">${h.fancy_file_stats(stat)}</div>
46 <div class="changes">${h.fancy_file_stats(stat)}</div>
47 </div>
47 </div>
48 %endfor
48 %endfor
49 </div>
49 </div>
50 % if c.limited_diff:
51 <h5>${_('Changeset was too big and was cut off...')}</h5>
52 % endif
50 </div>
53 </div>
51 </div>
54 </div>
52
55
53 ## diff block
56 ## diff block
54 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
57 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
55 %for fid, change, f, stat in c.files:
58 %for fid, change, f, stat in c.files:
56 ${diff_block.diff_block_simple([c.changes[fid]])}
59 ${diff_block.diff_block_simple([c.changes[fid]])}
57 %endfor
60 %endfor
58
61 % if c.limited_diff:
62 <h4>${_('Changeset was too big and was cut off...')}</h4>
63 % endif
59 <script type="text/javascript">
64 <script type="text/javascript">
60
65
61 YUE.onDOMReady(function(){
66 YUE.onDOMReady(function(){
62
67
63 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
68 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
64 var act = e.currentTarget.nextElementSibling;
69 var act = e.currentTarget.nextElementSibling;
65
70
66 if(YUD.hasClass(act,'active')){
71 if(YUD.hasClass(act,'active')){
67 YUD.removeClass(act,'active');
72 YUD.removeClass(act,'active');
68 YUD.setStyle(act,'display','none');
73 YUD.setStyle(act,'display','none');
69 }else{
74 }else{
70 YUD.addClass(act,'active');
75 YUD.addClass(act,'active');
71 YUD.setStyle(act,'display','');
76 YUD.setStyle(act,'display','');
72 }
77 }
73 });
78 });
74 })
79 })
75 </script>
80 </script>
76 </div>
81 </div>
77 </%def>
82 </%def>
General Comments 0
You need to be logged in to leave comments. Login now