##// END OF EJS Templates
changelog: don't load branches for parents as we don't need them for DAG graph.
marcink -
r3849:f735249e default
parent child Browse files
Show More
@@ -1,365 +1,365 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23
23
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from pyramid.renderers import render
26 from pyramid.renderers import render
27 from pyramid.response import Response
27 from pyramid.response import Response
28
28
29 from rhodecode.apps._base import RepoAppView
29 from rhodecode.apps._base import RepoAppView
30 import rhodecode.lib.helpers as h
30 import rhodecode.lib.helpers as h
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator)
32 LoginRequired, HasRepoPermissionAnyDecorator)
33
33
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.graphmod import _colored, _dagwalker
35 from rhodecode.lib.graphmod import _colored, _dagwalker
36 from rhodecode.lib.helpers import RepoPage
36 from rhodecode.lib.helpers import RepoPage
37 from rhodecode.lib.utils2 import safe_int, safe_str, str2bool
37 from rhodecode.lib.utils2 import safe_int, safe_str, str2bool
38 from rhodecode.lib.vcs.exceptions import (
38 from rhodecode.lib.vcs.exceptions import (
39 RepositoryError, CommitDoesNotExistError,
39 RepositoryError, CommitDoesNotExistError,
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 DEFAULT_CHANGELOG_SIZE = 20
44 DEFAULT_CHANGELOG_SIZE = 20
45
45
46
46
47 class RepoChangelogView(RepoAppView):
47 class RepoChangelogView(RepoAppView):
48
48
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
50 """
50 """
51 This is a safe way to get commit. If an error occurs it redirects to
51 This is a safe way to get commit. If an error occurs it redirects to
52 tip with proper message
52 tip with proper message
53
53
54 :param commit_id: id of commit to fetch
54 :param commit_id: id of commit to fetch
55 :param redirect_after: toggle redirection
55 :param redirect_after: toggle redirection
56 """
56 """
57 _ = self.request.translate
57 _ = self.request.translate
58
58
59 try:
59 try:
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
61 except EmptyRepositoryError:
61 except EmptyRepositoryError:
62 if not redirect_after:
62 if not redirect_after:
63 return None
63 return None
64
64
65 h.flash(h.literal(
65 h.flash(h.literal(
66 _('There are no commits yet')), category='warning')
66 _('There are no commits yet')), category='warning')
67 raise HTTPFound(
67 raise HTTPFound(
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
69
69
70 except (CommitDoesNotExistError, LookupError):
70 except (CommitDoesNotExistError, LookupError):
71 msg = _('No such commit exists for this repository')
71 msg = _('No such commit exists for this repository')
72 h.flash(msg, category='error')
72 h.flash(msg, category='error')
73 raise HTTPNotFound()
73 raise HTTPNotFound()
74 except RepositoryError as e:
74 except RepositoryError as e:
75 h.flash(safe_str(h.escape(e)), category='error')
75 h.flash(safe_str(h.escape(e)), category='error')
76 raise HTTPNotFound()
76 raise HTTPNotFound()
77
77
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
79 """
79 """
80 Generates a DAG graph for repo
80 Generates a DAG graph for repo
81
81
82 :param repo: repo instance
82 :param repo: repo instance
83 :param commits: list of commits
83 :param commits: list of commits
84 """
84 """
85 if not commits:
85 if not commits:
86 return json.dumps([]), json.dumps([])
86 return json.dumps([]), json.dumps([])
87
87
88 def serialize(commit, parents=True):
88 def serialize(commit, parents=True):
89 data = dict(
89 data = dict(
90 raw_id=commit.raw_id,
90 raw_id=commit.raw_id,
91 idx=commit.idx,
91 idx=commit.idx,
92 branch=h.escape(commit.branch),
92 branch=None,
93 )
93 )
94 if parents:
94 if parents:
95 data['parents'] = [
95 data['parents'] = [
96 serialize(x, parents=False) for x in commit.parents]
96 serialize(x, parents=False) for x in commit.parents]
97 return data
97 return data
98
98
99 prev_data = prev_data or []
99 prev_data = prev_data or []
100 next_data = next_data or []
100 next_data = next_data or []
101
101
102 current = [serialize(x) for x in commits]
102 current = [serialize(x) for x in commits]
103 commits = prev_data + current + next_data
103 commits = prev_data + current + next_data
104
104
105 dag = _dagwalker(repo, commits)
105 dag = _dagwalker(repo, commits)
106
106
107 data = [[commit_id, vtx, edges, branch]
107 data = [[commit_id, vtx, edges, branch]
108 for commit_id, vtx, edges, branch in _colored(dag)]
108 for commit_id, vtx, edges, branch in _colored(dag)]
109 return json.dumps(data), json.dumps(current)
109 return json.dumps(data), json.dumps(current)
110
110
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
113 h.flash('Branch {} is not found.'.format(h.escape(branch_name)),
113 h.flash('Branch {} is not found.'.format(h.escape(branch_name)),
114 category='warning')
114 category='warning')
115 redirect_url = h.route_path(
115 redirect_url = h.route_path(
116 'repo_commits_file', repo_name=repo_name,
116 'repo_commits_file', repo_name=repo_name,
117 commit_id=branch_name, f_path=f_path or '')
117 commit_id=branch_name, f_path=f_path or '')
118 raise HTTPFound(redirect_url)
118 raise HTTPFound(redirect_url)
119
119
120 def _load_changelog_data(
120 def _load_changelog_data(
121 self, c, collection, page, chunk_size, branch_name=None,
121 self, c, collection, page, chunk_size, branch_name=None,
122 dynamic=False, f_path=None, commit_id=None):
122 dynamic=False, f_path=None, commit_id=None):
123
123
124 def url_generator(**kw):
124 def url_generator(**kw):
125 query_params = {}
125 query_params = {}
126 query_params.update(kw)
126 query_params.update(kw)
127 if f_path:
127 if f_path:
128 # changelog for file
128 # changelog for file
129 return h.route_path(
129 return h.route_path(
130 'repo_commits_file',
130 'repo_commits_file',
131 repo_name=c.rhodecode_db_repo.repo_name,
131 repo_name=c.rhodecode_db_repo.repo_name,
132 commit_id=commit_id, f_path=f_path,
132 commit_id=commit_id, f_path=f_path,
133 _query=query_params)
133 _query=query_params)
134 else:
134 else:
135 return h.route_path(
135 return h.route_path(
136 'repo_commits',
136 'repo_commits',
137 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
137 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
138
138
139 c.total_cs = len(collection)
139 c.total_cs = len(collection)
140 c.showing_commits = min(chunk_size, c.total_cs)
140 c.showing_commits = min(chunk_size, c.total_cs)
141 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
141 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
142 items_per_page=chunk_size, branch=branch_name,
142 items_per_page=chunk_size, branch=branch_name,
143 url=url_generator)
143 url=url_generator)
144
144
145 c.next_page = c.pagination.next_page
145 c.next_page = c.pagination.next_page
146 c.prev_page = c.pagination.previous_page
146 c.prev_page = c.pagination.previous_page
147
147
148 if dynamic:
148 if dynamic:
149 if self.request.GET.get('chunk') != 'next':
149 if self.request.GET.get('chunk') != 'next':
150 c.next_page = None
150 c.next_page = None
151 if self.request.GET.get('chunk') != 'prev':
151 if self.request.GET.get('chunk') != 'prev':
152 c.prev_page = None
152 c.prev_page = None
153
153
154 page_commit_ids = [x.raw_id for x in c.pagination]
154 page_commit_ids = [x.raw_id for x in c.pagination]
155 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
155 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
156 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
156 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
157
157
158 def load_default_context(self):
158 def load_default_context(self):
159 c = self._get_local_tmpl_context(include_app_defaults=True)
159 c = self._get_local_tmpl_context(include_app_defaults=True)
160
160
161 c.rhodecode_repo = self.rhodecode_vcs_repo
161 c.rhodecode_repo = self.rhodecode_vcs_repo
162
162
163 return c
163 return c
164
164
165 def _get_preload_attrs(self):
165 def _get_preload_attrs(self):
166 pre_load = ['author', 'branch', 'date', 'message', 'parents',
166 pre_load = ['author', 'branch', 'date', 'message', 'parents',
167 'obsolete', 'phase', 'hidden']
167 'obsolete', 'phase', 'hidden']
168 return pre_load
168 return pre_load
169
169
170 @LoginRequired()
170 @LoginRequired()
171 @HasRepoPermissionAnyDecorator(
171 @HasRepoPermissionAnyDecorator(
172 'repository.read', 'repository.write', 'repository.admin')
172 'repository.read', 'repository.write', 'repository.admin')
173 @view_config(
173 @view_config(
174 route_name='repo_commits', request_method='GET',
174 route_name='repo_commits', request_method='GET',
175 renderer='rhodecode:templates/commits/changelog.mako')
175 renderer='rhodecode:templates/commits/changelog.mako')
176 @view_config(
176 @view_config(
177 route_name='repo_commits_file', request_method='GET',
177 route_name='repo_commits_file', request_method='GET',
178 renderer='rhodecode:templates/commits/changelog.mako')
178 renderer='rhodecode:templates/commits/changelog.mako')
179 # old routes for backward compat
179 # old routes for backward compat
180 @view_config(
180 @view_config(
181 route_name='repo_changelog', request_method='GET',
181 route_name='repo_changelog', request_method='GET',
182 renderer='rhodecode:templates/commits/changelog.mako')
182 renderer='rhodecode:templates/commits/changelog.mako')
183 @view_config(
183 @view_config(
184 route_name='repo_changelog_file', request_method='GET',
184 route_name='repo_changelog_file', request_method='GET',
185 renderer='rhodecode:templates/commits/changelog.mako')
185 renderer='rhodecode:templates/commits/changelog.mako')
186 def repo_changelog(self):
186 def repo_changelog(self):
187 c = self.load_default_context()
187 c = self.load_default_context()
188
188
189 commit_id = self.request.matchdict.get('commit_id')
189 commit_id = self.request.matchdict.get('commit_id')
190 f_path = self._get_f_path(self.request.matchdict)
190 f_path = self._get_f_path(self.request.matchdict)
191 show_hidden = str2bool(self.request.GET.get('evolve'))
191 show_hidden = str2bool(self.request.GET.get('evolve'))
192
192
193 chunk_size = 20
193 chunk_size = 20
194
194
195 c.branch_name = branch_name = self.request.GET.get('branch') or ''
195 c.branch_name = branch_name = self.request.GET.get('branch') or ''
196 c.book_name = book_name = self.request.GET.get('bookmark') or ''
196 c.book_name = book_name = self.request.GET.get('bookmark') or ''
197 c.f_path = f_path
197 c.f_path = f_path
198 c.commit_id = commit_id
198 c.commit_id = commit_id
199 c.show_hidden = show_hidden
199 c.show_hidden = show_hidden
200
200
201 hist_limit = safe_int(self.request.GET.get('limit')) or None
201 hist_limit = safe_int(self.request.GET.get('limit')) or None
202
202
203 p = safe_int(self.request.GET.get('page', 1), 1)
203 p = safe_int(self.request.GET.get('page', 1), 1)
204
204
205 c.selected_name = branch_name or book_name
205 c.selected_name = branch_name or book_name
206 if not commit_id and branch_name:
206 if not commit_id and branch_name:
207 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
207 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
208
208
209 c.changelog_for_path = f_path
209 c.changelog_for_path = f_path
210 pre_load = self._get_preload_attrs()
210 pre_load = self._get_preload_attrs()
211
211
212 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
212 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
213 try:
213 try:
214 if f_path:
214 if f_path:
215 log.debug('generating changelog for path %s', f_path)
215 log.debug('generating changelog for path %s', f_path)
216 # get the history for the file !
216 # get the history for the file !
217 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
217 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
218
218
219 try:
219 try:
220 collection = base_commit.get_path_history(
220 collection = base_commit.get_path_history(
221 f_path, limit=hist_limit, pre_load=pre_load)
221 f_path, limit=hist_limit, pre_load=pre_load)
222 if collection and partial_xhr:
222 if collection and partial_xhr:
223 # for ajax call we remove first one since we're looking
223 # for ajax call we remove first one since we're looking
224 # at it right now in the context of a file commit
224 # at it right now in the context of a file commit
225 collection.pop(0)
225 collection.pop(0)
226 except (NodeDoesNotExistError, CommitError):
226 except (NodeDoesNotExistError, CommitError):
227 # this node is not present at tip!
227 # this node is not present at tip!
228 try:
228 try:
229 commit = self._get_commit_or_redirect(commit_id)
229 commit = self._get_commit_or_redirect(commit_id)
230 collection = commit.get_path_history(f_path)
230 collection = commit.get_path_history(f_path)
231 except RepositoryError as e:
231 except RepositoryError as e:
232 h.flash(safe_str(e), category='warning')
232 h.flash(safe_str(e), category='warning')
233 redirect_url = h.route_path(
233 redirect_url = h.route_path(
234 'repo_commits', repo_name=self.db_repo_name)
234 'repo_commits', repo_name=self.db_repo_name)
235 raise HTTPFound(redirect_url)
235 raise HTTPFound(redirect_url)
236 collection = list(reversed(collection))
236 collection = list(reversed(collection))
237 else:
237 else:
238 collection = self.rhodecode_vcs_repo.get_commits(
238 collection = self.rhodecode_vcs_repo.get_commits(
239 branch_name=branch_name, show_hidden=show_hidden,
239 branch_name=branch_name, show_hidden=show_hidden,
240 pre_load=pre_load, translate_tags=False)
240 pre_load=pre_load, translate_tags=False)
241
241
242 self._load_changelog_data(
242 self._load_changelog_data(
243 c, collection, p, chunk_size, c.branch_name,
243 c, collection, p, chunk_size, c.branch_name,
244 f_path=f_path, commit_id=commit_id)
244 f_path=f_path, commit_id=commit_id)
245
245
246 except EmptyRepositoryError as e:
246 except EmptyRepositoryError as e:
247 h.flash(safe_str(h.escape(e)), category='warning')
247 h.flash(safe_str(h.escape(e)), category='warning')
248 raise HTTPFound(
248 raise HTTPFound(
249 h.route_path('repo_summary', repo_name=self.db_repo_name))
249 h.route_path('repo_summary', repo_name=self.db_repo_name))
250 except HTTPFound:
250 except HTTPFound:
251 raise
251 raise
252 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
252 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
253 log.exception(safe_str(e))
253 log.exception(safe_str(e))
254 h.flash(safe_str(h.escape(e)), category='error')
254 h.flash(safe_str(h.escape(e)), category='error')
255 raise HTTPFound(
255 raise HTTPFound(
256 h.route_path('repo_commits', repo_name=self.db_repo_name))
256 h.route_path('repo_commits', repo_name=self.db_repo_name))
257
257
258 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
258 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
259 # case when loading dynamic file history in file view
259 # case when loading dynamic file history in file view
260 # loading from ajax, we don't want the first result, it's popped
260 # loading from ajax, we don't want the first result, it's popped
261 # in the code above
261 # in the code above
262 html = render(
262 html = render(
263 'rhodecode:templates/commits/changelog_file_history.mako',
263 'rhodecode:templates/commits/changelog_file_history.mako',
264 self._get_template_context(c), self.request)
264 self._get_template_context(c), self.request)
265 return Response(html)
265 return Response(html)
266
266
267 commit_ids = []
267 commit_ids = []
268 if not f_path:
268 if not f_path:
269 # only load graph data when not in file history mode
269 # only load graph data when not in file history mode
270 commit_ids = c.pagination
270 commit_ids = c.pagination
271
271
272 c.graph_data, c.graph_commits = self._graph(
272 c.graph_data, c.graph_commits = self._graph(
273 self.rhodecode_vcs_repo, commit_ids)
273 self.rhodecode_vcs_repo, commit_ids)
274
274
275 return self._get_template_context(c)
275 return self._get_template_context(c)
276
276
277 @LoginRequired()
277 @LoginRequired()
278 @HasRepoPermissionAnyDecorator(
278 @HasRepoPermissionAnyDecorator(
279 'repository.read', 'repository.write', 'repository.admin')
279 'repository.read', 'repository.write', 'repository.admin')
280 @view_config(
280 @view_config(
281 route_name='repo_commits_elements', request_method=('GET', 'POST'),
281 route_name='repo_commits_elements', request_method=('GET', 'POST'),
282 renderer='rhodecode:templates/commits/changelog_elements.mako',
282 renderer='rhodecode:templates/commits/changelog_elements.mako',
283 xhr=True)
283 xhr=True)
284 @view_config(
284 @view_config(
285 route_name='repo_commits_elements_file', request_method=('GET', 'POST'),
285 route_name='repo_commits_elements_file', request_method=('GET', 'POST'),
286 renderer='rhodecode:templates/commits/changelog_elements.mako',
286 renderer='rhodecode:templates/commits/changelog_elements.mako',
287 xhr=True)
287 xhr=True)
288 def repo_commits_elements(self):
288 def repo_commits_elements(self):
289 c = self.load_default_context()
289 c = self.load_default_context()
290 commit_id = self.request.matchdict.get('commit_id')
290 commit_id = self.request.matchdict.get('commit_id')
291 f_path = self._get_f_path(self.request.matchdict)
291 f_path = self._get_f_path(self.request.matchdict)
292 show_hidden = str2bool(self.request.GET.get('evolve'))
292 show_hidden = str2bool(self.request.GET.get('evolve'))
293
293
294 chunk_size = 20
294 chunk_size = 20
295 hist_limit = safe_int(self.request.GET.get('limit')) or None
295 hist_limit = safe_int(self.request.GET.get('limit')) or None
296
296
297 def wrap_for_error(err):
297 def wrap_for_error(err):
298 html = '<tr>' \
298 html = '<tr>' \
299 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
299 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
300 '</tr>'.format(err)
300 '</tr>'.format(err)
301 return Response(html)
301 return Response(html)
302
302
303 c.branch_name = branch_name = self.request.GET.get('branch') or ''
303 c.branch_name = branch_name = self.request.GET.get('branch') or ''
304 c.book_name = book_name = self.request.GET.get('bookmark') or ''
304 c.book_name = book_name = self.request.GET.get('bookmark') or ''
305 c.f_path = f_path
305 c.f_path = f_path
306 c.commit_id = commit_id
306 c.commit_id = commit_id
307 c.show_hidden = show_hidden
307 c.show_hidden = show_hidden
308
308
309 c.selected_name = branch_name or book_name
309 c.selected_name = branch_name or book_name
310 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
310 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
311 return wrap_for_error(
311 return wrap_for_error(
312 safe_str('Branch: {} is not valid'.format(branch_name)))
312 safe_str('Branch: {} is not valid'.format(branch_name)))
313
313
314 pre_load = self._get_preload_attrs()
314 pre_load = self._get_preload_attrs()
315
315
316 if f_path:
316 if f_path:
317 try:
317 try:
318 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
318 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
319 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
319 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
320 log.exception(safe_str(e))
320 log.exception(safe_str(e))
321 raise HTTPFound(
321 raise HTTPFound(
322 h.route_path('repo_commits', repo_name=self.db_repo_name))
322 h.route_path('repo_commits', repo_name=self.db_repo_name))
323
323
324 collection = base_commit.get_path_history(
324 collection = base_commit.get_path_history(
325 f_path, limit=hist_limit, pre_load=pre_load)
325 f_path, limit=hist_limit, pre_load=pre_load)
326 collection = list(reversed(collection))
326 collection = list(reversed(collection))
327 else:
327 else:
328 collection = self.rhodecode_vcs_repo.get_commits(
328 collection = self.rhodecode_vcs_repo.get_commits(
329 branch_name=branch_name, show_hidden=show_hidden, pre_load=pre_load,
329 branch_name=branch_name, show_hidden=show_hidden, pre_load=pre_load,
330 translate_tags=False)
330 translate_tags=False)
331
331
332 p = safe_int(self.request.GET.get('page', 1), 1)
332 p = safe_int(self.request.GET.get('page', 1), 1)
333 try:
333 try:
334 self._load_changelog_data(
334 self._load_changelog_data(
335 c, collection, p, chunk_size, dynamic=True,
335 c, collection, p, chunk_size, dynamic=True,
336 f_path=f_path, commit_id=commit_id)
336 f_path=f_path, commit_id=commit_id)
337 except EmptyRepositoryError as e:
337 except EmptyRepositoryError as e:
338 return wrap_for_error(safe_str(e))
338 return wrap_for_error(safe_str(e))
339 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
339 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
340 log.exception('Failed to fetch commits')
340 log.exception('Failed to fetch commits')
341 return wrap_for_error(safe_str(e))
341 return wrap_for_error(safe_str(e))
342
342
343 prev_data = None
343 prev_data = None
344 next_data = None
344 next_data = None
345
345
346 try:
346 try:
347 prev_graph = json.loads(self.request.POST.get('graph') or '{}')
347 prev_graph = json.loads(self.request.POST.get('graph') or '{}')
348 except json.JSONDecodeError:
348 except json.JSONDecodeError:
349 prev_graph = {}
349 prev_graph = {}
350
350
351 if self.request.GET.get('chunk') == 'prev':
351 if self.request.GET.get('chunk') == 'prev':
352 next_data = prev_graph
352 next_data = prev_graph
353 elif self.request.GET.get('chunk') == 'next':
353 elif self.request.GET.get('chunk') == 'next':
354 prev_data = prev_graph
354 prev_data = prev_graph
355
355
356 commit_ids = []
356 commit_ids = []
357 if not f_path:
357 if not f_path:
358 # only load graph data when not in file history mode
358 # only load graph data when not in file history mode
359 commit_ids = c.pagination
359 commit_ids = c.pagination
360
360
361 c.graph_data, c.graph_commits = self._graph(
361 c.graph_data, c.graph_commits = self._graph(
362 self.rhodecode_vcs_repo, commit_ids,
362 self.rhodecode_vcs_repo, commit_ids,
363 prev_data=prev_data, next_data=next_data)
363 prev_data=prev_data, next_data=next_data)
364
364
365 return self._get_template_context(c)
365 return self._get_template_context(c)
@@ -1,211 +1,211 b''
1 // # Copyright (C) 2016-2019 RhodeCode GmbH
1 // # Copyright (C) 2016-2019 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var CommitsController = function () {
20 var CommitsController = function () {
21 var self = this;
21 var self = this;
22 this.$graphCanvas = $('#graph_canvas');
22 this.$graphCanvas = $('#graph_canvas');
23 this.$commitCounter = $('#commit-counter');
23 this.$commitCounter = $('#commit-counter');
24
24
25 this.getCurrentGraphData = function () {
25 this.getCurrentGraphData = function () {
26 // raw form
26 // raw form
27 return self.$graphCanvas.data('commits');
27 return self.$graphCanvas.data('commits');
28 };
28 };
29
29
30 this.setLabelText = function (graphData) {
30 this.setLabelText = function (graphData) {
31 var shown = $('.commit_hash').length;
31 var shown = $('.commit_hash').length;
32 var total = self.$commitCounter.data('total');
32 var total = self.$commitCounter.data('total');
33
33
34 if (shown == 1) {
34 if (shown == 1) {
35 var text = _gettext('showing {0} out of {1} commit').format(shown, total);
35 var text = _gettext('showing {0} out of {1} commit').format(shown, total);
36 } else {
36 } else {
37 var text = _gettext('showing {0} out of {1} commits').format(shown, total);
37 var text = _gettext('showing {0} out of {1} commits').format(shown, total);
38 }
38 }
39 self.$commitCounter.html(text)
39 self.$commitCounter.html(text)
40 };
40 };
41
41
42 this.reloadGraph = function (chunk) {
42 this.reloadGraph = function (chunk) {
43 chunk = chunk || 'next';
43 chunk = chunk || 'next';
44
44
45 // reset state on re-render !
45 // reset state on re-render !
46 self.$graphCanvas.html('');
46 self.$graphCanvas.html('');
47
47
48 var edgeData = $("[data-graph]").data('graph') || this.$graphCanvas.data('graph') || [];
48 var edgeData = $("[data-graph]").data('graph') || this.$graphCanvas.data('graph') || [];
49 var prev_link = $('.load-more-commits').find('.prev-commits').get(0);
49 var prev_link = $('.load-more-commits').find('.prev-commits').get(0);
50 var next_link = $('.load-more-commits').find('.next-commits').get(0);
50 var next_link = $('.load-more-commits').find('.next-commits').get(0);
51
51
52 // Determine max number of edges per row in graph
52 // Determine max number of edges per row in graph
53 var edgeCount = 1;
53 var edgeCount = 1;
54 $.each(edgeData, function (i, item) {
54 $.each(edgeData, function (i, item) {
55 $.each(item[2], function (key, value) {
55 $.each(item[2], function (key, value) {
56 if (value[1] > edgeCount) {
56 if (value[1] > edgeCount) {
57 edgeCount = value[1];
57 edgeCount = value[1];
58 }
58 }
59 });
59 });
60 });
60 });
61
61
62 if (prev_link && next_link) {
62 if (prev_link && next_link) {
63 var graph_padding = -64;
63 var graph_padding = -64;
64 }
64 }
65 else if (next_link) {
65 else if (next_link) {
66 var graph_padding = -32;
66 var graph_padding = -32;
67 } else {
67 } else {
68 var graph_padding = 0;
68 var graph_padding = 0;
69 }
69 }
70
70
71 var x_step = Math.min(10, Math.floor(86 / edgeCount));
71 var x_step = Math.min(10, Math.floor(86 / edgeCount));
72 var height = $('#changesets').find('.commits-range').height() + graph_padding;
72 var height = $('#changesets').find('.commits-range').height() + graph_padding;
73 var graph_options = {
73 var graph_options = {
74 width: 100,
74 width: 100,
75 height: height,
75 height: height,
76 x_step: x_step,
76 x_step: x_step,
77 y_step: 42,
77 y_step: 42,
78 dotRadius: 3.5,
78 dotRadius: 3.5,
79 lineWidth: 2.5
79 lineWidth: 2.5
80 };
80 };
81
81
82 var prevCommitsData = this.$graphCanvas.data('commits') || [];
82 var prevCommitsData = this.$graphCanvas.data('commits') || [];
83 var nextCommitsData = $("[data-graph]").data('commits') || [];
83 var nextCommitsData = $("[data-graph]").data('commits') || [];
84
84
85 if (chunk == 'next') {
85 if (chunk == 'next') {
86 var commitData = $.merge(prevCommitsData, nextCommitsData);
86 var commitData = $.merge(prevCommitsData, nextCommitsData);
87 } else {
87 } else {
88 var commitData = $.merge(nextCommitsData, prevCommitsData);
88 var commitData = $.merge(nextCommitsData, prevCommitsData);
89 }
89 }
90
90
91 this.$graphCanvas.data('graph', edgeData);
91 this.$graphCanvas.data('graph', edgeData);
92 this.$graphCanvas.data('commits', commitData);
92 this.$graphCanvas.data('commits', commitData);
93
93
94 // destroy dynamic loaded graph
94 // destroy dynamic loaded graph
95 $("[data-graph]").remove();
95 $("[data-graph]").remove();
96
96
97 this.$graphCanvas.commits(graph_options);
97 this.$graphCanvas.commits(graph_options);
98
98
99 this.setLabelText(edgeData);
99 this.setLabelText(edgeData);
100
100
101 var padding = 90;
101 var padding = 90;
102 if (prev_link) {
102 if (prev_link) {
103 padding += 34;
103 padding += 34;
104
104
105 }
105 }
106 $('#graph_nodes').css({'padding-top': padding});
106 $('#graph_nodes').css({'padding-top': padding});
107
107
108 $.each($('.message.truncate'), function(idx, value) {
108 $.each($('.message.truncate'), function(idx, value) {
109 if(!(value.offsetWidth < value.scrollWidth)){
109 if(!(value.offsetWidth < value.scrollWidth)){
110 var expandTd = $(this).closest('td').siblings('.expand_commit');
110 var expandTd = $(this).closest('td').siblings('.expand_commit');
111 expandTd.find('i').hide();
111 expandTd.find('i').hide();
112 expandTd.removeAttr('title');
112 expandTd.removeAttr('title');
113 expandTd.removeClass('expand_commit');
113 expandTd.removeClass('expand_commit');
114 }
114 }
115 });
115 });
116
116
117 };
117 };
118
118
119 this.getChunkUrl = function (page, chunk, branch, commit_id, f_path) {
119 this.getChunkUrl = function (page, chunk, branch, commit_id, f_path) {
120 var urlData = {
120 var urlData = {
121 'repo_name': templateContext.repo_name,
121 'repo_name': templateContext.repo_name,
122 'page': page,
122 'page': page,
123 'chunk': chunk
123 'chunk': chunk
124 };
124 };
125
125
126 if (branch !== undefined && branch !== '') {
126 if (branch !== undefined && branch !== '') {
127 urlData['branch'] = branch;
127 urlData['branch'] = branch;
128 }
128 }
129 if (commit_id !== undefined && commit_id !== '') {
129 if (commit_id !== undefined && commit_id !== '') {
130 urlData['commit_id'] = commit_id;
130 urlData['commit_id'] = commit_id;
131 }
131 }
132 if (f_path !== undefined && f_path !== '') {
132 if (f_path !== undefined && f_path !== '') {
133 urlData['f_path'] = f_path;
133 urlData['f_path'] = f_path;
134 }
134 }
135
135
136 if (urlData['commit_id'] && urlData['f_path']) {
136 if (urlData['commit_id'] && urlData['f_path']) {
137 return pyroutes.url('repo_commits_elements_file', urlData);
137 return pyroutes.url('repo_commits_elements_file', urlData);
138 }
138 }
139 else {
139 else {
140 return pyroutes.url('repo_commits_elements', urlData);
140 return pyroutes.url('repo_commits_elements', urlData);
141 }
141 }
142
142
143 };
143 };
144
144
145 this.loadNext = function (node, page, branch, commit_id, f_path) {
145 this.loadNext = function (node, page, branch, commit_id, f_path) {
146 var loadUrl = this.getChunkUrl(page, 'next', branch, commit_id, f_path);
146 var loadUrl = this.getChunkUrl(page, 'next', branch, commit_id, f_path);
147 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
147 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
148
148 $(node).html('loading...').addClass('disabled').css({'cursor':'default'});
149 $.post(loadUrl, postData, function (data) {
149 $.post(loadUrl, postData, function (data) {
150 $(node).closest('tbody').append(data);
150 $(node).closest('tbody').append(data);
151 $(node).closest('td').remove();
151 $(node).closest('td').remove();
152 self.reloadGraph('next');
152 self.reloadGraph('next');
153 })
153 })
154 };
154 };
155
155
156 this.loadPrev = function (node, page, branch, commit_id, f_path) {
156 this.loadPrev = function (node, page, branch, commit_id, f_path) {
157 var loadUrl = this.getChunkUrl(page, 'prev', branch, commit_id, f_path);
157 var loadUrl = this.getChunkUrl(page, 'prev', branch, commit_id, f_path);
158 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
158 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
159
159 $(node).html('loading...').addClass('disabled').css({'cursor':'default'});
160 $.post(loadUrl, postData, function (data) {
160 $.post(loadUrl, postData, function (data) {
161 $(node).closest('tbody').prepend(data);
161 $(node).closest('tbody').prepend(data);
162 $(node).closest('td').remove();
162 $(node).closest('td').remove();
163 self.reloadGraph('prev');
163 self.reloadGraph('prev');
164 })
164 })
165 };
165 };
166
166
167 this.expandCommit = function (node, reloadGraph) {
167 this.expandCommit = function (node, reloadGraph) {
168 reloadGraph = reloadGraph || false;
168 reloadGraph = reloadGraph || false;
169
169
170 var target_expand = $(node);
170 var target_expand = $(node);
171 var cid = target_expand.data('commitId');
171 var cid = target_expand.data('commitId');
172
172
173 if (target_expand.hasClass('open')) {
173 if (target_expand.hasClass('open')) {
174 $('#c-' + cid).css({
174 $('#c-' + cid).css({
175 'height': '1.5em',
175 'height': '1.5em',
176 'white-space': 'nowrap',
176 'white-space': 'nowrap',
177 'text-overflow': 'ellipsis',
177 'text-overflow': 'ellipsis',
178 'overflow': 'hidden'
178 'overflow': 'hidden'
179 });
179 });
180 $('#t-' + cid).css({
180 $('#t-' + cid).css({
181 'height': 'auto',
181 'height': 'auto',
182 'line-height': '.9em',
182 'line-height': '.9em',
183 'text-overflow': 'ellipsis',
183 'text-overflow': 'ellipsis',
184 'overflow': 'hidden',
184 'overflow': 'hidden',
185 'white-space': 'nowrap'
185 'white-space': 'nowrap'
186 });
186 });
187 target_expand.removeClass('open');
187 target_expand.removeClass('open');
188 }
188 }
189 else {
189 else {
190 $('#c-' + cid).css({
190 $('#c-' + cid).css({
191 'height': 'auto',
191 'height': 'auto',
192 'white-space': 'pre-line',
192 'white-space': 'pre-line',
193 'text-overflow': 'initial',
193 'text-overflow': 'initial',
194 'overflow': 'visible'
194 'overflow': 'visible'
195 });
195 });
196 $('#t-' + cid).css({
196 $('#t-' + cid).css({
197 'height': 'auto',
197 'height': 'auto',
198 'max-height': 'none',
198 'max-height': 'none',
199 'text-overflow': 'initial',
199 'text-overflow': 'initial',
200 'overflow': 'visible',
200 'overflow': 'visible',
201 'white-space': 'normal'
201 'white-space': 'normal'
202 });
202 });
203 target_expand.addClass('open');
203 target_expand.addClass('open');
204 }
204 }
205
205
206 if (reloadGraph) {
206 if (reloadGraph) {
207 // redraw the graph
207 // redraw the graph
208 self.reloadGraph();
208 self.reloadGraph();
209 }
209 }
210 }
210 }
211 };
211 };
General Comments 0
You need to be logged in to leave comments. Login now