Show More
@@ -33,6 +33,7 b' from rhodecode.model import user_group' | |||||
33 | from rhodecode.model import user |
|
33 | from rhodecode.model import user | |
34 | from rhodecode.model.db import User |
|
34 | from rhodecode.model.db import User | |
35 | from rhodecode.model.scm import ScmModel |
|
35 | from rhodecode.model.scm import ScmModel | |
|
36 | from rhodecode.model.settings import VcsSettingsModel | |||
36 |
|
37 | |||
37 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
38 |
|
39 | |||
@@ -256,6 +257,11 b' class RepoAppView(BaseAppView):' | |||||
256 | f_path_match = self._get_f_path_unchecked(matchdict, default) |
|
257 | f_path_match = self._get_f_path_unchecked(matchdict, default) | |
257 | return self.path_filter.assert_path_permissions(f_path_match) |
|
258 | return self.path_filter.assert_path_permissions(f_path_match) | |
258 |
|
259 | |||
|
260 | def _get_general_setting(self, target_repo, settings_key, default=False): | |||
|
261 | settings_model = VcsSettingsModel(repo=target_repo) | |||
|
262 | settings = settings_model.get_general_settings() | |||
|
263 | return settings.get(settings_key, default) | |||
|
264 | ||||
259 |
|
265 | |||
260 | class PathFilter(object): |
|
266 | class PathFilter(object): | |
261 |
|
267 |
@@ -34,17 +34,18 b' from rhodecode.lib.auth import (' | |||||
34 | LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired) |
|
34 | LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired) | |
35 |
|
35 | |||
36 | from rhodecode.lib.compat import OrderedDict |
|
36 | from rhodecode.lib.compat import OrderedDict | |
|
37 | from rhodecode.lib.diffs import cache_diff, load_cached_diff, diff_cache_exist | |||
37 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError |
|
38 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError | |
38 | import rhodecode.lib.helpers as h |
|
39 | import rhodecode.lib.helpers as h | |
39 | from rhodecode.lib.utils2 import safe_unicode |
|
40 | from rhodecode.lib.utils2 import safe_unicode, str2bool | |
40 | from rhodecode.lib.vcs.backends.base import EmptyCommit |
|
41 | from rhodecode.lib.vcs.backends.base import EmptyCommit | |
41 | from rhodecode.lib.vcs.exceptions import ( |
|
42 | from rhodecode.lib.vcs.exceptions import ( | |
42 |
RepositoryError, CommitDoesNotExistError |
|
43 | RepositoryError, CommitDoesNotExistError) | |
43 | from rhodecode.model.db import ChangesetComment, ChangesetStatus |
|
44 | from rhodecode.model.db import ChangesetComment, ChangesetStatus | |
44 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
45 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
45 | from rhodecode.model.comment import CommentsModel |
|
46 | from rhodecode.model.comment import CommentsModel | |
46 | from rhodecode.model.meta import Session |
|
47 | from rhodecode.model.meta import Session | |
47 |
|
48 | from rhodecode.model.settings import VcsSettingsModel | ||
48 |
|
49 | |||
49 | log = logging.getLogger(__name__) |
|
50 | log = logging.getLogger(__name__) | |
50 |
|
51 | |||
@@ -152,6 +153,12 b' class RepoCommitsView(RepoAppView):' | |||||
152 |
|
153 | |||
153 | return c |
|
154 | return c | |
154 |
|
155 | |||
|
156 | def _is_diff_cache_enabled(self, target_repo): | |||
|
157 | caching_enabled = self._get_general_setting( | |||
|
158 | target_repo, 'rhodecode_diff_cache') | |||
|
159 | log.debug('Diff caching enabled: %s', caching_enabled) | |||
|
160 | return caching_enabled | |||
|
161 | ||||
155 | def _commit(self, commit_id_range, method): |
|
162 | def _commit(self, commit_id_range, method): | |
156 | _ = self.request.translate |
|
163 | _ = self.request.translate | |
157 | c = self.load_default_context() |
|
164 | c = self.load_default_context() | |
@@ -240,43 +247,63 b' class RepoCommitsView(RepoAppView):' | |||||
240 | commit2 = commit |
|
247 | commit2 = commit | |
241 | commit1 = commit.parents[0] if commit.parents else EmptyCommit() |
|
248 | commit1 = commit.parents[0] if commit.parents else EmptyCommit() | |
242 |
|
249 | |||
|
250 | if method == 'show': | |||
|
251 | inline_comments = CommentsModel().get_inline_comments( | |||
|
252 | self.db_repo.repo_id, revision=commit.raw_id) | |||
|
253 | c.inline_cnt = CommentsModel().get_inline_comments_count( | |||
|
254 | inline_comments) | |||
|
255 | c.inline_comments = inline_comments | |||
|
256 | ||||
|
257 | cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path( | |||
|
258 | self.db_repo) | |||
|
259 | cache_file_path = diff_cache_exist( | |||
|
260 | cache_path, 'diff', commit.raw_id, | |||
|
261 | ign_whitespace_lcl, context_lcl, c.fulldiff) | |||
|
262 | ||||
|
263 | caching_enabled = self._is_diff_cache_enabled(self.db_repo) | |||
|
264 | force_recache = str2bool(self.request.GET.get('force_recache')) | |||
|
265 | ||||
|
266 | cached_diff = None | |||
|
267 | if caching_enabled: | |||
|
268 | cached_diff = load_cached_diff(cache_file_path) | |||
|
269 | ||||
|
270 | has_proper_diff_cache = cached_diff and cached_diff.get('diff') | |||
|
271 | if not force_recache and has_proper_diff_cache: | |||
|
272 | diffset = cached_diff['diff'] | |||
|
273 | else: | |||
|
274 | vcs_diff = self.rhodecode_vcs_repo.get_diff( | |||
|
275 | commit1, commit2, | |||
|
276 | ignore_whitespace=ign_whitespace_lcl, | |||
|
277 | context=context_lcl) | |||
|
278 | ||||
|
279 | diff_processor = diffs.DiffProcessor( | |||
|
280 | vcs_diff, format='newdiff', diff_limit=diff_limit, | |||
|
281 | file_limit=file_limit, show_full_diff=c.fulldiff) | |||
|
282 | ||||
|
283 | _parsed = diff_processor.prepare() | |||
|
284 | ||||
|
285 | diffset = codeblocks.DiffSet( | |||
|
286 | repo_name=self.db_repo_name, | |||
|
287 | source_node_getter=codeblocks.diffset_node_getter(commit1), | |||
|
288 | target_node_getter=codeblocks.diffset_node_getter(commit2)) | |||
|
289 | ||||
|
290 | diffset = self.path_filter.render_patchset_filtered( | |||
|
291 | diffset, _parsed, commit1.raw_id, commit2.raw_id) | |||
|
292 | ||||
|
293 | # save cached diff | |||
|
294 | if caching_enabled: | |||
|
295 | cache_diff(cache_file_path, diffset, None) | |||
|
296 | ||||
|
297 | c.limited_diff = diffset.limited_diff | |||
|
298 | c.changes[commit.raw_id] = diffset | |||
|
299 | else: | |||
|
300 | # TODO(marcink): no cache usage here... | |||
243 | _diff = self.rhodecode_vcs_repo.get_diff( |
|
301 | _diff = self.rhodecode_vcs_repo.get_diff( | |
244 | commit1, commit2, |
|
302 | commit1, commit2, | |
245 | ignore_whitespace=ign_whitespace_lcl, context=context_lcl) |
|
303 | ignore_whitespace=ign_whitespace_lcl, context=context_lcl) | |
246 | diff_processor = diffs.DiffProcessor( |
|
304 | diff_processor = diffs.DiffProcessor( | |
247 | _diff, format='newdiff', diff_limit=diff_limit, |
|
305 | _diff, format='newdiff', diff_limit=diff_limit, | |
248 | file_limit=file_limit, show_full_diff=c.fulldiff) |
|
306 | file_limit=file_limit, show_full_diff=c.fulldiff) | |
249 |
|
||||
250 | commit_changes = OrderedDict() |
|
|||
251 | if method == 'show': |
|
|||
252 | _parsed = diff_processor.prepare() |
|
|||
253 | c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer) |
|
|||
254 |
|
||||
255 | _parsed = diff_processor.prepare() |
|
|||
256 |
|
||||
257 | def _node_getter(commit): |
|
|||
258 | def get_node(fname): |
|
|||
259 | try: |
|
|||
260 | return commit.get_node(fname) |
|
|||
261 | except NodeDoesNotExistError: |
|
|||
262 | return None |
|
|||
263 | return get_node |
|
|||
264 |
|
||||
265 | inline_comments = CommentsModel().get_inline_comments( |
|
|||
266 | self.db_repo.repo_id, revision=commit.raw_id) |
|
|||
267 | c.inline_cnt = CommentsModel().get_inline_comments_count( |
|
|||
268 | inline_comments) |
|
|||
269 |
|
||||
270 | diffset = codeblocks.DiffSet( |
|
|||
271 | repo_name=self.db_repo_name, |
|
|||
272 | source_node_getter=_node_getter(commit1), |
|
|||
273 | target_node_getter=_node_getter(commit2), |
|
|||
274 | comments=inline_comments) |
|
|||
275 | diffset = self.path_filter.render_patchset_filtered( |
|
|||
276 | diffset, _parsed, commit1.raw_id, commit2.raw_id) |
|
|||
277 |
|
||||
278 | c.changes[commit.raw_id] = diffset |
|
|||
279 | else: |
|
|||
280 | # downloads/raw we only need RAW diff nothing else |
|
307 | # downloads/raw we only need RAW diff nothing else | |
281 | diff = self.path_filter.get_raw_patch(diff_processor) |
|
308 | diff = self.path_filter.get_raw_patch(diff_processor) | |
282 | c.changes[commit.raw_id] = [None, None, None, None, diff, None, None] |
|
309 | c.changes[commit.raw_id] = [None, None, None, None, diff, None, None] |
@@ -295,19 +295,10 b' class RepoCompareView(RepoAppView):' | |||||
295 | file_limit=file_limit, show_full_diff=c.fulldiff) |
|
295 | file_limit=file_limit, show_full_diff=c.fulldiff) | |
296 | _parsed = diff_processor.prepare() |
|
296 | _parsed = diff_processor.prepare() | |
297 |
|
297 | |||
298 | def _node_getter(commit): |
|
|||
299 | """ Returns a function that returns a node for a commit or None """ |
|
|||
300 | def get_node(fname): |
|
|||
301 | try: |
|
|||
302 | return commit.get_node(fname) |
|
|||
303 | except NodeDoesNotExistError: |
|
|||
304 | return None |
|
|||
305 | return get_node |
|
|||
306 |
|
||||
307 | diffset = codeblocks.DiffSet( |
|
298 | diffset = codeblocks.DiffSet( | |
308 | repo_name=source_repo.repo_name, |
|
299 | repo_name=source_repo.repo_name, | |
309 | source_node_getter=_node_getter(source_commit), |
|
300 | source_node_getter=codeblocks.diffset_node_getter(source_commit), | |
310 | target_node_getter=_node_getter(target_commit), |
|
301 | target_node_getter=codeblocks.diffset_node_getter(target_commit), | |
311 | ) |
|
302 | ) | |
312 | c.diffset = self.path_filter.render_patchset_filtered( |
|
303 | c.diffset = self.path_filter.render_patchset_filtered( | |
313 | diffset, _parsed, source_ref, target_ref) |
|
304 | diffset, _parsed, source_ref, target_ref) |
@@ -34,6 +34,7 b' from rhodecode.apps._base import RepoApp' | |||||
34 |
|
34 | |||
35 | from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream |
|
35 | from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream | |
36 | from rhodecode.lib.base import vcs_operation_context |
|
36 | from rhodecode.lib.base import vcs_operation_context | |
|
37 | from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist | |||
37 | from rhodecode.lib.ext_json import json |
|
38 | from rhodecode.lib.ext_json import json | |
38 | from rhodecode.lib.auth import ( |
|
39 | from rhodecode.lib.auth import ( | |
39 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, |
|
40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, | |
@@ -41,7 +42,7 b' from rhodecode.lib.auth import (' | |||||
41 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode |
|
42 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode | |
42 | from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason |
|
43 | from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason | |
43 | from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError, |
|
44 | from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError, | |
44 |
RepositoryRequirementError, |
|
45 | RepositoryRequirementError, EmptyRepositoryError) | |
45 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
46 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
46 | from rhodecode.model.comment import CommentsModel |
|
47 | from rhodecode.model.comment import CommentsModel | |
47 | from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion, |
|
48 | from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion, | |
@@ -201,10 +202,16 b' class RepoPullRequestsView(RepoAppView, ' | |||||
201 |
|
202 | |||
202 | return data |
|
203 | return data | |
203 |
|
204 | |||
|
205 | def _is_diff_cache_enabled(self, target_repo): | |||
|
206 | caching_enabled = self._get_general_setting( | |||
|
207 | target_repo, 'rhodecode_diff_cache') | |||
|
208 | log.debug('Diff caching enabled: %s', caching_enabled) | |||
|
209 | return caching_enabled | |||
|
210 | ||||
204 | def _get_diffset(self, source_repo_name, source_repo, |
|
211 | def _get_diffset(self, source_repo_name, source_repo, | |
205 | source_ref_id, target_ref_id, |
|
212 | source_ref_id, target_ref_id, | |
206 |
target_commit, source_commit, diff_limit, f |
|
213 | target_commit, source_commit, diff_limit, file_limit, | |
207 |
f |
|
214 | fulldiff): | |
208 |
|
215 | |||
209 | vcs_diff = PullRequestModel().get_diff( |
|
216 | vcs_diff = PullRequestModel().get_diff( | |
210 | source_repo, source_ref_id, target_ref_id) |
|
217 | source_repo, source_ref_id, target_ref_id) | |
@@ -215,21 +222,11 b' class RepoPullRequestsView(RepoAppView, ' | |||||
215 |
|
222 | |||
216 | _parsed = diff_processor.prepare() |
|
223 | _parsed = diff_processor.prepare() | |
217 |
|
224 | |||
218 | def _node_getter(commit): |
|
|||
219 | def get_node(fname): |
|
|||
220 | try: |
|
|||
221 | return commit.get_node(fname) |
|
|||
222 | except NodeDoesNotExistError: |
|
|||
223 | return None |
|
|||
224 |
|
||||
225 | return get_node |
|
|||
226 |
|
||||
227 | diffset = codeblocks.DiffSet( |
|
225 | diffset = codeblocks.DiffSet( | |
228 | repo_name=self.db_repo_name, |
|
226 | repo_name=self.db_repo_name, | |
229 | source_repo_name=source_repo_name, |
|
227 | source_repo_name=source_repo_name, | |
230 | source_node_getter=_node_getter(target_commit), |
|
228 | source_node_getter=codeblocks.diffset_node_getter(target_commit), | |
231 | target_node_getter=_node_getter(source_commit), |
|
229 | target_node_getter=codeblocks.diffset_node_getter(source_commit), | |
232 | comments=display_inline_comments |
|
|||
233 | ) |
|
230 | ) | |
234 | diffset = self.path_filter.render_patchset_filtered( |
|
231 | diffset = self.path_filter.render_patchset_filtered( | |
235 | diffset, _parsed, target_commit.raw_id, source_commit.raw_id) |
|
232 | diffset, _parsed, target_commit.raw_id, source_commit.raw_id) | |
@@ -443,42 +440,54 b' class RepoPullRequestsView(RepoAppView, ' | |||||
443 | commits_source_repo = source_scm |
|
440 | commits_source_repo = source_scm | |
444 |
|
441 | |||
445 | c.commits_source_repo = commits_source_repo |
|
442 | c.commits_source_repo = commits_source_repo | |
446 | commit_cache = {} |
|
|||
447 | try: |
|
|||
448 | pre_load = ["author", "branch", "date", "message"] |
|
|||
449 | show_revs = pull_request_at_ver.revisions |
|
|||
450 | for rev in show_revs: |
|
|||
451 | comm = commits_source_repo.get_commit( |
|
|||
452 | commit_id=rev, pre_load=pre_load) |
|
|||
453 | c.commit_ranges.append(comm) |
|
|||
454 | commit_cache[comm.raw_id] = comm |
|
|||
455 |
|
||||
456 | # Order here matters, we first need to get target, and then |
|
|||
457 | # the source |
|
|||
458 | target_commit = commits_source_repo.get_commit( |
|
|||
459 | commit_id=safe_str(target_ref_id)) |
|
|||
460 |
|
||||
461 | source_commit = commits_source_repo.get_commit( |
|
|||
462 | commit_id=safe_str(source_ref_id)) |
|
|||
463 |
|
||||
464 | except CommitDoesNotExistError: |
|
|||
465 | log.warning( |
|
|||
466 | 'Failed to get commit from `{}` repo'.format( |
|
|||
467 | commits_source_repo), exc_info=True) |
|
|||
468 | except RepositoryRequirementError: |
|
|||
469 | log.warning( |
|
|||
470 | 'Failed to get all required data from repo', exc_info=True) |
|
|||
471 | c.missing_requirements = True |
|
|||
472 |
|
||||
473 | c.ancestor = None # set it to None, to hide it from PR view |
|
443 | c.ancestor = None # set it to None, to hide it from PR view | |
474 |
|
444 | |||
475 | try: |
|
445 | # empty version means latest, so we keep this to prevent | |
476 | ancestor_id = source_scm.get_common_ancestor( |
|
446 | # double caching | |
477 | source_commit.raw_id, target_commit.raw_id, target_scm) |
|
447 | version_normalized = version or 'latest' | |
478 | c.ancestor_commit = source_scm.get_commit(ancestor_id) |
|
448 | from_version_normalized = from_version or 'latest' | |
479 | except Exception: |
|
449 | ||
480 | c.ancestor_commit = None |
|
450 | cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path( | |
|
451 | target_repo) | |||
|
452 | cache_file_path = diff_cache_exist( | |||
|
453 | cache_path, 'pull_request', pull_request_id, version_normalized, | |||
|
454 | from_version_normalized, source_ref_id, target_ref_id, c.fulldiff) | |||
|
455 | ||||
|
456 | caching_enabled = self._is_diff_cache_enabled(c.target_repo) | |||
|
457 | force_recache = str2bool(self.request.GET.get('force_recache')) | |||
|
458 | ||||
|
459 | cached_diff = None | |||
|
460 | if caching_enabled: | |||
|
461 | cached_diff = load_cached_diff(cache_file_path) | |||
481 |
|
462 | |||
|
463 | has_proper_commit_cache = ( | |||
|
464 | cached_diff and cached_diff.get('commits') | |||
|
465 | and len(cached_diff.get('commits', [])) == 5 | |||
|
466 | and cached_diff.get('commits')[0] | |||
|
467 | and cached_diff.get('commits')[3]) | |||
|
468 | if not force_recache and has_proper_commit_cache: | |||
|
469 | diff_commit_cache = \ | |||
|
470 | (ancestor_commit, commit_cache, missing_requirements, | |||
|
471 | source_commit, target_commit) = cached_diff['commits'] | |||
|
472 | else: | |||
|
473 | diff_commit_cache = \ | |||
|
474 | (ancestor_commit, commit_cache, missing_requirements, | |||
|
475 | source_commit, target_commit) = self.get_commits( | |||
|
476 | commits_source_repo, | |||
|
477 | pull_request_at_ver, | |||
|
478 | source_commit, | |||
|
479 | source_ref_id, | |||
|
480 | source_scm, | |||
|
481 | target_commit, | |||
|
482 | target_ref_id, | |||
|
483 | target_scm) | |||
|
484 | ||||
|
485 | # register our commit range | |||
|
486 | for comm in commit_cache.values(): | |||
|
487 | c.commit_ranges.append(comm) | |||
|
488 | ||||
|
489 | c.missing_requirements = missing_requirements | |||
|
490 | c.ancestor_commit = ancestor_commit | |||
482 | c.statuses = source_repo.statuses( |
|
491 | c.statuses = source_repo.statuses( | |
483 | [x.raw_id for x in c.commit_ranges]) |
|
492 | [x.raw_id for x in c.commit_ranges]) | |
484 |
|
493 | |||
@@ -500,12 +509,23 b' class RepoPullRequestsView(RepoAppView, ' | |||||
500 |
|
509 | |||
501 | c.missing_commits = True |
|
510 | c.missing_commits = True | |
502 | else: |
|
511 | else: | |
|
512 | c.inline_comments = display_inline_comments | |||
503 |
|
513 | |||
|
514 | has_proper_diff_cache = cached_diff and cached_diff.get('commits') | |||
|
515 | if not force_recache and has_proper_diff_cache: | |||
|
516 | c.diffset = cached_diff['diff'] | |||
|
517 | (ancestor_commit, commit_cache, missing_requirements, | |||
|
518 | source_commit, target_commit) = cached_diff['commits'] | |||
|
519 | else: | |||
504 | c.diffset = self._get_diffset( |
|
520 | c.diffset = self._get_diffset( | |
505 | c.source_repo.repo_name, commits_source_repo, |
|
521 | c.source_repo.repo_name, commits_source_repo, | |
506 | source_ref_id, target_ref_id, |
|
522 | source_ref_id, target_ref_id, | |
507 | target_commit, source_commit, |
|
523 | target_commit, source_commit, | |
508 |
diff_limit, c.fulldiff |
|
524 | diff_limit, file_limit, c.fulldiff) | |
|
525 | ||||
|
526 | # save cached diff | |||
|
527 | if caching_enabled: | |||
|
528 | cache_diff(cache_file_path, c.diffset, diff_commit_cache) | |||
509 |
|
529 | |||
510 | c.limited_diff = c.diffset.limited_diff |
|
530 | c.limited_diff = c.diffset.limited_diff | |
511 |
|
531 | |||
@@ -568,7 +588,6 b' class RepoPullRequestsView(RepoAppView, ' | |||||
568 | if self._rhodecode_user.user_id in allowed_reviewers: |
|
588 | if self._rhodecode_user.user_id in allowed_reviewers: | |
569 | for co in general_comments: |
|
589 | for co in general_comments: | |
570 | if co.author.user_id == self._rhodecode_user.user_id: |
|
590 | if co.author.user_id == self._rhodecode_user.user_id: | |
571 | # each comment has a status change |
|
|||
572 | status = co.status_change |
|
591 | status = co.status_change | |
573 | if status: |
|
592 | if status: | |
574 | _ver_pr = status[0].comment.pull_request_version_id |
|
593 | _ver_pr = status[0].comment.pull_request_version_id | |
@@ -576,6 +595,43 b' class RepoPullRequestsView(RepoAppView, ' | |||||
576 |
|
595 | |||
577 | return self._get_template_context(c) |
|
596 | return self._get_template_context(c) | |
578 |
|
597 | |||
|
598 | def get_commits( | |||
|
599 | self, commits_source_repo, pull_request_at_ver, source_commit, | |||
|
600 | source_ref_id, source_scm, target_commit, target_ref_id, target_scm): | |||
|
601 | commit_cache = collections.OrderedDict() | |||
|
602 | missing_requirements = False | |||
|
603 | try: | |||
|
604 | pre_load = ["author", "branch", "date", "message"] | |||
|
605 | show_revs = pull_request_at_ver.revisions | |||
|
606 | for rev in show_revs: | |||
|
607 | comm = commits_source_repo.get_commit( | |||
|
608 | commit_id=rev, pre_load=pre_load) | |||
|
609 | commit_cache[comm.raw_id] = comm | |||
|
610 | ||||
|
611 | # Order here matters, we first need to get target, and then | |||
|
612 | # the source | |||
|
613 | target_commit = commits_source_repo.get_commit( | |||
|
614 | commit_id=safe_str(target_ref_id)) | |||
|
615 | ||||
|
616 | source_commit = commits_source_repo.get_commit( | |||
|
617 | commit_id=safe_str(source_ref_id)) | |||
|
618 | except CommitDoesNotExistError: | |||
|
619 | log.warning( | |||
|
620 | 'Failed to get commit from `{}` repo'.format( | |||
|
621 | commits_source_repo), exc_info=True) | |||
|
622 | except RepositoryRequirementError: | |||
|
623 | log.warning( | |||
|
624 | 'Failed to get all required data from repo', exc_info=True) | |||
|
625 | missing_requirements = True | |||
|
626 | ancestor_commit = None | |||
|
627 | try: | |||
|
628 | ancestor_id = source_scm.get_common_ancestor( | |||
|
629 | source_commit.raw_id, target_commit.raw_id, target_scm) | |||
|
630 | ancestor_commit = source_scm.get_commit(ancestor_id) | |||
|
631 | except Exception: | |||
|
632 | ancestor_commit = None | |||
|
633 | return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit | |||
|
634 | ||||
579 | def assure_not_empty_repo(self): |
|
635 | def assure_not_empty_repo(self): | |
580 | _ = self.request.translate |
|
636 | _ = self.request.translate | |
581 |
|
637 |
@@ -30,6 +30,7 b' from rhodecode.lib.helpers import (' | |||||
30 | get_lexer_for_filenode, html_escape, get_custom_lexer) |
|
30 | get_lexer_for_filenode, html_escape, get_custom_lexer) | |
31 | from rhodecode.lib.utils2 import AttributeDict, StrictAttributeDict |
|
31 | from rhodecode.lib.utils2 import AttributeDict, StrictAttributeDict | |
32 | from rhodecode.lib.vcs.nodes import FileNode |
|
32 | from rhodecode.lib.vcs.nodes import FileNode | |
|
33 | from rhodecode.lib.vcs.exceptions import VCSError, NodeDoesNotExistError | |||
33 | from rhodecode.lib.diff_match_patch import diff_match_patch |
|
34 | from rhodecode.lib.diff_match_patch import diff_match_patch | |
34 | from rhodecode.lib.diffs import LimitedDiffContainer |
|
35 | from rhodecode.lib.diffs import LimitedDiffContainer | |
35 | from pygments.lexers import get_lexer_by_name |
|
36 | from pygments.lexers import get_lexer_by_name | |
@@ -351,6 +352,16 b' def tokens_diff(old_tokens, new_tokens, ' | |||||
351 | return old_tokens_result, new_tokens_result, similarity |
|
352 | return old_tokens_result, new_tokens_result, similarity | |
352 |
|
353 | |||
353 |
|
354 | |||
|
355 | def diffset_node_getter(commit): | |||
|
356 | def get_node(fname): | |||
|
357 | try: | |||
|
358 | return commit.get_node(fname) | |||
|
359 | except NodeDoesNotExistError: | |||
|
360 | return None | |||
|
361 | ||||
|
362 | return get_node | |||
|
363 | ||||
|
364 | ||||
354 | class DiffSet(object): |
|
365 | class DiffSet(object): | |
355 | """ |
|
366 | """ | |
356 | An object for parsing the diff result from diffs.DiffProcessor and |
|
367 | An object for parsing the diff result from diffs.DiffProcessor and | |
@@ -515,6 +526,7 b' class DiffSet(object):' | |||||
515 | if target_file_path in self.comments_store: |
|
526 | if target_file_path in self.comments_store: | |
516 | for lineno, comments in self.comments_store[target_file_path].items(): |
|
527 | for lineno, comments in self.comments_store[target_file_path].items(): | |
517 | left_comments[lineno] = comments |
|
528 | left_comments[lineno] = comments | |
|
529 | ||||
518 | # left comments are one that we couldn't place in diff lines. |
|
530 | # left comments are one that we couldn't place in diff lines. | |
519 | # could be outdated, or the diff changed and this line is no |
|
531 | # could be outdated, or the diff changed and this line is no | |
520 | # longer available |
|
532 | # longer available | |
@@ -551,7 +563,7 b' class DiffSet(object):' | |||||
551 |
|
563 | |||
552 | result.lines.extend( |
|
564 | result.lines.extend( | |
553 | self.parse_lines(before, after, source_file, target_file)) |
|
565 | self.parse_lines(before, after, source_file, target_file)) | |
554 | result.unified = self.as_unified(result.lines) |
|
566 | result.unified = list(self.as_unified(result.lines)) | |
555 | result.sideside = result.lines |
|
567 | result.sideside = result.lines | |
556 |
|
568 | |||
557 | return result |
|
569 | return result | |
@@ -606,8 +618,9 b' class DiffSet(object):' | |||||
606 | original.lineno = before['old_lineno'] |
|
618 | original.lineno = before['old_lineno'] | |
607 | original.content = before['line'] |
|
619 | original.content = before['line'] | |
608 | original.action = self.action_to_op(before['action']) |
|
620 | original.action = self.action_to_op(before['action']) | |
609 | original.comments = self.get_comments_for('old', |
|
621 | ||
610 | source_file, before['old_lineno']) |
|
622 | original.get_comment_args = ( | |
|
623 | source_file, 'o', before['old_lineno']) | |||
611 |
|
624 | |||
612 | if after: |
|
625 | if after: | |
613 | if after['action'] == 'new-no-nl': |
|
626 | if after['action'] == 'new-no-nl': | |
@@ -619,8 +632,9 b' class DiffSet(object):' | |||||
619 | modified.lineno = after['new_lineno'] |
|
632 | modified.lineno = after['new_lineno'] | |
620 | modified.content = after['line'] |
|
633 | modified.content = after['line'] | |
621 | modified.action = self.action_to_op(after['action']) |
|
634 | modified.action = self.action_to_op(after['action']) | |
622 | modified.comments = self.get_comments_for('new', |
|
635 | ||
623 | target_file, after['new_lineno']) |
|
636 | modified.get_comment_args = ( | |
|
637 | target_file, 'n', after['new_lineno']) | |||
624 |
|
638 | |||
625 | # diff the lines |
|
639 | # diff the lines | |
626 | if before_tokens and after_tokens: |
|
640 | if before_tokens and after_tokens: | |
@@ -649,23 +663,6 b' class DiffSet(object):' | |||||
649 |
|
663 | |||
650 | return lines |
|
664 | return lines | |
651 |
|
665 | |||
652 | def get_comments_for(self, version, filename, line_number): |
|
|||
653 | if hasattr(filename, 'unicode_path'): |
|
|||
654 | filename = filename.unicode_path |
|
|||
655 |
|
||||
656 | if not isinstance(filename, basestring): |
|
|||
657 | return None |
|
|||
658 |
|
||||
659 | line_key = { |
|
|||
660 | 'old': 'o', |
|
|||
661 | 'new': 'n', |
|
|||
662 | }[version] + str(line_number) |
|
|||
663 |
|
||||
664 | if filename in self.comments_store: |
|
|||
665 | file_comments = self.comments_store[filename] |
|
|||
666 | if line_key in file_comments: |
|
|||
667 | return file_comments.pop(line_key) |
|
|||
668 |
|
||||
669 | def get_line_tokens(self, line_text, line_number, file=None): |
|
666 | def get_line_tokens(self, line_text, line_number, file=None): | |
670 | filenode = None |
|
667 | filenode = None | |
671 | filename = None |
|
668 | filename = None | |
@@ -722,25 +719,25 b' class DiffSet(object):' | |||||
722 | if line.original.action == ' ': |
|
719 | if line.original.action == ' ': | |
723 | yield (line.original.lineno, line.modified.lineno, |
|
720 | yield (line.original.lineno, line.modified.lineno, | |
724 | line.original.action, line.original.content, |
|
721 | line.original.action, line.original.content, | |
725 | line.original.comments) |
|
722 | line.original.get_comment_args) | |
726 | continue |
|
723 | continue | |
727 |
|
724 | |||
728 | if line.original.action == '-': |
|
725 | if line.original.action == '-': | |
729 | yield (line.original.lineno, None, |
|
726 | yield (line.original.lineno, None, | |
730 | line.original.action, line.original.content, |
|
727 | line.original.action, line.original.content, | |
731 | line.original.comments) |
|
728 | line.original.get_comment_args) | |
732 |
|
729 | |||
733 | if line.modified.action == '+': |
|
730 | if line.modified.action == '+': | |
734 | buf.append(( |
|
731 | buf.append(( | |
735 | None, line.modified.lineno, |
|
732 | None, line.modified.lineno, | |
736 | line.modified.action, line.modified.content, |
|
733 | line.modified.action, line.modified.content, | |
737 | line.modified.comments)) |
|
734 | line.modified.get_comment_args)) | |
738 | continue |
|
735 | continue | |
739 |
|
736 | |||
740 | if line.modified: |
|
737 | if line.modified: | |
741 | yield (None, line.modified.lineno, |
|
738 | yield (None, line.modified.lineno, | |
742 | line.modified.action, line.modified.content, |
|
739 | line.modified.action, line.modified.content, | |
743 | line.modified.comments) |
|
740 | line.modified.get_comment_args) | |
744 |
|
741 | |||
745 | for b in buf: |
|
742 | for b in buf: | |
746 | yield b |
|
743 | yield b |
@@ -23,16 +23,18 b'' | |||||
23 | Set of diffing helpers, previously part of vcs |
|
23 | Set of diffing helpers, previously part of vcs | |
24 | """ |
|
24 | """ | |
25 |
|
25 | |||
|
26 | import os | |||
26 | import re |
|
27 | import re | |
27 | import collections |
|
28 | import collections | |
28 | import difflib |
|
29 | import difflib | |
29 | import logging |
|
30 | import logging | |
|
31 | import cPickle as pickle | |||
30 |
|
32 | |||
31 | from itertools import tee, imap |
|
33 | from itertools import tee, imap | |
32 |
|
34 | |||
33 | from rhodecode.lib.vcs.exceptions import VCSError |
|
35 | from rhodecode.lib.vcs.exceptions import VCSError | |
34 | from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode |
|
36 | from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode | |
35 | from rhodecode.lib.utils2 import safe_unicode |
|
37 | from rhodecode.lib.utils2 import safe_unicode, safe_str | |
36 |
|
38 | |||
37 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
38 |
|
40 | |||
@@ -1129,3 +1131,82 b' class LineNotInDiffException(Exception):' | |||||
1129 |
|
1131 | |||
1130 | class DiffLimitExceeded(Exception): |
|
1132 | class DiffLimitExceeded(Exception): | |
1131 | pass |
|
1133 | pass | |
|
1134 | ||||
|
1135 | ||||
|
1136 | def cache_diff(cached_diff_file, diff, commits): | |||
|
1137 | ||||
|
1138 | struct = { | |||
|
1139 | 'version': 'v1', | |||
|
1140 | 'diff': diff, | |||
|
1141 | 'commits': commits | |||
|
1142 | } | |||
|
1143 | ||||
|
1144 | try: | |||
|
1145 | with open(cached_diff_file, 'wb') as f: | |||
|
1146 | pickle.dump(struct, f) | |||
|
1147 | log.debug('Saved diff cache under %s', cached_diff_file) | |||
|
1148 | except Exception: | |||
|
1149 | log.warn('Failed to save cache', exc_info=True) | |||
|
1150 | # cleanup file to not store it "damaged" | |||
|
1151 | try: | |||
|
1152 | os.remove(cached_diff_file) | |||
|
1153 | except Exception: | |||
|
1154 | log.exception('Failed to cleanup path %s', cached_diff_file) | |||
|
1155 | ||||
|
1156 | ||||
|
1157 | def load_cached_diff(cached_diff_file): | |||
|
1158 | ||||
|
1159 | default_struct = { | |||
|
1160 | 'version': 'v1', | |||
|
1161 | 'diff': None, | |||
|
1162 | 'commits': None | |||
|
1163 | } | |||
|
1164 | ||||
|
1165 | has_cache = os.path.isfile(cached_diff_file) | |||
|
1166 | if not has_cache: | |||
|
1167 | return default_struct | |||
|
1168 | ||||
|
1169 | data = None | |||
|
1170 | try: | |||
|
1171 | with open(cached_diff_file, 'rb') as f: | |||
|
1172 | data = pickle.load(f) | |||
|
1173 | log.debug('Loaded diff cache from %s', cached_diff_file) | |||
|
1174 | except Exception: | |||
|
1175 | log.warn('Failed to read diff cache file', exc_info=True) | |||
|
1176 | ||||
|
1177 | if not data: | |||
|
1178 | data = default_struct | |||
|
1179 | ||||
|
1180 | if not isinstance(data, dict): | |||
|
1181 | # old version of data ? | |||
|
1182 | data = default_struct | |||
|
1183 | ||||
|
1184 | return data | |||
|
1185 | ||||
|
1186 | ||||
|
1187 | def generate_diff_cache_key(*args): | |||
|
1188 | """ | |||
|
1189 | Helper to generate a cache key using arguments | |||
|
1190 | """ | |||
|
1191 | def arg_mapper(input_param): | |||
|
1192 | input_param = safe_str(input_param) | |||
|
1193 | # we cannot allow '/' in arguments since it would allow | |||
|
1194 | # subdirectory usage | |||
|
1195 | input_param.replace('/', '_') | |||
|
1196 | return input_param or None # prevent empty string arguments | |||
|
1197 | ||||
|
1198 | return '_'.join([ | |||
|
1199 | '{}' for i in range(len(args))]).format(*map(arg_mapper, args)) | |||
|
1200 | ||||
|
1201 | ||||
|
1202 | def diff_cache_exist(cache_storage, *args): | |||
|
1203 | """ | |||
|
1204 | Based on all generated arguments check and return a cache path | |||
|
1205 | """ | |||
|
1206 | cache_key = generate_diff_cache_key(*args) | |||
|
1207 | cache_file_path = os.path.join(cache_storage, cache_key) | |||
|
1208 | # prevent path traversal attacks using some param that have e.g '../../' | |||
|
1209 | if not os.path.abspath(cache_file_path).startswith(cache_storage): | |||
|
1210 | raise ValueError('Final path must be within {}'.format(cache_storage)) | |||
|
1211 | ||||
|
1212 | return cache_file_path |
@@ -709,7 +709,19 b' def extract_mentioned_users(s):' | |||||
709 | return sorted(list(usrs), key=lambda k: k.lower()) |
|
709 | return sorted(list(usrs), key=lambda k: k.lower()) | |
710 |
|
710 | |||
711 |
|
711 | |||
712 |
class |
|
712 | class AttributeDictBase(dict): | |
|
713 | def __getstate__(self): | |||
|
714 | odict = self.__dict__ # get attribute dictionary | |||
|
715 | return odict | |||
|
716 | ||||
|
717 | def __setstate__(self, dict): | |||
|
718 | self.__dict__ = dict | |||
|
719 | ||||
|
720 | __setattr__ = dict.__setitem__ | |||
|
721 | __delattr__ = dict.__delitem__ | |||
|
722 | ||||
|
723 | ||||
|
724 | class StrictAttributeDict(AttributeDictBase): | |||
713 | """ |
|
725 | """ | |
714 | Strict Version of Attribute dict which raises an Attribute error when |
|
726 | Strict Version of Attribute dict which raises an Attribute error when | |
715 | requested attribute is not set |
|
727 | requested attribute is not set | |
@@ -720,15 +732,12 b' class StrictAttributeDict(dict):' | |||||
720 | except KeyError: |
|
732 | except KeyError: | |
721 | raise AttributeError('%s object has no attribute %s' % ( |
|
733 | raise AttributeError('%s object has no attribute %s' % ( | |
722 | self.__class__, attr)) |
|
734 | self.__class__, attr)) | |
723 | __setattr__ = dict.__setitem__ |
|
|||
724 | __delattr__ = dict.__delitem__ |
|
|||
725 |
|
735 | |||
726 |
|
736 | |||
727 |
class AttributeDict( |
|
737 | class AttributeDict(AttributeDictBase): | |
728 | def __getattr__(self, attr): |
|
738 | def __getattr__(self, attr): | |
729 | return self.get(attr, None) |
|
739 | return self.get(attr, None) | |
730 | __setattr__ = dict.__setitem__ |
|
740 | ||
731 | __delattr__ = dict.__delitem__ |
|
|||
732 |
|
741 | |||
733 |
|
742 | |||
734 | def fix_PATH(os_=None): |
|
743 | def fix_PATH(os_=None): |
@@ -206,6 +206,14 b' class BaseRepository(object):' | |||||
206 | def __ne__(self, other): |
|
206 | def __ne__(self, other): | |
207 | return not self.__eq__(other) |
|
207 | return not self.__eq__(other) | |
208 |
|
208 | |||
|
209 | def get_create_shadow_cache_pr_path(self, repo): | |||
|
210 | path = os.path.join( | |||
|
211 | os.path.dirname(self.path), | |||
|
212 | '.__shadow_diff_cache_repo_{}/'.format(repo.repo_id)) | |||
|
213 | if not os.path.exists(path): | |||
|
214 | os.makedirs(path, 0755) | |||
|
215 | return path | |||
|
216 | ||||
209 | @classmethod |
|
217 | @classmethod | |
210 | def get_default_config(cls, default=None): |
|
218 | def get_default_config(cls, default=None): | |
211 | config = Config() |
|
219 | config = Config() | |
@@ -745,6 +753,12 b' class BaseCommit(object):' | |||||
745 | 'branch': self.branch |
|
753 | 'branch': self.branch | |
746 | } |
|
754 | } | |
747 |
|
755 | |||
|
756 | def __getstate__(self): | |||
|
757 | d = self.__dict__.copy() | |||
|
758 | d.pop('_remote', None) | |||
|
759 | d.pop('repository', None) | |||
|
760 | return d | |||
|
761 | ||||
748 | def _get_refs(self): |
|
762 | def _get_refs(self): | |
749 | return { |
|
763 | return { | |
750 | 'branches': [self.branch] if self.branch else [], |
|
764 | 'branches': [self.branch] if self.branch else [], |
@@ -430,6 +430,9 b' class _BaseVcsSettingsForm(formencode.Sc' | |||||
430 | vcs_svn_proxy_http_requests_enabled = v.StringBoolean(if_missing=False) |
|
430 | vcs_svn_proxy_http_requests_enabled = v.StringBoolean(if_missing=False) | |
431 | vcs_svn_proxy_http_server_url = v.UnicodeString(strip=True, if_missing=None) |
|
431 | vcs_svn_proxy_http_server_url = v.UnicodeString(strip=True, if_missing=None) | |
432 |
|
432 | |||
|
433 | # cache | |||
|
434 | rhodecode_diff_cache = v.StringBoolean(if_missing=False) | |||
|
435 | ||||
433 |
|
436 | |||
434 | def ApplicationUiSettingsForm(localizer): |
|
437 | def ApplicationUiSettingsForm(localizer): | |
435 | _ = localizer |
|
438 | _ = localizer |
@@ -417,7 +417,9 b' class VcsSettingsModel(object):' | |||||
417 | 'hg_use_rebase_for_merging', |
|
417 | 'hg_use_rebase_for_merging', | |
418 | 'hg_close_branch_before_merging', |
|
418 | 'hg_close_branch_before_merging', | |
419 | 'git_use_rebase_for_merging', |
|
419 | 'git_use_rebase_for_merging', | |
420 |
'git_close_branch_before_merging' |
|
420 | 'git_close_branch_before_merging', | |
|
421 | 'diff_cache', | |||
|
422 | ) | |||
421 |
|
423 | |||
422 | HOOKS_SETTINGS = ( |
|
424 | HOOKS_SETTINGS = ( | |
423 | ('hooks', 'changegroup.repo_size'), |
|
425 | ('hooks', 'changegroup.repo_size'), | |
@@ -438,6 +440,7 b' class VcsSettingsModel(object):' | |||||
438 | GLOBAL_GIT_SETTINGS = ( |
|
440 | GLOBAL_GIT_SETTINGS = ( | |
439 | ('vcs_git_lfs', 'enabled'), |
|
441 | ('vcs_git_lfs', 'enabled'), | |
440 | ('vcs_git_lfs', 'store_location')) |
|
442 | ('vcs_git_lfs', 'store_location')) | |
|
443 | ||||
441 | GLOBAL_SVN_SETTINGS = ( |
|
444 | GLOBAL_SVN_SETTINGS = ( | |
442 | ('vcs_svn_proxy', 'http_requests_enabled'), |
|
445 | ('vcs_svn_proxy', 'http_requests_enabled'), | |
443 | ('vcs_svn_proxy', 'http_server_url')) |
|
446 | ('vcs_svn_proxy', 'http_server_url')) | |
@@ -571,6 +574,7 b' class VcsSettingsModel(object):' | |||||
571 | self._create_or_update_ui( |
|
574 | self._create_or_update_ui( | |
572 | self.repo_settings, *phases, value=safe_str(data[phases_key])) |
|
575 | self.repo_settings, *phases, value=safe_str(data[phases_key])) | |
573 |
|
576 | |||
|
577 | ||||
574 | def create_or_update_global_hg_settings(self, data): |
|
578 | def create_or_update_global_hg_settings(self, data): | |
575 | largefiles, largefiles_store, phases, hgsubversion, evolve \ |
|
579 | largefiles, largefiles_store, phases, hgsubversion, evolve \ | |
576 | = self.GLOBAL_HG_SETTINGS |
|
580 | = self.GLOBAL_HG_SETTINGS |
@@ -312,6 +312,20 b'' | |||||
312 | </div> |
|
312 | </div> | |
313 | % endif |
|
313 | % endif | |
314 |
|
314 | |||
|
315 | % if display_globals or repo_type in ['hg', 'git', 'svn']: | |||
|
316 | <div class="panel panel-default"> | |||
|
317 | <div class="panel-heading" id="vcs-pull-requests-options"> | |||
|
318 | <h3 class="panel-title">${_('Diff cache')}<a class="permalink" href="#vcs-pull-requests-options"> ¶</a></h3> | |||
|
319 | </div> | |||
|
320 | <div class="panel-body"> | |||
|
321 | <div class="checkbox"> | |||
|
322 | ${h.checkbox('rhodecode_diff_cache' + suffix, 'True', **kwargs)} | |||
|
323 | <label for="rhodecode_diff_cache${suffix}">${_('Enable caching diffs for pull requests cache and commits')}</label> | |||
|
324 | </div> | |||
|
325 | </div> | |||
|
326 | </div> | |||
|
327 | % endif | |||
|
328 | ||||
315 | % if display_globals or repo_type in ['hg',]: |
|
329 | % if display_globals or repo_type in ['hg',]: | |
316 | <div class="panel panel-default"> |
|
330 | <div class="panel panel-default"> | |
317 | <div class="panel-heading" id="vcs-pull-requests-options"> |
|
331 | <div class="panel-heading" id="vcs-pull-requests-options"> |
@@ -213,7 +213,7 b'' | |||||
213 | <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/> |
|
213 | <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/> | |
214 | ${cbdiffs.render_diffset_menu()} |
|
214 | ${cbdiffs.render_diffset_menu()} | |
215 | ${cbdiffs.render_diffset( |
|
215 | ${cbdiffs.render_diffset( | |
216 | c.changes[c.commit.raw_id], commit=c.commit, use_comments=True)} |
|
216 | c.changes[c.commit.raw_id], commit=c.commit, use_comments=True,inline_comments=c.inline_comments )} | |
217 | </div> |
|
217 | </div> | |
218 |
|
218 | |||
219 | ## template for inline comment form |
|
219 | ## template for inline comment form |
@@ -44,13 +44,15 b" return '%s_%s_%i' % (h.safeid(filename)," | |||||
44 |
|
44 | |||
45 | # special file-comments that were deleted in previous versions |
|
45 | # special file-comments that were deleted in previous versions | |
46 | # it's used for showing outdated comments for deleted files in a PR |
|
46 | # it's used for showing outdated comments for deleted files in a PR | |
47 | deleted_files_comments=None |
|
47 | deleted_files_comments=None, | |
|
48 | ||||
|
49 | # for cache purpose | |||
|
50 | inline_comments=None | |||
48 |
|
51 | |||
49 | )"> |
|
52 | )"> | |
50 |
|
||||
51 | %if use_comments: |
|
53 | %if use_comments: | |
52 | <div id="cb-comments-inline-container-template" class="js-template"> |
|
54 | <div id="cb-comments-inline-container-template" class="js-template"> | |
53 | ${inline_comments_container([])} |
|
55 | ${inline_comments_container([], inline_comments)} | |
54 | </div> |
|
56 | </div> | |
55 | <div class="js-template" id="cb-comment-inline-form-template"> |
|
57 | <div class="js-template" id="cb-comment-inline-form-template"> | |
56 | <div class="comment-inline-form ac"> |
|
58 | <div class="comment-inline-form ac"> | |
@@ -211,9 +213,9 b' collapse_all = len(diffset.files) > coll' | |||||
211 | </td> |
|
213 | </td> | |
212 | </tr> |
|
214 | </tr> | |
213 | %if c.diffmode == 'unified': |
|
215 | %if c.diffmode == 'unified': | |
214 | ${render_hunk_lines_unified(hunk, use_comments=use_comments)} |
|
216 | ${render_hunk_lines_unified(hunk, use_comments=use_comments, inline_comments=inline_comments)} | |
215 | %elif c.diffmode == 'sideside': |
|
217 | %elif c.diffmode == 'sideside': | |
216 | ${render_hunk_lines_sideside(hunk, use_comments=use_comments)} |
|
218 | ${render_hunk_lines_sideside(hunk, use_comments=use_comments, inline_comments=inline_comments)} | |
217 | %else: |
|
219 | %else: | |
218 | <tr class="cb-line"> |
|
220 | <tr class="cb-line"> | |
219 | <td>unknown diff mode</td> |
|
221 | <td>unknown diff mode</td> | |
@@ -230,7 +232,7 b' collapse_all = len(diffset.files) > coll' | |||||
230 | <td class="cb-lineno cb-context"></td> |
|
232 | <td class="cb-lineno cb-context"></td> | |
231 | <td class="cb-lineno cb-context"></td> |
|
233 | <td class="cb-lineno cb-context"></td> | |
232 | <td class="cb-content cb-context"> |
|
234 | <td class="cb-content cb-context"> | |
233 | ${inline_comments_container(comments)} |
|
235 | ${inline_comments_container(comments, inline_comments)} | |
234 | </td> |
|
236 | </td> | |
235 | </tr> |
|
237 | </tr> | |
236 | %elif c.diffmode == 'sideside': |
|
238 | %elif c.diffmode == 'sideside': | |
@@ -239,7 +241,7 b' collapse_all = len(diffset.files) > coll' | |||||
239 | <td class="cb-lineno cb-context"></td> |
|
241 | <td class="cb-lineno cb-context"></td> | |
240 | <td class="cb-content cb-context"> |
|
242 | <td class="cb-content cb-context"> | |
241 | % if lineno.startswith('o'): |
|
243 | % if lineno.startswith('o'): | |
242 | ${inline_comments_container(comments)} |
|
244 | ${inline_comments_container(comments, inline_comments)} | |
243 | % endif |
|
245 | % endif | |
244 | </td> |
|
246 | </td> | |
245 |
|
247 | |||
@@ -247,7 +249,7 b' collapse_all = len(diffset.files) > coll' | |||||
247 | <td class="cb-lineno cb-context"></td> |
|
249 | <td class="cb-lineno cb-context"></td> | |
248 | <td class="cb-content cb-context"> |
|
250 | <td class="cb-content cb-context"> | |
249 | % if lineno.startswith('n'): |
|
251 | % if lineno.startswith('n'): | |
250 | ${inline_comments_container(comments)} |
|
252 | ${inline_comments_container(comments, inline_comments)} | |
251 | % endif |
|
253 | % endif | |
252 | </td> |
|
254 | </td> | |
253 | </tr> |
|
255 | </tr> | |
@@ -298,7 +300,7 b' collapse_all = len(diffset.files) > coll' | |||||
298 | <td class="cb-lineno cb-context"></td> |
|
300 | <td class="cb-lineno cb-context"></td> | |
299 | <td class="cb-lineno cb-context"></td> |
|
301 | <td class="cb-lineno cb-context"></td> | |
300 | <td class="cb-content cb-context"> |
|
302 | <td class="cb-content cb-context"> | |
301 | ${inline_comments_container(comments_dict['comments'])} |
|
303 | ${inline_comments_container(comments_dict['comments'], inline_comments)} | |
302 | </td> |
|
304 | </td> | |
303 | </tr> |
|
305 | </tr> | |
304 | %elif c.diffmode == 'sideside': |
|
306 | %elif c.diffmode == 'sideside': | |
@@ -310,7 +312,7 b' collapse_all = len(diffset.files) > coll' | |||||
310 | <td class="cb-data cb-context"></td> |
|
312 | <td class="cb-data cb-context"></td> | |
311 | <td class="cb-lineno cb-context"></td> |
|
313 | <td class="cb-lineno cb-context"></td> | |
312 | <td class="cb-content cb-context"> |
|
314 | <td class="cb-content cb-context"> | |
313 | ${inline_comments_container(comments_dict['comments'])} |
|
315 | ${inline_comments_container(comments_dict['comments'], inline_comments)} | |
314 | </td> |
|
316 | </td> | |
315 | </tr> |
|
317 | </tr> | |
316 | %endif |
|
318 | %endif | |
@@ -484,12 +486,11 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
484 | </%def> |
|
486 | </%def> | |
485 |
|
487 | |||
486 |
|
488 | |||
487 | <%def name="inline_comments_container(comments)"> |
|
489 | <%def name="inline_comments_container(comments, inline_comments)"> | |
488 | <div class="inline-comments"> |
|
490 | <div class="inline-comments"> | |
489 | %for comment in comments: |
|
491 | %for comment in comments: | |
490 | ${commentblock.comment_block(comment, inline=True)} |
|
492 | ${commentblock.comment_block(comment, inline=True)} | |
491 | %endfor |
|
493 | %endfor | |
492 |
|
||||
493 | % if comments and comments[-1].outdated: |
|
494 | % if comments and comments[-1].outdated: | |
494 | <span class="btn btn-secondary cb-comment-add-button comment-outdated}" |
|
495 | <span class="btn btn-secondary cb-comment-add-button comment-outdated}" | |
495 | style="display: none;}"> |
|
496 | style="display: none;}"> | |
@@ -505,8 +506,23 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
505 | </div> |
|
506 | </div> | |
506 | </%def> |
|
507 | </%def> | |
507 |
|
508 | |||
|
509 | <%! | |||
|
510 | def get_comments_for(comments, filename, line_version, line_number): | |||
|
511 | if hasattr(filename, 'unicode_path'): | |||
|
512 | filename = filename.unicode_path | |||
508 |
|
513 | |||
509 | <%def name="render_hunk_lines_sideside(hunk, use_comments=False)"> |
|
514 | if not isinstance(filename, basestring): | |
|
515 | return None | |||
|
516 | ||||
|
517 | line_key = '{}{}'.format(line_version, line_number) | |||
|
518 | if comments and filename in comments: | |||
|
519 | file_comments = comments[filename] | |||
|
520 | if line_key in file_comments: | |||
|
521 | return file_comments[line_key] | |||
|
522 | %> | |||
|
523 | ||||
|
524 | <%def name="render_hunk_lines_sideside(hunk, use_comments=False, inline_comments=None)"> | |||
|
525 | ||||
510 | %for i, line in enumerate(hunk.sideside): |
|
526 | %for i, line in enumerate(hunk.sideside): | |
511 | <% |
|
527 | <% | |
512 | old_line_anchor, new_line_anchor = None, None |
|
528 | old_line_anchor, new_line_anchor = None, None | |
@@ -521,12 +537,16 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
521 | data-line-no="${line.original.lineno}" |
|
537 | data-line-no="${line.original.lineno}" | |
522 | > |
|
538 | > | |
523 | <div> |
|
539 | <div> | |
524 | %if line.original.comments: |
|
540 | <% loc = None %> | |
525 | <% has_outdated = any([x.outdated for x in line.original.comments]) %> |
|
541 | %if line.original.get_comment_args: | |
|
542 | <% loc = get_comments_for(inline_comments, *line.original.get_comment_args) %> | |||
|
543 | %endif | |||
|
544 | %if loc: | |||
|
545 | <% has_outdated = any([x.outdated for x in loc]) %> | |||
526 | % if has_outdated: |
|
546 | % if has_outdated: | |
527 |
<i title="${_('comments including outdated')}:${len(l |
|
547 | <i title="${_('comments including outdated')}:${len(loc)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
528 | % else: |
|
548 | % else: | |
529 |
<i title="${_('comments')}: ${len(l |
|
549 | <i title="${_('comments')}: ${len(loc)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
530 | % endif |
|
550 | % endif | |
531 | %endif |
|
551 | %endif | |
532 | </div> |
|
552 | </div> | |
@@ -548,20 +568,28 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
548 | ${render_add_comment_button()} |
|
568 | ${render_add_comment_button()} | |
549 | %endif |
|
569 | %endif | |
550 | <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span> |
|
570 | <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span> | |
551 | %if use_comments and line.original.lineno and line.original.comments: |
|
571 | ||
552 |
|
|
572 | %if use_comments and line.original.lineno and loc: | |
|
573 | ${inline_comments_container(loc, inline_comments)} | |||
553 | %endif |
|
574 | %endif | |
|
575 | ||||
554 | </td> |
|
576 | </td> | |
555 | <td class="cb-data ${action_class(line.modified.action)}" |
|
577 | <td class="cb-data ${action_class(line.modified.action)}" | |
556 | data-line-no="${line.modified.lineno}" |
|
578 | data-line-no="${line.modified.lineno}" | |
557 | > |
|
579 | > | |
558 | <div> |
|
580 | <div> | |
559 | %if line.modified.comments: |
|
581 | ||
560 | <% has_outdated = any([x.outdated for x in line.modified.comments]) %> |
|
582 | %if line.modified.get_comment_args: | |
|
583 | <% lmc = get_comments_for(inline_comments, *line.modified.get_comment_args) %> | |||
|
584 | %else: | |||
|
585 | <% lmc = None%> | |||
|
586 | %endif | |||
|
587 | %if lmc: | |||
|
588 | <% has_outdated = any([x.outdated for x in lmc]) %> | |||
561 | % if has_outdated: |
|
589 | % if has_outdated: | |
562 |
<i title="${_('comments including outdated')}:${len(l |
|
590 | <i title="${_('comments including outdated')}:${len(lmc)}" class="icon-comment_toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
563 | % else: |
|
591 | % else: | |
564 |
<i title="${_('comments')}: ${len(l |
|
592 | <i title="${_('comments')}: ${len(lmc)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i> | |
565 | % endif |
|
593 | % endif | |
566 | %endif |
|
594 | %endif | |
567 | </div> |
|
595 | </div> | |
@@ -583,8 +611,8 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
583 | ${render_add_comment_button()} |
|
611 | ${render_add_comment_button()} | |
584 | %endif |
|
612 | %endif | |
585 | <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span> |
|
613 | <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span> | |
586 |
%if use_comments and line.modified.lineno and l |
|
614 | %if use_comments and line.modified.lineno and lmc: | |
587 |
${inline_comments_container(l |
|
615 | ${inline_comments_container(lmc, inline_comments)} | |
588 | %endif |
|
616 | %endif | |
589 | </td> |
|
617 | </td> | |
590 | </tr> |
|
618 | </tr> | |
@@ -592,8 +620,8 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
592 | </%def> |
|
620 | </%def> | |
593 |
|
621 | |||
594 |
|
622 | |||
595 | <%def name="render_hunk_lines_unified(hunk, use_comments=False)"> |
|
623 | <%def name="render_hunk_lines_unified(hunk, use_comments=False, inline_comments=None)"> | |
596 | %for old_line_no, new_line_no, action, content, comments in hunk.unified: |
|
624 | %for old_line_no, new_line_no, action, content, comments_args in hunk.unified: | |
597 | <% |
|
625 | <% | |
598 | old_line_anchor, new_line_anchor = None, None |
|
626 | old_line_anchor, new_line_anchor = None, None | |
599 | if old_line_no: |
|
627 | if old_line_no: | |
@@ -604,6 +632,13 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
604 | <tr class="cb-line"> |
|
632 | <tr class="cb-line"> | |
605 | <td class="cb-data ${action_class(action)}"> |
|
633 | <td class="cb-data ${action_class(action)}"> | |
606 | <div> |
|
634 | <div> | |
|
635 | ||||
|
636 | %if comments_args: | |||
|
637 | <% comments = get_comments_for(inline_comments, *comments_args) %> | |||
|
638 | %else: | |||
|
639 | <% comments = None%> | |||
|
640 | %endif | |||
|
641 | ||||
607 |
|
|
642 | % if comments: | |
608 | <% has_outdated = any([x.outdated for x in comments]) %> |
|
643 | <% has_outdated = any([x.outdated for x in comments]) %> | |
609 | % if has_outdated: |
|
644 | % if has_outdated: | |
@@ -642,7 +677,7 b' from rhodecode.lib.diffs import NEW_FILE' | |||||
642 | %endif |
|
677 | %endif | |
643 | <span class="cb-code">${action} ${content or '' | n}</span> |
|
678 | <span class="cb-code">${action} ${content or '' | n}</span> | |
644 | %if use_comments and comments: |
|
679 | %if use_comments and comments: | |
645 | ${inline_comments_container(comments)} |
|
680 | ${inline_comments_container(comments, inline_comments)} | |
646 | %endif |
|
681 | %endif | |
647 | </td> |
|
682 | </td> | |
648 | </tr> |
|
683 | </tr> |
@@ -570,7 +570,8 b'' | |||||
570 | c.diffset, use_comments=True, |
|
570 | c.diffset, use_comments=True, | |
571 | collapse_when_files_over=30, |
|
571 | collapse_when_files_over=30, | |
572 | disable_new_comments=not c.allowed_to_comment, |
|
572 | disable_new_comments=not c.allowed_to_comment, | |
573 |
deleted_files_comments=c.deleted_files_comments |
|
573 | deleted_files_comments=c.deleted_files_comments, | |
|
574 | inline_comments=c.inline_comments)} | |||
574 | </div> |
|
575 | </div> | |
575 | % else: |
|
576 | % else: | |
576 | ## skipping commits we need to clear the view for missing commits |
|
577 | ## skipping commits we need to clear the view for missing commits |
@@ -44,6 +44,7 b' GENERAL_FORM_DATA = {' | |||||
44 | 'rhodecode_hg_close_branch_before_merging': True, |
|
44 | 'rhodecode_hg_close_branch_before_merging': True, | |
45 | 'rhodecode_git_use_rebase_for_merging': True, |
|
45 | 'rhodecode_git_use_rebase_for_merging': True, | |
46 | 'rhodecode_git_close_branch_before_merging': True, |
|
46 | 'rhodecode_git_close_branch_before_merging': True, | |
|
47 | 'rhodecode_diff_cache': True, | |||
47 | } |
|
48 | } | |
48 |
|
49 | |||
49 |
|
50 |
General Comments 0
You need to be logged in to leave comments.
Login now