##// END OF EJS Templates
pr-versioning: implemented versioning for pull requests....
marcink -
r1368:9a887d01 default
parent child Browse files
Show More
@@ -66,113 +66,10 b' log = logging.getLogger(__name__)'
66
66
67
67
68 class PullrequestsController(BaseRepoController):
68 class PullrequestsController(BaseRepoController):
69
69 def __before__(self):
70 def __before__(self):
70 super(PullrequestsController, self).__before__()
71 super(PullrequestsController, self).__before__()
71
72
72 def _load_compare_data(self, pull_request, inline_comments):
73 """
74 Load context data needed for generating compare diff
75
76 :param pull_request: object related to the request
77 :param enable_comments: flag to determine if comments are included
78 """
79 source_repo = pull_request.source_repo
80 source_ref_id = pull_request.source_ref_parts.commit_id
81
82 target_repo = pull_request.target_repo
83 target_ref_id = pull_request.target_ref_parts.commit_id
84
85 # despite opening commits for bookmarks/branches/tags, we always
86 # convert this to rev to prevent changes after bookmark or branch change
87 c.source_ref_type = 'rev'
88 c.source_ref = source_ref_id
89
90 c.target_ref_type = 'rev'
91 c.target_ref = target_ref_id
92
93 c.source_repo = source_repo
94 c.target_repo = target_repo
95
96 c.fulldiff = bool(request.GET.get('fulldiff'))
97
98 # diff_limit is the old behavior, will cut off the whole diff
99 # if the limit is applied otherwise will just hide the
100 # big files from the front-end
101 diff_limit = self.cut_off_limit_diff
102 file_limit = self.cut_off_limit_file
103
104 pre_load = ["author", "branch", "date", "message"]
105
106 c.commit_ranges = []
107 source_commit = EmptyCommit()
108 target_commit = EmptyCommit()
109 c.missing_requirements = False
110 try:
111 c.commit_ranges = [
112 source_repo.get_commit(commit_id=rev, pre_load=pre_load)
113 for rev in pull_request.revisions]
114
115 c.statuses = source_repo.statuses(
116 [x.raw_id for x in c.commit_ranges])
117
118 target_commit = source_repo.get_commit(
119 commit_id=safe_str(target_ref_id))
120 source_commit = source_repo.get_commit(
121 commit_id=safe_str(source_ref_id))
122 except RepositoryRequirementError:
123 c.missing_requirements = True
124
125 # auto collapse if we have more than limit
126 collapse_limit = diffs.DiffProcessor._collapse_commits_over
127 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
128
129 c.changes = {}
130 c.missing_commits = False
131 if (c.missing_requirements or
132 isinstance(source_commit, EmptyCommit) or
133 source_commit == target_commit):
134 _parsed = []
135 c.missing_commits = True
136 else:
137 vcs_diff = PullRequestModel().get_diff(pull_request)
138 diff_processor = diffs.DiffProcessor(
139 vcs_diff, format='newdiff', diff_limit=diff_limit,
140 file_limit=file_limit, show_full_diff=c.fulldiff)
141
142 _parsed = diff_processor.prepare()
143 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
144
145 included_files = {}
146 for f in _parsed:
147 included_files[f['filename']] = f['stats']
148
149 c.deleted_files = [fname for fname in inline_comments if
150 fname not in included_files]
151
152 c.deleted_files_comments = collections.defaultdict(dict)
153 for fname, per_line_comments in inline_comments.items():
154 if fname in c.deleted_files:
155 c.deleted_files_comments[fname]['stats'] = 0
156 c.deleted_files_comments[fname]['comments'] = list()
157 for lno, comments in per_line_comments.items():
158 c.deleted_files_comments[fname]['comments'].extend(comments)
159
160 def _node_getter(commit):
161 def get_node(fname):
162 try:
163 return commit.get_node(fname)
164 except NodeDoesNotExistError:
165 return None
166 return get_node
167
168 c.diffset = codeblocks.DiffSet(
169 repo_name=c.repo_name,
170 source_repo_name=c.source_repo.repo_name,
171 source_node_getter=_node_getter(target_commit),
172 target_node_getter=_node_getter(source_commit),
173 comments=inline_comments
174 ).render_patchset(_parsed, target_commit.raw_id, source_commit.raw_id)
175
176 def _extract_ordering(self, request):
73 def _extract_ordering(self, request):
177 column_index = safe_int(request.GET.get('order[0][column]'))
74 column_index = safe_int(request.GET.get('order[0][column]'))
178 order_dir = request.GET.get('order[0][dir]', 'desc')
75 order_dir = request.GET.get('order[0][dir]', 'desc')
@@ -693,71 +590,53 b' class PullrequestsController(BaseRepoCon'
693
590
694 pull_request_display_obj = PullRequest.get_pr_display_object(
591 pull_request_display_obj = PullRequest.get_pr_display_object(
695 pull_request_obj, _org_pull_request_obj)
592 pull_request_obj, _org_pull_request_obj)
593
696 return _org_pull_request_obj, pull_request_obj, \
594 return _org_pull_request_obj, pull_request_obj, \
697 pull_request_display_obj, at_version
595 pull_request_display_obj, at_version
698
596
699 def _get_pr_version_changes(self, version, pull_request_latest):
700 """
701 Generate changes commits, and diff data based on the current pr version
702 """
703
704 #TODO(marcink): save those changes as JSON metadata for chaching later.
705
706 # fake the version to add the "initial" state object
707 pull_request_initial = PullRequest.get_pr_display_object(
708 pull_request_latest, pull_request_latest,
709 internal_methods=['get_commit', 'versions'])
710 pull_request_initial.revisions = []
711 pull_request_initial.source_repo.get_commit = types.MethodType(
712 lambda *a, **k: EmptyCommit(), pull_request_initial)
713 pull_request_initial.source_repo.scm_instance = types.MethodType(
714 lambda *a, **k: EmptyRepository(), pull_request_initial)
715
716 _changes_versions = [pull_request_latest] + \
717 list(reversed(c.versions)) + \
718 [pull_request_initial]
719
720 if version == 'latest':
721 index = 0
722 else:
723 for pos, prver in enumerate(_changes_versions):
724 ver = getattr(prver, 'pull_request_version_id', -1)
725 if ver == safe_int(version):
726 index = pos
727 break
728 else:
729 index = 0
730
731 cur_obj = _changes_versions[index]
732 prev_obj = _changes_versions[index + 1]
733
734 old_commit_ids = set(prev_obj.revisions)
735 new_commit_ids = set(cur_obj.revisions)
736
737 changes = PullRequestModel()._calculate_commit_id_changes(
738 old_commit_ids, new_commit_ids)
739
740 old_diff_data, new_diff_data = PullRequestModel()._generate_update_diffs(
741 cur_obj, prev_obj)
742 file_changes = PullRequestModel()._calculate_file_changes(
743 old_diff_data, new_diff_data)
744 return changes, file_changes
745
746 @LoginRequired()
597 @LoginRequired()
747 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
598 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
748 'repository.admin')
599 'repository.admin')
749 def show(self, repo_name, pull_request_id):
600 def show(self, repo_name, pull_request_id):
750 pull_request_id = safe_int(pull_request_id)
601 pull_request_id = safe_int(pull_request_id)
751 version = request.GET.get('version')
602 version = request.GET.get('version')
603 from_version = request.GET.get('from_version') or version
752 merge_checks = request.GET.get('merge_checks')
604 merge_checks = request.GET.get('merge_checks')
605 c.fulldiff = str2bool(request.GET.get('fulldiff'))
606
607 # register for JS templates
608 c.template_context['pull_request_data']['pull_request_id'] = \
609 pull_request_id
753
610
754 (pull_request_latest,
611 (pull_request_latest,
755 pull_request_at_ver,
612 pull_request_at_ver,
756 pull_request_display_obj,
613 pull_request_display_obj,
757 at_version) = self._get_pr_version(pull_request_id, version=version)
614 at_version) = self._get_pr_version(
615 pull_request_id, version=version)
616 versions = pull_request_display_obj.versions()
617
618 c.at_version = at_version
619 c.at_version_num = (at_version
620 if at_version and at_version != 'latest'
621 else None)
622 c.at_version_pos = ChangesetComment.get_index_from_version(
623 c.at_version_num, versions)
758
624
759 c.template_context['pull_request_data']['pull_request_id'] = \
625 (prev_pull_request_latest,
760 pull_request_id
626 prev_pull_request_at_ver,
627 prev_pull_request_display_obj,
628 prev_at_version) = self._get_pr_version(
629 pull_request_id, version=from_version)
630
631 c.from_version = prev_at_version
632 c.from_version_num = (prev_at_version
633 if prev_at_version and prev_at_version != 'latest'
634 else None)
635 c.from_version_pos = ChangesetComment.get_index_from_version(
636 c.from_version_num, versions)
637
638 # define if we're in COMPARE mode or VIEW at version mode
639 compare = at_version != prev_at_version
761
640
762 # pull_requests repo_name we opened it against
641 # pull_requests repo_name we opened it against
763 # ie. target_repo must match
642 # ie. target_repo must match
@@ -767,12 +646,12 b' class PullrequestsController(BaseRepoCon'
767 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(
646 c.shadow_clone_url = PullRequestModel().get_shadow_clone_url(
768 pull_request_at_ver)
647 pull_request_at_ver)
769
648
770 c.ancestor = None # TODO: add ancestor here
649 c.ancestor = None # empty ancestor hidden in display
771 c.pull_request = pull_request_display_obj
650 c.pull_request = pull_request_display_obj
772 c.pull_request_latest = pull_request_latest
651 c.pull_request_latest = pull_request_latest
773
652
774 pr_closed = pull_request_latest.is_closed()
653 pr_closed = pull_request_latest.is_closed()
775 if at_version and not at_version == 'latest':
654 if compare or (at_version and not at_version == 'latest'):
776 c.allowed_to_change_status = False
655 c.allowed_to_change_status = False
777 c.allowed_to_update = False
656 c.allowed_to_update = False
778 c.allowed_to_merge = False
657 c.allowed_to_merge = False
@@ -789,35 +668,50 b' class PullrequestsController(BaseRepoCon'
789 pull_request_latest, c.rhodecode_user) and not pr_closed
668 pull_request_latest, c.rhodecode_user) and not pr_closed
790 c.allowed_to_comment = not pr_closed
669 c.allowed_to_comment = not pr_closed
791
670
792 cc_model = CommentsModel()
671 # check merge capabilities
672 _merge_check = MergeCheck.validate(
673 pull_request_latest, user=c.rhodecode_user)
674 c.pr_merge_errors = _merge_check.error_details
675 c.pr_merge_possible = not _merge_check.failed
676 c.pr_merge_message = _merge_check.merge_msg
793
677
678 if merge_checks:
679 return render('/pullrequests/pullrequest_merge_checks.mako')
680
681 comments_model = CommentsModel()
682
683 # reviewers and statuses
794 c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses()
684 c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses()
685 allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers]
795 c.pull_request_review_status = pull_request_at_ver.calculated_review_status()
686 c.pull_request_review_status = pull_request_at_ver.calculated_review_status()
796
687
797 c.versions = pull_request_display_obj.versions()
798 c.at_version = at_version
799 c.at_version_num = at_version if at_version and at_version != 'latest' else None
800 c.at_version_pos = ChangesetComment.get_index_from_version(
801 c.at_version_num, c.versions)
802
803 # GENERAL COMMENTS with versions #
688 # GENERAL COMMENTS with versions #
804 q = cc_model._all_general_comments_of_pull_request(pull_request_latest)
689 q = comments_model._all_general_comments_of_pull_request(pull_request_latest)
690 q = q.order_by(ChangesetComment.comment_id.asc())
805 general_comments = q.order_by(ChangesetComment.pull_request_version_id.asc())
691 general_comments = q.order_by(ChangesetComment.pull_request_version_id.asc())
806
692
807 # pick comments we want to render at current version
693 # pick comments we want to render at current version
808 c.comment_versions = cc_model.aggregate_comments(
694 c.comment_versions = comments_model.aggregate_comments(
809 general_comments, c.versions, c.at_version_num)
695 general_comments, versions, c.at_version_num)
810 c.comments = c.comment_versions[c.at_version_num]['until']
696 c.comments = c.comment_versions[c.at_version_num]['until']
811
697
812 # INLINE COMMENTS with versions #
698 # INLINE COMMENTS with versions #
813 q = cc_model._all_inline_comments_of_pull_request(pull_request_latest)
699 q = comments_model._all_inline_comments_of_pull_request(pull_request_latest)
700 q = q.order_by(ChangesetComment.comment_id.asc())
814 inline_comments = q.order_by(ChangesetComment.pull_request_version_id.asc())
701 inline_comments = q.order_by(ChangesetComment.pull_request_version_id.asc())
815 c.inline_versions = cc_model.aggregate_comments(
702 c.inline_versions = comments_model.aggregate_comments(
816 inline_comments, c.versions, c.at_version_num, inline=True)
703 inline_comments, versions, c.at_version_num, inline=True)
704
705 # inject latest version
706 latest_ver = PullRequest.get_pr_display_object(
707 pull_request_latest, pull_request_latest)
708
709 c.versions = versions + [latest_ver]
817
710
818 # if we use version, then do not show later comments
711 # if we use version, then do not show later comments
819 # than current version
712 # than current version
820 display_inline_comments = collections.defaultdict(lambda: collections.defaultdict(list))
713 display_inline_comments = collections.defaultdict(
714 lambda: collections.defaultdict(list))
821 for co in inline_comments:
715 for co in inline_comments:
822 if c.at_version_num:
716 if c.at_version_num:
823 # pick comments that are at least UPTO given version, so we
717 # pick comments that are at least UPTO given version, so we
@@ -831,17 +725,126 b' class PullrequestsController(BaseRepoCon'
831 if should_render:
725 if should_render:
832 display_inline_comments[co.f_path][co.line_no].append(co)
726 display_inline_comments[co.f_path][co.line_no].append(co)
833
727
834 _merge_check = MergeCheck.validate(
728 # load diff data into template context, if we use compare mode then
835 pull_request_latest, user=c.rhodecode_user)
729 # diff is calculated based on changes between versions of PR
836 c.pr_merge_errors = _merge_check.error_details
730
837 c.pr_merge_possible = not _merge_check.failed
731 source_repo = pull_request_at_ver.source_repo
838 c.pr_merge_message = _merge_check.merge_msg
732 source_ref_id = pull_request_at_ver.source_ref_parts.commit_id
733
734 target_repo = pull_request_at_ver.target_repo
735 target_ref_id = pull_request_at_ver.target_ref_parts.commit_id
736
737 if compare:
738 # in compare switch the diff base to latest commit from prev version
739 target_ref_id = prev_pull_request_display_obj.revisions[0]
740
741 # despite opening commits for bookmarks/branches/tags, we always
742 # convert this to rev to prevent changes after bookmark or branch change
743 c.source_ref_type = 'rev'
744 c.source_ref = source_ref_id
745
746 c.target_ref_type = 'rev'
747 c.target_ref = target_ref_id
748
749 c.source_repo = source_repo
750 c.target_repo = target_repo
751
752 # diff_limit is the old behavior, will cut off the whole diff
753 # if the limit is applied otherwise will just hide the
754 # big files from the front-end
755 diff_limit = self.cut_off_limit_diff
756 file_limit = self.cut_off_limit_file
757
758 c.commit_ranges = []
759 source_commit = EmptyCommit()
760 target_commit = EmptyCommit()
761 c.missing_requirements = False
762
763 # try first shadow repo, fallback to regular repo
764 try:
765 commits_source_repo = pull_request_latest.get_shadow_repo()
766 except Exception:
767 log.debug('Failed to get shadow repo', exc_info=True)
768 commits_source_repo = source_repo.scm_instance()
769
770 c.commits_source_repo = commits_source_repo
771 commit_cache = {}
772 try:
773 pre_load = ["author", "branch", "date", "message"]
774 show_revs = pull_request_at_ver.revisions
775 for rev in show_revs:
776 comm = commits_source_repo.get_commit(
777 commit_id=rev, pre_load=pre_load)
778 c.commit_ranges.append(comm)
779 commit_cache[comm.raw_id] = comm
839
780
840 if merge_checks:
781 target_commit = commits_source_repo.get_commit(
841 return render('/pullrequests/pullrequest_merge_checks.mako')
782 commit_id=safe_str(target_ref_id))
783 source_commit = commits_source_repo.get_commit(
784 commit_id=safe_str(source_ref_id))
785 except CommitDoesNotExistError:
786 pass
787 except RepositoryRequirementError:
788 log.warning(
789 'Failed to get all required data from repo', exc_info=True)
790 c.missing_requirements = True
791
792 c.statuses = source_repo.statuses(
793 [x.raw_id for x in c.commit_ranges])
794
795 # auto collapse if we have more than limit
796 collapse_limit = diffs.DiffProcessor._collapse_commits_over
797 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
798 c.compare_mode = compare
799
800 c.missing_commits = False
801 if (c.missing_requirements or isinstance(source_commit, EmptyCommit)
802 or source_commit == target_commit):
803
804 c.missing_commits = True
805 else:
806 vcs_diff = PullRequestModel().get_diff(
807 commits_source_repo, source_ref_id, target_ref_id)
808
809 diff_processor = diffs.DiffProcessor(
810 vcs_diff, format='newdiff', diff_limit=diff_limit,
811 file_limit=file_limit, show_full_diff=c.fulldiff)
842
812
843 # load compare data into template context
813 _parsed = diff_processor.prepare()
844 self._load_compare_data(pull_request_at_ver, display_inline_comments)
814 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
815
816 def _node_getter(commit):
817 def get_node(fname):
818 try:
819 return commit.get_node(fname)
820 except NodeDoesNotExistError:
821 return None
822
823 return get_node
824
825 diffset = codeblocks.DiffSet(
826 repo_name=c.repo_name,
827 source_repo_name=c.source_repo.repo_name,
828 source_node_getter=_node_getter(target_commit),
829 target_node_getter=_node_getter(source_commit),
830 comments=display_inline_comments
831 )
832 c.diffset = diffset.render_patchset(
833 _parsed, target_commit.raw_id, source_commit.raw_id)
834
835 # calculate removed files that are bound to comments
836 comment_deleted_files = [
837 fname for fname in display_inline_comments
838 if fname not in c.diffset.file_stats]
839
840 c.deleted_files_comments = collections.defaultdict(dict)
841 for fname, per_line_comments in display_inline_comments.items():
842 if fname in comment_deleted_files:
843 c.deleted_files_comments[fname]['stats'] = 0
844 c.deleted_files_comments[fname]['comments'] = list()
845 for lno, comments in per_line_comments.items():
846 c.deleted_files_comments[fname]['comments'].extend(
847 comments)
845
848
846 # this is a hack to properly display links, when creating PR, the
849 # this is a hack to properly display links, when creating PR, the
847 # compare view and others uses different notation, and
850 # compare view and others uses different notation, and
@@ -856,21 +859,55 b' class PullrequestsController(BaseRepoCon'
856 statuses = ChangesetStatus.STATUSES
859 statuses = ChangesetStatus.STATUSES
857 c.commit_statuses = statuses
860 c.commit_statuses = statuses
858
861
859 c.changes = None
862 c.show_version_changes = not pr_closed
860 c.file_changes = None
863 if c.show_version_changes:
864 cur_obj = pull_request_at_ver
865 prev_obj = prev_pull_request_at_ver
866
867 old_commit_ids = prev_obj.revisions
868 new_commit_ids = cur_obj.revisions
869 commit_changes = PullRequestModel()._calculate_commit_id_changes(
870 old_commit_ids, new_commit_ids)
871 c.commit_changes_summary = commit_changes
872
873 # calculate the diff for commits between versions
874 c.commit_changes = []
875 mark = lambda cs, fw: list(
876 h.itertools.izip_longest([], cs, fillvalue=fw))
877 for c_type, raw_id in mark(commit_changes.added, 'a') \
878 + mark(commit_changes.removed, 'r') \
879 + mark(commit_changes.common, 'c'):
861
880
862 c.show_version_changes = 1 # control flag, not used yet
881 if raw_id in commit_cache:
882 commit = commit_cache[raw_id]
883 else:
884 try:
885 commit = commits_source_repo.get_commit(raw_id)
886 except CommitDoesNotExistError:
887 # in case we fail extracting still use "dummy" commit
888 # for display in commit diff
889 commit = h.AttributeDict(
890 {'raw_id': raw_id,
891 'message': 'EMPTY or MISSING COMMIT'})
892 c.commit_changes.append([c_type, commit])
863
893
864 if at_version and c.show_version_changes:
894 # current user review statuses for each version
865 c.changes, c.file_changes = self._get_pr_version_changes(
895 c.review_versions = {}
866 version, pull_request_latest)
896 if c.rhodecode_user.user_id in allowed_reviewers:
897 for co in general_comments:
898 if co.author.user_id == c.rhodecode_user.user_id:
899 # each comment has a status change
900 status = co.status_change
901 if status:
902 _ver_pr = status[0].comment.pull_request_version_id
903 c.review_versions[_ver_pr] = status[0]
867
904
868 return render('/pullrequests/pullrequest_show.mako')
905 return render('/pullrequests/pullrequest_show.mako')
869
906
870 @LoginRequired()
907 @LoginRequired()
871 @NotAnonymous()
908 @NotAnonymous()
872 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
909 @HasRepoPermissionAnyDecorator(
873 'repository.admin')
910 'repository.read', 'repository.write', 'repository.admin')
874 @auth.CSRFRequired()
911 @auth.CSRFRequired()
875 @jsonify
912 @jsonify
876 def comment(self, repo_name, pull_request_id):
913 def comment(self, repo_name, pull_request_id):
@@ -382,6 +382,7 b' class DiffSet(object):'
382 lines_deleted=0,
382 lines_deleted=0,
383 changed_files=0,
383 changed_files=0,
384 files=[],
384 files=[],
385 file_stats={},
385 limited_diff=isinstance(patchset, LimitedDiffContainer),
386 limited_diff=isinstance(patchset, LimitedDiffContainer),
386 repo_name=self.repo_name,
387 repo_name=self.repo_name,
387 source_repo_name=self.source_repo_name,
388 source_repo_name=self.source_repo_name,
@@ -389,6 +390,7 b' class DiffSet(object):'
389 target_ref=target_ref,
390 target_ref=target_ref,
390 ))
391 ))
391 for patch in patchset:
392 for patch in patchset:
393 diffset.file_stats[patch['filename']] = patch['stats']
392 filediff = self.render_patch(patch)
394 filediff = self.render_patch(patch)
393 filediff.diffset = diffset
395 filediff.diffset = diffset
394 diffset.files.append(filediff)
396 diffset.files.append(filediff)
@@ -37,6 +37,7 b' import time'
37 import string
37 import string
38 import hashlib
38 import hashlib
39 import pygments
39 import pygments
40 import itertools
40
41
41 from datetime import datetime
42 from datetime import datetime
42 from functools import partial
43 from functools import partial
@@ -3028,7 +3028,7 b' class ChangesetStatus(Base, BaseModel):'
3028 pull_request = relationship('PullRequest', lazy='joined')
3028 pull_request = relationship('PullRequest', lazy='joined')
3029
3029
3030 def __unicode__(self):
3030 def __unicode__(self):
3031 return u"<%s('%s[%s]:%s')>" % (
3031 return u"<%s('%s[v%s]:%s')>" % (
3032 self.__class__.__name__,
3032 self.__class__.__name__,
3033 self.status, self.version, self.author
3033 self.status, self.version, self.author
3034 )
3034 )
@@ -3254,7 +3254,6 b' class PullRequest(Base, _PullRequestBase'
3254 cascade="all, delete, delete-orphan",
3254 cascade="all, delete, delete-orphan",
3255 lazy='dynamic')
3255 lazy='dynamic')
3256
3256
3257
3258 @classmethod
3257 @classmethod
3259 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
3258 def get_pr_display_object(cls, pull_request_obj, org_pull_request_obj,
3260 internal_methods=None):
3259 internal_methods=None):
@@ -3290,6 +3289,10 b' class PullRequest(Base, _PullRequestBase'
3290 def is_closed(self):
3289 def is_closed(self):
3291 return pull_request_obj.is_closed()
3290 return pull_request_obj.is_closed()
3292
3291
3292 @property
3293 def pull_request_version_id(self):
3294 return getattr(pull_request_obj, 'pull_request_version_id', None)
3295
3293 attrs = StrictAttributeDict(pull_request_obj.get_api_data())
3296 attrs = StrictAttributeDict(pull_request_obj.get_api_data())
3294
3297
3295 attrs.author = StrictAttributeDict(
3298 attrs.author = StrictAttributeDict(
@@ -235,7 +235,7 b' class NotificationModel(BaseModel):'
235 .filter(UserNotification.notification == notification)\
235 .filter(UserNotification.notification == notification)\
236 .filter(UserNotification.user == user).scalar()
236 .filter(UserNotification.user == user).scalar()
237
237
238 def make_description(self, notification, show_age=True):
238 def make_description(self, notification, show_age=True, translate=None):
239 """
239 """
240 Creates a human readable description based on properties
240 Creates a human readable description based on properties
241 of notification object
241 of notification object
@@ -273,6 +273,8 b' class NotificationModel(BaseModel):'
273 if show_age:
273 if show_age:
274 template = templates[0]
274 template = templates[0]
275 date_or_age = h.age(notification.created_on)
275 date_or_age = h.age(notification.created_on)
276 if translate:
277 date_or_age = translate(date_or_age)
276 else:
278 else:
277 template = templates[1]
279 template = templates[1]
278 date_or_age = h.format_date(notification.created_on)
280 date_or_age = h.format_date(notification.created_on)
@@ -50,7 +50,7 b' from rhodecode.model.changeset_status im'
50 from rhodecode.model.comment import CommentsModel
50 from rhodecode.model.comment import CommentsModel
51 from rhodecode.model.db import (
51 from rhodecode.model.db import (
52 PullRequest, PullRequestReviewers, ChangesetStatus,
52 PullRequest, PullRequestReviewers, ChangesetStatus,
53 PullRequestVersion, ChangesetComment)
53 PullRequestVersion, ChangesetComment, Repository)
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55 from rhodecode.model.notification import NotificationModel, \
55 from rhodecode.model.notification import NotificationModel, \
56 EmailNotificationModel
56 EmailNotificationModel
@@ -728,13 +728,23 b' class PullRequestModel(BaseModel):'
728 return version
728 return version
729
729
730 def _generate_update_diffs(self, pull_request, pull_request_version):
730 def _generate_update_diffs(self, pull_request, pull_request_version):
731
731 diff_context = (
732 diff_context = (
732 self.DIFF_CONTEXT +
733 self.DIFF_CONTEXT +
733 CommentsModel.needed_extra_diff_context())
734 CommentsModel.needed_extra_diff_context())
735
736 source_repo = pull_request_version.source_repo
737 source_ref_id = pull_request_version.source_ref_parts.commit_id
738 target_ref_id = pull_request_version.target_ref_parts.commit_id
734 old_diff = self._get_diff_from_pr_or_version(
739 old_diff = self._get_diff_from_pr_or_version(
735 pull_request_version, context=diff_context)
740 source_repo, source_ref_id, target_ref_id, context=diff_context)
741
742 source_repo = pull_request.source_repo
743 source_ref_id = pull_request.source_ref_parts.commit_id
744 target_ref_id = pull_request.target_ref_parts.commit_id
745
736 new_diff = self._get_diff_from_pr_or_version(
746 new_diff = self._get_diff_from_pr_or_version(
737 pull_request, context=diff_context)
747 source_repo, source_ref_id, target_ref_id, context=diff_context)
738
748
739 old_diff_data = diffs.DiffProcessor(old_diff)
749 old_diff_data = diffs.DiffProcessor(old_diff)
740 old_diff_data.prepare()
750 old_diff_data.prepare()
@@ -768,10 +778,11 b' class PullRequestModel(BaseModel):'
768 Session().add(comment)
778 Session().add(comment)
769
779
770 def _calculate_commit_id_changes(self, old_ids, new_ids):
780 def _calculate_commit_id_changes(self, old_ids, new_ids):
771 added = new_ids.difference(old_ids)
781 added = [x for x in new_ids if x not in old_ids]
772 common = old_ids.intersection(new_ids)
782 common = [x for x in new_ids if x in old_ids]
773 removed = old_ids.difference(new_ids)
783 removed = [x for x in old_ids if x not in new_ids]
774 return ChangeTuple(added, common, removed)
784 total = new_ids
785 return ChangeTuple(added, common, removed, total)
775
786
776 def _calculate_file_changes(self, old_diff_data, new_diff_data):
787 def _calculate_file_changes(self, old_diff_data, new_diff_data):
777
788
@@ -1261,20 +1272,20 b' class PullRequestModel(BaseModel):'
1261 raise EmptyRepositoryError()
1272 raise EmptyRepositoryError()
1262 return groups, selected
1273 return groups, selected
1263
1274
1264 def get_diff(self, pull_request, context=DIFF_CONTEXT):
1275 def get_diff(self, source_repo, source_ref_id, target_ref_id, context=DIFF_CONTEXT):
1265 pull_request = self.__get_pull_request(pull_request)
1276 return self._get_diff_from_pr_or_version(
1266 return self._get_diff_from_pr_or_version(pull_request, context=context)
1277 source_repo, source_ref_id, target_ref_id, context=context)
1267
1278
1268 def _get_diff_from_pr_or_version(self, pr_or_version, context):
1279 def _get_diff_from_pr_or_version(
1269 source_repo = pr_or_version.source_repo
1280 self, source_repo, source_ref_id, target_ref_id, context):
1270
1271 # we swap org/other ref since we run a simple diff on one repo
1272 target_ref_id = pr_or_version.target_ref_parts.commit_id
1273 source_ref_id = pr_or_version.source_ref_parts.commit_id
1274 target_commit = source_repo.get_commit(
1281 target_commit = source_repo.get_commit(
1275 commit_id=safe_str(target_ref_id))
1282 commit_id=safe_str(target_ref_id))
1276 source_commit = source_repo.get_commit(commit_id=safe_str(source_ref_id))
1283 source_commit = source_repo.get_commit(
1277 vcs_repo = source_repo.scm_instance()
1284 commit_id=safe_str(source_ref_id))
1285 if isinstance(source_repo, Repository):
1286 vcs_repo = source_repo.scm_instance()
1287 else:
1288 vcs_repo = source_repo
1278
1289
1279 # TODO: johbo: In the context of an update, we cannot reach
1290 # TODO: johbo: In the context of an update, we cannot reach
1280 # the old commit anymore with our normal mechanisms. It needs
1291 # the old commit anymore with our normal mechanisms. It needs
@@ -1403,7 +1414,7 b' class MergeCheck(object):'
1403
1414
1404
1415
1405 ChangeTuple = namedtuple('ChangeTuple',
1416 ChangeTuple = namedtuple('ChangeTuple',
1406 ['added', 'common', 'removed'])
1417 ['added', 'common', 'removed', 'total'])
1407
1418
1408 FileChangeTuple = namedtuple('FileChangeTuple',
1419 FileChangeTuple = namedtuple('FileChangeTuple',
1409 ['added', 'modified', 'removed'])
1420 ['added', 'modified', 'removed'])
@@ -1393,6 +1393,14 b' table.integrations {'
1393 }
1393 }
1394 }
1394 }
1395
1395
1396 .subtitle-compare {
1397 margin: -15px 0px 0px 0px;
1398 }
1399
1400 .comments-summary-td {
1401 border-top: 1px dashed @grey5;
1402 }
1403
1396 // new entry in group_members
1404 // new entry in group_members
1397 .td-author-new-entry {
1405 .td-author-new-entry {
1398 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1406 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
@@ -1422,6 +1430,57 b' table.integrations {'
1422 margin-left: 5px;
1430 margin-left: 5px;
1423 }
1431 }
1424
1432
1433 .compare_view_commits {
1434 .color-a {
1435 color: @alert1;
1436 }
1437
1438 .color-c {
1439 color: @color3;
1440 }
1441
1442 .color-r {
1443 color: @color5;
1444 }
1445
1446 .color-a-bg {
1447 background-color: @alert1;
1448 }
1449
1450 .color-c-bg {
1451 background-color: @alert3;
1452 }
1453
1454 .color-r-bg {
1455 background-color: @alert2;
1456 }
1457
1458 .color-a-border {
1459 border: 1px solid @alert1;
1460 }
1461
1462 .color-c-border {
1463 border: 1px solid @alert3;
1464 }
1465
1466 .color-r-border {
1467 border: 1px solid @alert2;
1468 }
1469
1470 .commit-change-indicator {
1471 width: 15px;
1472 height: 15px;
1473 position: relative;
1474 left: 15px;
1475 }
1476
1477 .commit-change-content {
1478 text-align: center;
1479 vertical-align: middle;
1480 line-height: 15px;
1481 }
1482 }
1483
1425 .compare_view_files {
1484 .compare_view_files {
1426 width: 100%;
1485 width: 100%;
1427
1486
@@ -1663,10 +1722,27 b' BIN_FILENODE = 7'
1663 }
1722 }
1664
1723
1665 .pr-versions {
1724 .pr-versions {
1666 position: relative;
1725 font-size: 1.1em;
1667 top: 6px;
1726
1727 table {
1728 padding: 0px 5px;
1729 }
1730
1731 td {
1732 line-height: 15px;
1733 }
1734
1735 .flag_status {
1736 margin: 0;
1737 }
1738
1739 .compare-radio-button {
1740 position: relative;
1741 top: -3px;
1742 }
1668 }
1743 }
1669
1744
1745
1670 #close_pull_request {
1746 #close_pull_request {
1671 margin-right: 0px;
1747 margin-right: 0px;
1672 }
1748 }
@@ -310,20 +310,42 b''
310 }
310 }
311 }
311 }
312
312
313 $('.expand_commit').on('click',function(e){
313 $('.expand_commit').on('click', function (e) {
314 var target_expand = $(this);
314 var target_expand = $(this);
315 var cid = target_expand.data('commitId');
315 var cid = target_expand.data('commitId');
316
316
317 if (target_expand.hasClass('open')){
317 if (target_expand.hasClass('open')) {
318 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
318 $('#c-' + cid).css({
319 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
319 'height': '1.5em',
320 target_expand.removeClass('open');
320 'white-space': 'nowrap',
321 }
321 'text-overflow': 'ellipsis',
322 else {
322 'overflow': 'hidden'
323 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
323 });
324 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
324 $('#t-' + cid).css({
325 target_expand.addClass('open');
325 'height': 'auto',
326 }
326 'line-height': '.9em',
327 'text-overflow': 'ellipsis',
328 'overflow': 'hidden',
329 'white-space': 'nowrap'
330 });
331 target_expand.removeClass('open');
332 }
333 else {
334 $('#c-' + cid).css({
335 'height': 'auto',
336 'white-space': 'pre-line',
337 'text-overflow': 'initial',
338 'overflow': 'visible'
339 });
340 $('#t-' + cid).css({
341 'height': 'auto',
342 'max-height': 'none',
343 'text-overflow': 'initial',
344 'overflow': 'visible',
345 'white-space': 'normal'
346 });
347 target_expand.addClass('open');
348 }
327 // redraw the graph
349 // redraw the graph
328 graph_options.height = $("#changesets").height();
350 graph_options.height = $("#changesets").height();
329 $("canvas").remove();
351 $("canvas").remove();
@@ -134,13 +134,6 b' collapse_all = len(diffset.files) > coll'
134 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
134 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
135 %endif
135 %endif
136
136
137 <% at_ver = getattr(c, 'at_version_pos', None) %>
138 % if at_ver:
139 <div class="pull-right">
140 ${_('Showing changes at version %d') % at_ver}
141 </div>
142 % endif
143
144 </h2>
137 </h2>
145 </div>
138 </div>
146
139
@@ -1,4 +1,5 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
2
3
3 <%def name="title()">
4 <%def name="title()">
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
5 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
@@ -165,47 +166,23 b''
165 </div>
166 </div>
166
167
167 <div class="field">
168 <div class="field">
168 <div class="label-summary">
169 <div class="label-summary">
169 <label>${_('Versions')} (${len(c.versions)+1}):</label>
170 <label>${_('Versions')}:</label>
170 </div>
171 </div>
172
173 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
174 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
171
175
172 <div class="pr-versions">
176 <div class="pr-versions">
173 % if c.show_version_changes:
177 % if c.show_version_changes:
178 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
179 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
180 <div id="show-pr-versions" class="input btn btn-link" onclick="return versionController.toggleVersionView(this)"
181 data-toggle-on="${ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}"
182 data-toggle-off="${_('Hide all versions of this pull request')}">
183 ${ungettext('{} version available for this pull request, show it.', '{} versions available for this pull request, show them.', len(c.versions)).format(len(c.versions))}
184 </div>
174 <table>
185 <table>
175 ## CURRENTLY SELECT PR VERSION
176 <tr class="version-pr" style="display: ${'' if c.at_version_num is None else 'none'}">
177 <td>
178 % if c.at_version_num is None:
179 <i class="icon-ok link"></i>
180 % else:
181 <i class="icon-comment"></i>
182 <code>
183 ${len(c.comment_versions[None]['at'])}/${len(c.inline_versions[None]['at'])}
184 </code>
185 % endif
186 </td>
187 <td>
188 <code>
189 % if c.versions:
190 <a href="${h.url.current(version='latest')}">${_('latest')}</a>
191 % else:
192 ${_('initial')}
193 % endif
194 </code>
195 </td>
196 <td>
197 <code>${c.pull_request_latest.source_ref_parts.commit_id[:6]}</code>
198 </td>
199 <td>
200 ${_('created')} ${h.age_component(c.pull_request_latest.updated_on)}
201 </td>
202 <td align="right">
203 % if c.versions and c.at_version_num in [None, 'latest']:
204 <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('Show all versions')}</span>
205 % endif
206 </td>
207 </tr>
208
209 ## SHOW ALL VERSIONS OF PR
186 ## SHOW ALL VERSIONS OF PR
210 <% ver_pr = None %>
187 <% ver_pr = None %>
211
188
@@ -213,46 +190,52 b''
213 <% ver_pos = data[0] %>
190 <% ver_pos = data[0] %>
214 <% ver = data[1] %>
191 <% ver = data[1] %>
215 <% ver_pr = ver.pull_request_version_id %>
192 <% ver_pr = ver.pull_request_version_id %>
193 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
216
194
217 <tr class="version-pr" style="display: ${'' if c.at_version_num == ver_pr else 'none'}">
195 <tr class="version-pr" style="display: ${display_row}">
196 <td>
197 <code>
198 <a href="${h.url.current(version=ver_pr or 'latest')}">v${ver_pos}</a>
199 </code>
200 </td>
218 <td>
201 <td>
219 % if c.at_version_num == ver_pr:
202 <input ${'checked="checked"' if c.from_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
220 <i class="icon-ok link"></i>
203 <input ${'checked="checked"' if c.at_version_num == ver_pr else ''} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
221 % else:
204 </td>
205 <td>
206 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
207 <div class="${'flag_status %s' % review_status} tooltip pull-left" title="${_('Your review status at this version')}">
208 </div>
209 </td>
210 <td>
211 % if c.at_version_num != ver_pr:
222 <i class="icon-comment"></i>
212 <i class="icon-comment"></i>
223 <code class="tooltip" title="${_('Comment from pull request version {0}, general:{1} inline{2}').format(ver_pos, len(c.comment_versions[ver_pr]['at']), len(c.inline_versions[ver_pr]['at']))}">
213 <code class="tooltip" title="${_('Comment from pull request version {0}, general:{1} inline:{2}').format(ver_pos, len(c.comment_versions[ver_pr]['at']), len(c.inline_versions[ver_pr]['at']))}">
224 ${len(c.comment_versions[ver_pr]['at'])}/${len(c.inline_versions[ver_pr]['at'])}
214 ${len(c.comment_versions[ver_pr]['at'])}/${len(c.inline_versions[ver_pr]['at'])}
225 </code>
215 </code>
226 % endif
216 % endif
227 </td>
217 </td>
228 <td>
218 <td>
229 <code>
219 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
230 <a href="${h.url.current(version=ver_pr)}">v${ver_pos}</a>
231 </code>
232 </td>
220 </td>
233 <td>
221 <td>
234 <code>${ver.source_ref_parts.commit_id[:6]}</code>
222 ${h.age_component(ver.updated_on)}
235 </td>
236 <td>
237 ${_('created')} ${h.age_component(ver.updated_on)}
238 </td>
239 <td align="right">
240 % if c.at_version_num == ver_pr:
241 <span id="show-pr-versions" class="btn btn-link" onclick="$('.version-pr').show(); $(this).hide(); return false">${_('Show all versions')}</span>
242 % endif
243 </td>
223 </td>
244 </tr>
224 </tr>
245 % endfor
225 % endfor
246
226
247 ## show comment/inline comments summary
248 <tr>
227 <tr>
249 <td>
228 <td colspan="5">
229 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none" data-label-text="${_('show changes between versions')}">
230 ${_('show changes between versions')}
231 </button>
250 </td>
232 </td>
233 </tr>
251
234
252 <td colspan="4" style="border-top: 1px dashed #dbd9da">
235 ## show comment/inline comments summary
253 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
236 <%def name="comments_summary()">
254 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
237 <tr>
255
238 <td colspan="6" class="comments-summary-td">
256
239
257 % if c.at_version:
240 % if c.at_version:
258 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['display']) %>
241 <% inline_comm_count_ver = len(c.inline_versions[c.at_version_num]['display']) %>
@@ -264,6 +247,7 b''
264 ${_('Comments for this pull request')}:
247 ${_('Comments for this pull request')}:
265 % endif
248 % endif
266
249
250
267 %if general_comm_count_ver:
251 %if general_comm_count_ver:
268 <a href="#comments">${_("%d General ") % general_comm_count_ver}</a>
252 <a href="#comments">${_("%d General ") % general_comm_count_ver}</a>
269 %else:
253 %else:
@@ -285,37 +269,18 b''
285 %endif
269 %endif
286 </td>
270 </td>
287 </tr>
271 </tr>
288
272 </%def>
289 <tr>
273 ${comments_summary()}
290 <td></td>
291 <td colspan="4">
292 % if c.at_version:
293 <pre>
294 Changed commits:
295 * added: ${len(c.changes.added)}
296 * removed: ${len(c.changes.removed)}
297
298 % if not (c.file_changes.added+c.file_changes.modified+c.file_changes.removed):
299 No file changes found
300 % else:
301 Changed files:
302 %for file_name in c.file_changes.added:
303 * A <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a>
304 %endfor
305 %for file_name in c.file_changes.modified:
306 * M <a href="#${'a_' + h.FID('', file_name)}">${file_name}</a>
307 %endfor
308 %for file_name in c.file_changes.removed:
309 * R ${file_name}
310 %endfor
311 % endif
312 </pre>
313 % endif
314 </td>
315 </tr>
316 </table>
274 </table>
317 % else:
275 % else:
276 <div class="input">
318 ${_('Pull request versions not available')}.
277 ${_('Pull request versions not available')}.
278 </div>
279 <div>
280 <table>
281 ${comments_summary()}
282 </table>
283 </div>
319 % endif
284 % endif
320 </div>
285 </div>
321 </div>
286 </div>
@@ -426,7 +391,15 b' Changed files:'
426 </div>
391 </div>
427 </div>
392 </div>
428 % endif
393 % endif
429 <div class="compare_view_commits_title">
394
395 <div class="compare_view_commits_title">
396 % if not c.compare_mode:
397
398 % if c.at_version_pos:
399 <h4>
400 ${_('Showing changes at v%d, commenting is disabled.') % c.at_version_pos}
401 </h4>
402 % endif
430
403
431 <div class="pull-left">
404 <div class="pull-left">
432 <div class="btn-group">
405 <div class="btn-group">
@@ -453,11 +426,113 b' Changed files:'
453 % endif
426 % endif
454
427
455 </div>
428 </div>
456
429 % endif
457 </div>
430 </div>
458
431
459 % if not c.missing_commits:
432 % if not c.missing_commits:
460 <%include file="/compare/compare_commits.mako" />
433 % if c.compare_mode:
434 % if c.at_version:
435 <h4>
436 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_pos, ver_to=c.at_version_pos if c.at_version_pos else 'latest')}:
437 </h4>
438
439 <div class="subtitle-compare">
440 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
441 </div>
442
443 <div class="container">
444 <table class="rctable compare_view_commits">
445 <tr>
446 <th></th>
447 <th>${_('Time')}</th>
448 <th>${_('Author')}</th>
449 <th>${_('Commit')}</th>
450 <th></th>
451 <th>${_('Description')}</th>
452 </tr>
453
454 % for c_type, commit in c.commit_changes:
455 % if c_type in ['a', 'r']:
456 <%
457 if c_type == 'a':
458 cc_title = _('Commit added in displayed changes')
459 elif c_type == 'r':
460 cc_title = _('Commit removed in displayed changes')
461 else:
462 cc_title = ''
463 %>
464 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
465 <td>
466 <div class="commit-change-indicator color-${c_type}-border">
467 <div class="commit-change-content color-${c_type} tooltip" title="${cc_title}">
468 ${c_type.upper()}
469 </div>
470 </div>
471 </td>
472 <td class="td-time">
473 ${h.age_component(commit.date)}
474 </td>
475 <td class="td-user">
476 ${base.gravatar_with_user(commit.author, 16)}
477 </td>
478 <td class="td-hash">
479 <code>
480 <a href="${h.url('changeset_home', repo_name=c.target_repo.repo_name, revision=commit.raw_id)}">
481 r${commit.revision}:${h.short_id(commit.raw_id)}
482 </a>
483 ${h.hidden('revisions', commit.raw_id)}
484 </code>
485 </td>
486 <td class="expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}">
487 <div class="show_more_col">
488 <i class="show_more"></i>
489 </div>
490 </td>
491 <td class="mid td-description">
492 <div class="log-container truncate-wrap">
493 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">
494 ${h.urlify_commit_message(commit.message, c.repo_name)}
495 </div>
496 </div>
497 </td>
498 </tr>
499 % endif
500 % endfor
501 </table>
502 </div>
503
504 <script>
505 $('.expand_commit').on('click',function(e){
506 var target_expand = $(this);
507 var cid = target_expand.data('commitId');
508
509 if (target_expand.hasClass('open')){
510 $('#c-'+cid).css({
511 'height': '1.5em',
512 'white-space': 'nowrap',
513 'text-overflow': 'ellipsis',
514 'overflow':'hidden'
515 });
516 target_expand.removeClass('open');
517 }
518 else {
519 $('#c-'+cid).css({
520 'height': 'auto',
521 'white-space': 'pre-line',
522 'text-overflow': 'initial',
523 'overflow':'visible'
524 });
525 target_expand.addClass('open');
526 }
527 });
528 </script>
529
530 % endif
531
532 % else:
533 <%include file="/compare/compare_commits.mako" />
534 % endif
535
461 <div class="cs_files">
536 <div class="cs_files">
462 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
537 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
463 ${cbdiffs.render_diffset_menu()}
538 ${cbdiffs.render_diffset_menu()}
@@ -528,6 +603,107 b' Changed files:'
528 }
603 }
529 }
604 }
530
605
606 VersionController = function() {
607 var self = this;
608 this.$verSource = $('input[name=ver_source]');
609 this.$verTarget = $('input[name=ver_target]');
610
611 this.adjustRadioSelectors = function (curNode) {
612 var getVal = function(item) {
613 if (item == 'latest'){
614 return Number.MAX_SAFE_INTEGER
615 }
616 else {
617 return parseInt(item)
618 }
619 };
620
621 var curVal = getVal($(curNode).val());
622 $.each(self.$verSource, function(index, value){
623 var elVal = getVal($(value).val());
624 if(elVal > curVal){
625 $(value).attr('disabled', 'disabled');
626 $(value).removeAttr('checked');
627 }
628 else{
629 $(value).removeAttr('disabled');
630 }
631 });
632
633 self.setLockAction(false, $(curNode).data('verPos'));
634 };
635
636
637 this.attachVersionListener = function () {
638 self.$verTarget.change(function(e){
639 self.adjustRadioSelectors(this)
640 });
641 self.$verSource.change(function(e){
642 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
643 });
644 };
645
646 this.init = function () {
647
648 var curNode = self.$verTarget.filter(':checked');
649 self.adjustRadioSelectors(curNode);
650 self.setLockAction(true);
651 self.attachVersionListener();
652
653 };
654
655 this.setLockAction = function (state, selectedVersion) {
656 if(state){
657 $('#show-version-diff').attr('disabled','disabled')
658 $('#show-version-diff').addClass('disabled')
659 $('#show-version-diff').html($('#show-version-diff').data('labelText'));
660 }
661 else{
662 $('#show-version-diff').removeAttr('disabled');
663 $('#show-version-diff').removeClass('disabled')
664 //$('#show-version-diff').html(_gettext('show changes for v') + selectedVersion)
665 }
666
667 };
668
669 this.showVersionDiff = function(){
670 var target = self.$verTarget.filter(':checked');
671 var source = self.$verSource.filter(':checked');
672
673 if (target.val() && source.val()) {
674 var params = {
675 'pull_request_id': ${c.pull_request.pull_request_id},
676 'repo_name': templateContext.repo_name,
677 'version': target.val(),
678 'from_version': source.val()
679 };
680 window.location = pyroutes.url('pullrequest_show', params)
681 }
682
683 return false;
684 };
685
686 this.toggleVersionView = function (elem) {
687
688 if ($('#show-version-diff').is(':visible')) {
689 $('.version-pr').hide();
690 $('#show-version-diff').hide();
691 $(elem).html($(elem).data('toggleOn'))
692 } else {
693 $('.version-pr').show();
694 $('#show-version-diff').show();
695 $(elem).html($(elem).data('toggleOff'))
696 }
697
698 return false
699 }
700
701 };
702
703 versionController = new VersionController();
704 versionController.init();
705
706
531 $(function(){
707 $(function(){
532 ReviewerAutoComplete('user');
708 ReviewerAutoComplete('user');
533 // custom code mirror
709 // custom code mirror
General Comments 0
You need to be logged in to leave comments. Login now