Show More
@@ -1,177 +1,188 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.compare |
|
3 | rhodecode.controllers.compare | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | compare controller for pylons showing differences between two |
|
6 | compare controller for pylons showing differences between two | |
7 | repos, branches, bookmarks or tips |
|
7 | repos, branches, bookmarks or tips | |
8 |
|
8 | |||
9 | :created_on: May 6, 2012 |
|
9 | :created_on: May 6, 2012 | |
10 | :author: marcink |
|
10 | :author: marcink | |
11 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> |
|
11 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |
12 | :license: GPLv3, see COPYING for more details. |
|
12 | :license: GPLv3, see COPYING for more details. | |
13 | """ |
|
13 | """ | |
14 | # This program is free software: you can redistribute it and/or modify |
|
14 | # This program is free software: you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by |
|
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation, either version 3 of the License, or |
|
16 | # the Free Software Foundation, either version 3 of the License, or | |
17 | # (at your option) any later version. |
|
17 | # (at your option) any later version. | |
18 | # |
|
18 | # | |
19 | # This program is distributed in the hope that it will be useful, |
|
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. |
|
22 | # GNU General Public License for more details. | |
23 | # |
|
23 | # | |
24 | # You should have received a copy of the GNU General Public License |
|
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 | import logging |
|
26 | import logging | |
27 | import traceback |
|
27 | import traceback | |
28 |
|
28 | |||
29 | from webob.exc import HTTPNotFound |
|
29 | from webob.exc import HTTPNotFound | |
30 | from pylons import request, response, session, tmpl_context as c, url |
|
30 | from pylons import request, response, session, tmpl_context as c, url | |
31 | from pylons.controllers.util import abort, redirect |
|
31 | from pylons.controllers.util import abort, redirect | |
32 | from pylons.i18n.translation import _ |
|
32 | from pylons.i18n.translation import _ | |
33 |
|
33 | |||
34 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError |
|
34 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError | |
35 | from rhodecode.lib import helpers as h |
|
35 | from rhodecode.lib import helpers as h | |
36 | from rhodecode.lib.base import BaseRepoController, render |
|
36 | from rhodecode.lib.base import BaseRepoController, render | |
37 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
37 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
38 | from rhodecode.lib import diffs |
|
38 | from rhodecode.lib import diffs | |
39 |
|
39 | |||
40 | from rhodecode.model.db import Repository |
|
40 | from rhodecode.model.db import Repository | |
41 | from rhodecode.model.pull_request import PullRequestModel |
|
41 | from rhodecode.model.pull_request import PullRequestModel | |
42 | from webob.exc import HTTPBadRequest |
|
42 | from webob.exc import HTTPBadRequest | |
43 | from rhodecode.lib.diffs import LimitedDiffContainer |
|
43 | from rhodecode.lib.diffs import LimitedDiffContainer | |
44 | from rhodecode.lib.vcs.backends.base import EmptyChangeset |
|
44 | from rhodecode.lib.vcs.backends.base import EmptyChangeset | |
45 |
|
45 | |||
46 | log = logging.getLogger(__name__) |
|
46 | log = logging.getLogger(__name__) | |
47 |
|
47 | |||
48 |
|
48 | |||
49 | class CompareController(BaseRepoController): |
|
49 | class CompareController(BaseRepoController): | |
50 |
|
50 | |||
51 | @LoginRequired() |
|
51 | @LoginRequired() | |
52 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
52 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
53 | 'repository.admin') |
|
53 | 'repository.admin') | |
54 | def __before__(self): |
|
54 | def __before__(self): | |
55 | super(CompareController, self).__before__() |
|
55 | super(CompareController, self).__before__() | |
56 |
|
56 | |||
57 | def __get_cs_or_redirect(self, rev, repo, redirect_after=True, |
|
57 | def __get_cs_or_redirect(self, rev, repo, redirect_after=True, | |
58 | partial=False): |
|
58 | partial=False): | |
59 | """ |
|
59 | """ | |
60 | Safe way to get changeset if error occur it redirects to changeset with |
|
60 | Safe way to get changeset if error occur it redirects to changeset with | |
61 | proper message. If partial is set then don't do redirect raise Exception |
|
61 | proper message. If partial is set then don't do redirect raise Exception | |
62 | instead |
|
62 | instead | |
63 |
|
63 | |||
64 | :param rev: revision to fetch |
|
64 | :param rev: revision to fetch | |
65 | :param repo: repo instance |
|
65 | :param repo: repo instance | |
66 | """ |
|
66 | """ | |
67 |
|
67 | |||
68 | try: |
|
68 | try: | |
69 | type_, rev = rev |
|
69 | type_, rev = rev | |
70 | return repo.scm_instance.get_changeset(rev) |
|
70 | return repo.scm_instance.get_changeset(rev) | |
71 | except EmptyRepositoryError, e: |
|
71 | except EmptyRepositoryError, e: | |
72 | if not redirect_after: |
|
72 | if not redirect_after: | |
73 | return None |
|
73 | return None | |
74 | h.flash(h.literal(_('There are no changesets yet')), |
|
74 | h.flash(h.literal(_('There are no changesets yet')), | |
75 | category='warning') |
|
75 | category='warning') | |
76 | redirect(url('summary_home', repo_name=repo.repo_name)) |
|
76 | redirect(url('summary_home', repo_name=repo.repo_name)) | |
77 |
|
77 | |||
78 | except RepositoryError, e: |
|
78 | except RepositoryError, e: | |
79 | log.error(traceback.format_exc()) |
|
79 | log.error(traceback.format_exc()) | |
80 | h.flash(str(e), category='warning') |
|
80 | h.flash(str(e), category='warning') | |
81 | if not partial: |
|
81 | if not partial: | |
82 | redirect(h.url('summary_home', repo_name=repo.repo_name)) |
|
82 | redirect(h.url('summary_home', repo_name=repo.repo_name)) | |
83 | raise HTTPBadRequest() |
|
83 | raise HTTPBadRequest() | |
84 |
|
84 | |||
85 | def index(self, org_ref_type, org_ref, other_ref_type, other_ref): |
|
85 | def index(self, org_ref_type, org_ref, other_ref_type, other_ref): | |
86 | # org_ref will be evaluated in org_repo |
|
86 | # org_ref will be evaluated in org_repo | |
87 | org_repo = c.rhodecode_db_repo.repo_name |
|
87 | org_repo = c.rhodecode_db_repo.repo_name | |
88 | org_ref = (org_ref_type, org_ref) |
|
88 | org_ref = (org_ref_type, org_ref) | |
89 | # other_ref will be evaluated in other_repo |
|
89 | # other_ref will be evaluated in other_repo | |
90 | other_ref = (other_ref_type, other_ref) |
|
90 | other_ref = (other_ref_type, other_ref) | |
91 | other_repo = request.GET.get('other_repo', org_repo) |
|
91 | other_repo = request.GET.get('other_repo', org_repo) | |
|
92 | # If merge is True: | |||
|
93 | # Show what org would get if merged with other: | |||
|
94 | # List changesets that are ancestors of other but not of org. | |||
|
95 | # New changesets in org is thus ignored. | |||
|
96 | # Diff will be from common ancestor, and merges of org to other will thus be ignored. | |||
|
97 | # If merge is False: | |||
|
98 | # Make a raw diff from org to other, no matter if related or not. | |||
|
99 | # Changesets in one and not in the other will be ignored | |||
|
100 | merge = bool(request.GET.get('merge')) | |||
92 | # fulldiff disables cut_off_limit |
|
101 | # fulldiff disables cut_off_limit | |
93 | c.fulldiff = request.GET.get('fulldiff') |
|
102 | c.fulldiff = request.GET.get('fulldiff') | |
94 | # partial uses compare_cs.html template directly |
|
103 | # partial uses compare_cs.html template directly | |
95 | partial = request.environ.get('HTTP_X_PARTIAL_XHR') |
|
104 | partial = request.environ.get('HTTP_X_PARTIAL_XHR') | |
96 | # as_form puts hidden input field with changeset revisions |
|
105 | # as_form puts hidden input field with changeset revisions | |
97 | c.as_form = partial and request.GET.get('as_form') |
|
106 | c.as_form = partial and request.GET.get('as_form') | |
98 | # swap url for compare_diff page - never partial and never as_form |
|
107 | # swap url for compare_diff page - never partial and never as_form | |
99 | c.swap_url = h.url('compare_url', |
|
108 | c.swap_url = h.url('compare_url', | |
100 | repo_name=other_repo, |
|
109 | repo_name=other_repo, | |
101 | org_ref_type=other_ref[0], org_ref=other_ref[1], |
|
110 | org_ref_type=other_ref[0], org_ref=other_ref[1], | |
102 | other_repo=org_repo, |
|
111 | other_repo=org_repo, | |
103 |
other_ref_type=org_ref[0], other_ref=org_ref[1] |
|
112 | other_ref_type=org_ref[0], other_ref=org_ref[1], | |
|
113 | merge=merge or '') | |||
104 |
|
114 | |||
105 | org_repo = Repository.get_by_repo_name(org_repo) |
|
115 | org_repo = Repository.get_by_repo_name(org_repo) | |
106 | other_repo = Repository.get_by_repo_name(other_repo) |
|
116 | other_repo = Repository.get_by_repo_name(other_repo) | |
107 |
|
117 | |||
108 | self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial) |
|
118 | self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial) | |
109 | self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial) |
|
119 | self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial) | |
110 |
|
120 | |||
111 | if org_repo is None: |
|
121 | if org_repo is None: | |
112 | log.error('Could not find org repo %s' % org_repo) |
|
122 | log.error('Could not find org repo %s' % org_repo) | |
113 | raise HTTPNotFound |
|
123 | raise HTTPNotFound | |
114 | if other_repo is None: |
|
124 | if other_repo is None: | |
115 | log.error('Could not find other repo %s' % other_repo) |
|
125 | log.error('Could not find other repo %s' % other_repo) | |
116 | raise HTTPNotFound |
|
126 | raise HTTPNotFound | |
117 |
|
127 | |||
118 | if org_repo != other_repo and h.is_git(org_repo): |
|
128 | if org_repo != other_repo and h.is_git(org_repo): | |
119 | log.error('compare of two remote repos not available for GIT REPOS') |
|
129 | log.error('compare of two remote repos not available for GIT REPOS') | |
120 | raise HTTPNotFound |
|
130 | raise HTTPNotFound | |
121 |
|
131 | |||
122 | if org_repo.scm_instance.alias != other_repo.scm_instance.alias: |
|
132 | if org_repo.scm_instance.alias != other_repo.scm_instance.alias: | |
123 | log.error('compare of two different kind of remote repos not available') |
|
133 | log.error('compare of two different kind of remote repos not available') | |
124 | raise HTTPNotFound |
|
134 | raise HTTPNotFound | |
125 |
|
135 | |||
126 | c.org_repo = org_repo |
|
136 | c.org_repo = org_repo | |
127 | c.other_repo = other_repo |
|
137 | c.other_repo = other_repo | |
128 | c.org_ref = org_ref[1] |
|
138 | c.org_ref = org_ref[1] | |
129 | c.other_ref = other_ref[1] |
|
139 | c.other_ref = other_ref[1] | |
130 | c.org_ref_type = org_ref[0] |
|
140 | c.org_ref_type = org_ref[0] | |
131 | c.other_ref_type = other_ref[0] |
|
141 | c.other_ref_type = other_ref[0] | |
132 |
|
142 | |||
133 | c.cs_ranges, ancestor = PullRequestModel().get_compare_data( |
|
143 | c.cs_ranges, c.ancestor = PullRequestModel().get_compare_data( | |
134 | org_repo, org_ref, other_repo, other_ref) |
|
144 | org_repo, org_ref, other_repo, other_ref, merge) | |
135 |
|
145 | |||
136 | c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in |
|
146 | c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in | |
137 | c.cs_ranges]) |
|
147 | c.cs_ranges]) | |
138 | if partial: |
|
148 | if partial: | |
|
149 | assert c.ancestor | |||
139 | return render('compare/compare_cs.html') |
|
150 | return render('compare/compare_cs.html') | |
140 |
|
151 | |||
141 | if ancestor and org_repo != other_repo: |
|
152 | if c.ancestor: | |
|
153 | assert merge | |||
142 | # case we want a simple diff without incoming changesets, |
|
154 | # case we want a simple diff without incoming changesets, | |
143 | # previewing what will be merged. |
|
155 | # previewing what will be merged. | |
144 |
# Make the diff on the |
|
156 | # Make the diff on the other repo (which is known to have other_ref) | |
145 | # revision that is common ancestor |
|
|||
146 | log.debug('Using ancestor %s as org_ref instead of %s' |
|
157 | log.debug('Using ancestor %s as org_ref instead of %s' | |
147 | % (ancestor, org_ref)) |
|
158 | % (c.ancestor, org_ref)) | |
148 | org_ref = ('rev', ancestor) |
|
159 | org_ref = ('rev', c.ancestor) | |
149 | org_repo = other_repo |
|
160 | org_repo = other_repo | |
150 |
|
161 | |||
151 | diff_limit = self.cut_off_limit if not c.fulldiff else None |
|
162 | diff_limit = self.cut_off_limit if not c.fulldiff else None | |
152 |
|
163 | |||
153 | _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref) |
|
164 | _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref) | |
154 |
|
165 | |||
155 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', |
|
166 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', | |
156 | diff_limit=diff_limit) |
|
167 | diff_limit=diff_limit) | |
157 | _parsed = diff_processor.prepare() |
|
168 | _parsed = diff_processor.prepare() | |
158 |
|
169 | |||
159 | c.limited_diff = False |
|
170 | c.limited_diff = False | |
160 | if isinstance(_parsed, LimitedDiffContainer): |
|
171 | if isinstance(_parsed, LimitedDiffContainer): | |
161 | c.limited_diff = True |
|
172 | c.limited_diff = True | |
162 |
|
173 | |||
163 | c.files = [] |
|
174 | c.files = [] | |
164 | c.changes = {} |
|
175 | c.changes = {} | |
165 | c.lines_added = 0 |
|
176 | c.lines_added = 0 | |
166 | c.lines_deleted = 0 |
|
177 | c.lines_deleted = 0 | |
167 | for f in _parsed: |
|
178 | for f in _parsed: | |
168 | st = f['stats'] |
|
179 | st = f['stats'] | |
169 | if st[0] != 'b': |
|
180 | if st[0] != 'b': | |
170 | c.lines_added += st[0] |
|
181 | c.lines_added += st[0] | |
171 | c.lines_deleted += st[1] |
|
182 | c.lines_deleted += st[1] | |
172 | fid = h.FID('', f['filename']) |
|
183 | fid = h.FID('', f['filename']) | |
173 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) |
|
184 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) | |
174 | diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f]) |
|
185 | diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f]) | |
175 | c.changes[fid] = [f['operation'], f['filename'], diff] |
|
186 | c.changes[fid] = [f['operation'], f['filename'], diff] | |
176 |
|
187 | |||
177 | return render('compare/compare_diff.html') |
|
188 | return render('compare/compare_diff.html') |
@@ -1,479 +1,476 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.pullrequests |
|
3 | rhodecode.controllers.pullrequests | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | pull requests controller for rhodecode for initializing pull requests |
|
6 | pull requests controller for rhodecode for initializing pull requests | |
7 |
|
7 | |||
8 | :created_on: May 7, 2012 |
|
8 | :created_on: May 7, 2012 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import logging |
|
25 | import logging | |
26 | import traceback |
|
26 | import traceback | |
27 | import formencode |
|
27 | import formencode | |
28 |
|
28 | |||
29 | from webob.exc import HTTPNotFound, HTTPForbidden |
|
29 | from webob.exc import HTTPNotFound, HTTPForbidden | |
30 | from collections import defaultdict |
|
30 | from collections import defaultdict | |
31 | from itertools import groupby |
|
31 | from itertools import groupby | |
32 |
|
32 | |||
33 | from pylons import request, response, session, tmpl_context as c, url |
|
33 | from pylons import request, response, session, tmpl_context as c, url | |
34 | from pylons.controllers.util import abort, redirect |
|
34 | from pylons.controllers.util import abort, redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from rhodecode.lib.compat import json |
|
37 | from rhodecode.lib.compat import json | |
38 | from rhodecode.lib.base import BaseRepoController, render |
|
38 | from rhodecode.lib.base import BaseRepoController, render | |
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ |
|
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ | |
40 | NotAnonymous |
|
40 | NotAnonymous | |
41 | from rhodecode.lib import helpers as h |
|
41 | from rhodecode.lib import helpers as h | |
42 | from rhodecode.lib import diffs |
|
42 | from rhodecode.lib import diffs | |
43 | from rhodecode.lib.utils import action_logger, jsonify |
|
43 | from rhodecode.lib.utils import action_logger, jsonify | |
44 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError |
|
44 | from rhodecode.lib.vcs.exceptions import EmptyRepositoryError | |
45 | from rhodecode.lib.vcs.backends.base import EmptyChangeset |
|
45 | from rhodecode.lib.vcs.backends.base import EmptyChangeset | |
46 | from rhodecode.lib.diffs import LimitedDiffContainer |
|
46 | from rhodecode.lib.diffs import LimitedDiffContainer | |
47 | from rhodecode.model.db import User, PullRequest, ChangesetStatus,\ |
|
47 | from rhodecode.model.db import User, PullRequest, ChangesetStatus,\ | |
48 | ChangesetComment |
|
48 | ChangesetComment | |
49 | from rhodecode.model.pull_request import PullRequestModel |
|
49 | from rhodecode.model.pull_request import PullRequestModel | |
50 | from rhodecode.model.meta import Session |
|
50 | from rhodecode.model.meta import Session | |
51 | from rhodecode.model.repo import RepoModel |
|
51 | from rhodecode.model.repo import RepoModel | |
52 | from rhodecode.model.comment import ChangesetCommentsModel |
|
52 | from rhodecode.model.comment import ChangesetCommentsModel | |
53 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
53 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
54 | from rhodecode.model.forms import PullRequestForm |
|
54 | from rhodecode.model.forms import PullRequestForm | |
55 |
|
55 | |||
56 | log = logging.getLogger(__name__) |
|
56 | log = logging.getLogger(__name__) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | class PullrequestsController(BaseRepoController): |
|
59 | class PullrequestsController(BaseRepoController): | |
60 |
|
60 | |||
61 | @LoginRequired() |
|
61 | @LoginRequired() | |
62 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
62 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
63 | 'repository.admin') |
|
63 | 'repository.admin') | |
64 | def __before__(self): |
|
64 | def __before__(self): | |
65 | super(PullrequestsController, self).__before__() |
|
65 | super(PullrequestsController, self).__before__() | |
66 | repo_model = RepoModel() |
|
66 | repo_model = RepoModel() | |
67 | c.users_array = repo_model.get_users_js() |
|
67 | c.users_array = repo_model.get_users_js() | |
68 | c.users_groups_array = repo_model.get_users_groups_js() |
|
68 | c.users_groups_array = repo_model.get_users_groups_js() | |
69 |
|
69 | |||
70 | def _get_repo_refs(self, repo, rev=None): |
|
70 | def _get_repo_refs(self, repo, rev=None): | |
71 | """return a structure with repo's interesting changesets, suitable for |
|
71 | """return a structure with repo's interesting changesets, suitable for | |
72 | the selectors in pullrequest.html""" |
|
72 | the selectors in pullrequest.html""" | |
73 | branches = [('branch:%s:%s' % (k, v), k) |
|
73 | branches = [('branch:%s:%s' % (k, v), k) | |
74 | for k, v in repo.branches.iteritems()] |
|
74 | for k, v in repo.branches.iteritems()] | |
75 | bookmarks = [('book:%s:%s' % (k, v), k) |
|
75 | bookmarks = [('book:%s:%s' % (k, v), k) | |
76 | for k, v in repo.bookmarks.iteritems()] |
|
76 | for k, v in repo.bookmarks.iteritems()] | |
77 | tags = [('tag:%s:%s' % (k, v), k) |
|
77 | tags = [('tag:%s:%s' % (k, v), k) | |
78 | for k, v in repo.tags.iteritems() |
|
78 | for k, v in repo.tags.iteritems() | |
79 | if k != 'tip'] |
|
79 | if k != 'tip'] | |
80 |
|
80 | |||
81 | tip = repo.tags['tip'] |
|
81 | tip = repo.tags['tip'] | |
82 | colontip = ':' + tip |
|
82 | colontip = ':' + tip | |
83 | tips = [x[1] for x in branches + bookmarks + tags |
|
83 | tips = [x[1] for x in branches + bookmarks + tags | |
84 | if x[0].endswith(colontip)] |
|
84 | if x[0].endswith(colontip)] | |
85 | selected = 'tag:tip:%s' % tip |
|
85 | selected = 'tag:tip:%s' % tip | |
86 | special = [(selected, 'tip (%s)' % ', '.join(tips))] |
|
86 | special = [(selected, 'tip (%s)' % ', '.join(tips))] | |
87 |
|
87 | |||
88 | if rev: |
|
88 | if rev: | |
89 | selected = 'rev:%s:%s' % (rev, rev) |
|
89 | selected = 'rev:%s:%s' % (rev, rev) | |
90 | special.append((selected, rev)) |
|
90 | special.append((selected, rev)) | |
91 |
|
91 | |||
92 | return [(special, _("Special")), |
|
92 | return [(special, _("Special")), | |
93 | (bookmarks, _("Bookmarks")), |
|
93 | (bookmarks, _("Bookmarks")), | |
94 | (branches, _("Branches")), |
|
94 | (branches, _("Branches")), | |
95 | (tags, _("Tags")), |
|
95 | (tags, _("Tags")), | |
96 | ], selected |
|
96 | ], selected | |
97 |
|
97 | |||
98 | def _get_is_allowed_change_status(self, pull_request): |
|
98 | def _get_is_allowed_change_status(self, pull_request): | |
99 | owner = self.rhodecode_user.user_id == pull_request.user_id |
|
99 | owner = self.rhodecode_user.user_id == pull_request.user_id | |
100 | reviewer = self.rhodecode_user.user_id in [x.user_id for x in |
|
100 | reviewer = self.rhodecode_user.user_id in [x.user_id for x in | |
101 | pull_request.reviewers] |
|
101 | pull_request.reviewers] | |
102 | return (self.rhodecode_user.admin or owner or reviewer) |
|
102 | return (self.rhodecode_user.admin or owner or reviewer) | |
103 |
|
103 | |||
104 | def show_all(self, repo_name): |
|
104 | def show_all(self, repo_name): | |
105 | c.pull_requests = PullRequestModel().get_all(repo_name) |
|
105 | c.pull_requests = PullRequestModel().get_all(repo_name) | |
106 | c.repo_name = repo_name |
|
106 | c.repo_name = repo_name | |
107 | return render('/pullrequests/pullrequest_show_all.html') |
|
107 | return render('/pullrequests/pullrequest_show_all.html') | |
108 |
|
108 | |||
109 | @NotAnonymous() |
|
109 | @NotAnonymous() | |
110 | def index(self): |
|
110 | def index(self): | |
111 | org_repo = c.rhodecode_db_repo |
|
111 | org_repo = c.rhodecode_db_repo | |
112 |
|
112 | |||
113 | if org_repo.scm_instance.alias != 'hg': |
|
113 | if org_repo.scm_instance.alias != 'hg': | |
114 | log.error('Review not available for GIT REPOS') |
|
114 | log.error('Review not available for GIT REPOS') | |
115 | raise HTTPNotFound |
|
115 | raise HTTPNotFound | |
116 |
|
116 | |||
117 | try: |
|
117 | try: | |
118 | org_repo.scm_instance.get_changeset() |
|
118 | org_repo.scm_instance.get_changeset() | |
119 | except EmptyRepositoryError, e: |
|
119 | except EmptyRepositoryError, e: | |
120 | h.flash(h.literal(_('There are no changesets yet')), |
|
120 | h.flash(h.literal(_('There are no changesets yet')), | |
121 | category='warning') |
|
121 | category='warning') | |
122 | redirect(url('summary_home', repo_name=org_repo.repo_name)) |
|
122 | redirect(url('summary_home', repo_name=org_repo.repo_name)) | |
123 |
|
123 | |||
124 | org_rev = request.GET.get('rev_end') |
|
124 | org_rev = request.GET.get('rev_end') | |
125 | # rev_start is not directly useful - its parent could however be used |
|
125 | # rev_start is not directly useful - its parent could however be used | |
126 | # as default for other and thus give a simple compare view |
|
126 | # as default for other and thus give a simple compare view | |
127 | #other_rev = request.POST.get('rev_start') |
|
127 | #other_rev = request.POST.get('rev_start') | |
128 |
|
128 | |||
129 | other_repos_info = {} |
|
129 | other_repos_info = {} | |
130 |
|
130 | |||
131 | c.org_repos = [] |
|
131 | c.org_repos = [] | |
132 | c.org_repos.append((org_repo.repo_name, org_repo.repo_name)) |
|
132 | c.org_repos.append((org_repo.repo_name, org_repo.repo_name)) | |
133 | c.default_org_repo = org_repo.repo_name |
|
133 | c.default_org_repo = org_repo.repo_name | |
134 | c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, org_rev) |
|
134 | c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, org_rev) | |
135 |
|
135 | |||
136 | c.other_repos = [] |
|
136 | c.other_repos = [] | |
137 | # add org repo to other so we can open pull request against itself |
|
137 | # add org repo to other so we can open pull request against itself | |
138 | c.other_repos.extend(c.org_repos) |
|
138 | c.other_repos.extend(c.org_repos) | |
139 | c.default_other_repo = org_repo.repo_name |
|
139 | c.default_other_repo = org_repo.repo_name | |
140 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance) |
|
140 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance) | |
141 | usr_data = lambda usr: dict(user_id=usr.user_id, |
|
141 | usr_data = lambda usr: dict(user_id=usr.user_id, | |
142 | username=usr.username, |
|
142 | username=usr.username, | |
143 | firstname=usr.firstname, |
|
143 | firstname=usr.firstname, | |
144 | lastname=usr.lastname, |
|
144 | lastname=usr.lastname, | |
145 | gravatar_link=h.gravatar_url(usr.email, 14)) |
|
145 | gravatar_link=h.gravatar_url(usr.email, 14)) | |
146 | other_repos_info[org_repo.repo_name] = { |
|
146 | other_repos_info[org_repo.repo_name] = { | |
147 | 'user': usr_data(org_repo.user), |
|
147 | 'user': usr_data(org_repo.user), | |
148 | 'description': org_repo.description, |
|
148 | 'description': org_repo.description, | |
149 | 'revs': h.select('other_ref', c.default_other_ref, |
|
149 | 'revs': h.select('other_ref', c.default_other_ref, | |
150 | c.default_other_refs, class_='refs') |
|
150 | c.default_other_refs, class_='refs') | |
151 | } |
|
151 | } | |
152 |
|
152 | |||
153 | # gather forks and add to this list ... even though it is rare to |
|
153 | # gather forks and add to this list ... even though it is rare to | |
154 | # request forks to pull their parent |
|
154 | # request forks to pull their parent | |
155 | for fork in org_repo.forks: |
|
155 | for fork in org_repo.forks: | |
156 | c.other_repos.append((fork.repo_name, fork.repo_name)) |
|
156 | c.other_repos.append((fork.repo_name, fork.repo_name)) | |
157 | refs, default_ref = self._get_repo_refs(fork.scm_instance) |
|
157 | refs, default_ref = self._get_repo_refs(fork.scm_instance) | |
158 | other_repos_info[fork.repo_name] = { |
|
158 | other_repos_info[fork.repo_name] = { | |
159 | 'user': usr_data(fork.user), |
|
159 | 'user': usr_data(fork.user), | |
160 | 'description': fork.description, |
|
160 | 'description': fork.description, | |
161 | 'revs': h.select('other_ref', default_ref, refs, class_='refs') |
|
161 | 'revs': h.select('other_ref', default_ref, refs, class_='refs') | |
162 | } |
|
162 | } | |
163 |
|
163 | |||
164 | # add parents of this fork also, but only if it's not empty |
|
164 | # add parents of this fork also, but only if it's not empty | |
165 | if org_repo.parent and org_repo.parent.scm_instance.revisions: |
|
165 | if org_repo.parent and org_repo.parent.scm_instance.revisions: | |
166 | c.default_other_repo = org_repo.parent.repo_name |
|
166 | c.default_other_repo = org_repo.parent.repo_name | |
167 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance) |
|
167 | c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance) | |
168 | c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name)) |
|
168 | c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name)) | |
169 | other_repos_info[org_repo.parent.repo_name] = { |
|
169 | other_repos_info[org_repo.parent.repo_name] = { | |
170 | 'user': usr_data(org_repo.parent.user), |
|
170 | 'user': usr_data(org_repo.parent.user), | |
171 | 'description': org_repo.parent.description, |
|
171 | 'description': org_repo.parent.description, | |
172 | 'revs': h.select('other_ref', c.default_other_ref, |
|
172 | 'revs': h.select('other_ref', c.default_other_ref, | |
173 | c.default_other_refs, class_='refs') |
|
173 | c.default_other_refs, class_='refs') | |
174 | } |
|
174 | } | |
175 |
|
175 | |||
176 | c.other_repos_info = json.dumps(other_repos_info) |
|
176 | c.other_repos_info = json.dumps(other_repos_info) | |
177 | # other repo owner |
|
177 | # other repo owner | |
178 | c.review_members = [] |
|
178 | c.review_members = [] | |
179 | return render('/pullrequests/pullrequest.html') |
|
179 | return render('/pullrequests/pullrequest.html') | |
180 |
|
180 | |||
181 | @NotAnonymous() |
|
181 | @NotAnonymous() | |
182 | def create(self, repo_name): |
|
182 | def create(self, repo_name): | |
183 | repo = RepoModel()._get_repo(repo_name) |
|
183 | repo = RepoModel()._get_repo(repo_name) | |
184 | try: |
|
184 | try: | |
185 | _form = PullRequestForm(repo.repo_id)().to_python(request.POST) |
|
185 | _form = PullRequestForm(repo.repo_id)().to_python(request.POST) | |
186 | except formencode.Invalid, errors: |
|
186 | except formencode.Invalid, errors: | |
187 | log.error(traceback.format_exc()) |
|
187 | log.error(traceback.format_exc()) | |
188 | if errors.error_dict.get('revisions'): |
|
188 | if errors.error_dict.get('revisions'): | |
189 | msg = 'Revisions: %s' % errors.error_dict['revisions'] |
|
189 | msg = 'Revisions: %s' % errors.error_dict['revisions'] | |
190 | elif errors.error_dict.get('pullrequest_title'): |
|
190 | elif errors.error_dict.get('pullrequest_title'): | |
191 | msg = _('Pull request requires a title with min. 3 chars') |
|
191 | msg = _('Pull request requires a title with min. 3 chars') | |
192 | else: |
|
192 | else: | |
193 | msg = _('error during creation of pull request') |
|
193 | msg = _('error during creation of pull request') | |
194 |
|
194 | |||
195 | h.flash(msg, 'error') |
|
195 | h.flash(msg, 'error') | |
196 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
196 | return redirect(url('pullrequest_home', repo_name=repo_name)) | |
197 |
|
197 | |||
198 | org_repo = _form['org_repo'] |
|
198 | org_repo = _form['org_repo'] | |
199 |
org_ref = _form[' |
|
199 | org_ref = 'rev:merge:%s' % _form['merge_rev'] | |
200 | other_repo = _form['other_repo'] |
|
200 | other_repo = _form['other_repo'] | |
201 |
other_ref = _form[' |
|
201 | other_ref = 'rev:ancestor:%s' % _form['ancestor_rev'] | |
202 | revisions = _form['revisions'] |
|
202 | revisions = _form['revisions'] | |
203 | reviewers = _form['review_members'] |
|
203 | reviewers = _form['review_members'] | |
204 |
|
204 | |||
205 | title = _form['pullrequest_title'] |
|
205 | title = _form['pullrequest_title'] | |
206 | description = _form['pullrequest_desc'] |
|
206 | description = _form['pullrequest_desc'] | |
207 |
|
207 | |||
208 | try: |
|
208 | try: | |
209 | pull_request = PullRequestModel().create( |
|
209 | pull_request = PullRequestModel().create( | |
210 | self.rhodecode_user.user_id, org_repo, org_ref, other_repo, |
|
210 | self.rhodecode_user.user_id, org_repo, org_ref, other_repo, | |
211 | other_ref, revisions, reviewers, title, description |
|
211 | other_ref, revisions, reviewers, title, description | |
212 | ) |
|
212 | ) | |
213 | Session().commit() |
|
213 | Session().commit() | |
214 | h.flash(_('Successfully opened new pull request'), |
|
214 | h.flash(_('Successfully opened new pull request'), | |
215 | category='success') |
|
215 | category='success') | |
216 | except Exception: |
|
216 | except Exception: | |
217 | h.flash(_('Error occurred during sending pull request'), |
|
217 | h.flash(_('Error occurred during sending pull request'), | |
218 | category='error') |
|
218 | category='error') | |
219 | log.error(traceback.format_exc()) |
|
219 | log.error(traceback.format_exc()) | |
220 | return redirect(url('pullrequest_home', repo_name=repo_name)) |
|
220 | return redirect(url('pullrequest_home', repo_name=repo_name)) | |
221 |
|
221 | |||
222 | return redirect(url('pullrequest_show', repo_name=other_repo, |
|
222 | return redirect(url('pullrequest_show', repo_name=other_repo, | |
223 | pull_request_id=pull_request.pull_request_id)) |
|
223 | pull_request_id=pull_request.pull_request_id)) | |
224 |
|
224 | |||
225 | @NotAnonymous() |
|
225 | @NotAnonymous() | |
226 | @jsonify |
|
226 | @jsonify | |
227 | def update(self, repo_name, pull_request_id): |
|
227 | def update(self, repo_name, pull_request_id): | |
228 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
228 | pull_request = PullRequest.get_or_404(pull_request_id) | |
229 | if pull_request.is_closed(): |
|
229 | if pull_request.is_closed(): | |
230 | raise HTTPForbidden() |
|
230 | raise HTTPForbidden() | |
231 | #only owner or admin can update it |
|
231 | #only owner or admin can update it | |
232 | owner = pull_request.author.user_id == c.rhodecode_user.user_id |
|
232 | owner = pull_request.author.user_id == c.rhodecode_user.user_id | |
233 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: |
|
233 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |
234 | reviewers_ids = map(int, filter(lambda v: v not in [None, ''], |
|
234 | reviewers_ids = map(int, filter(lambda v: v not in [None, ''], | |
235 | request.POST.get('reviewers_ids', '').split(','))) |
|
235 | request.POST.get('reviewers_ids', '').split(','))) | |
236 |
|
236 | |||
237 | PullRequestModel().update_reviewers(pull_request_id, reviewers_ids) |
|
237 | PullRequestModel().update_reviewers(pull_request_id, reviewers_ids) | |
238 | Session().commit() |
|
238 | Session().commit() | |
239 | return True |
|
239 | return True | |
240 | raise HTTPForbidden() |
|
240 | raise HTTPForbidden() | |
241 |
|
241 | |||
242 | @NotAnonymous() |
|
242 | @NotAnonymous() | |
243 | @jsonify |
|
243 | @jsonify | |
244 | def delete(self, repo_name, pull_request_id): |
|
244 | def delete(self, repo_name, pull_request_id): | |
245 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
245 | pull_request = PullRequest.get_or_404(pull_request_id) | |
246 | #only owner can delete it ! |
|
246 | #only owner can delete it ! | |
247 | if pull_request.author.user_id == c.rhodecode_user.user_id: |
|
247 | if pull_request.author.user_id == c.rhodecode_user.user_id: | |
248 | PullRequestModel().delete(pull_request) |
|
248 | PullRequestModel().delete(pull_request) | |
249 | Session().commit() |
|
249 | Session().commit() | |
250 | h.flash(_('Successfully deleted pull request'), |
|
250 | h.flash(_('Successfully deleted pull request'), | |
251 | category='success') |
|
251 | category='success') | |
252 | return redirect(url('admin_settings_my_account', anchor='pullrequests')) |
|
252 | return redirect(url('admin_settings_my_account', anchor='pullrequests')) | |
253 | raise HTTPForbidden() |
|
253 | raise HTTPForbidden() | |
254 |
|
254 | |||
255 | def _load_compare_data(self, pull_request, enable_comments=True): |
|
255 | def _load_compare_data(self, pull_request, enable_comments=True): | |
256 | """ |
|
256 | """ | |
257 | Load context data needed for generating compare diff |
|
257 | Load context data needed for generating compare diff | |
258 |
|
258 | |||
259 | :param pull_request: |
|
259 | :param pull_request: | |
260 | :type pull_request: |
|
260 | :type pull_request: | |
261 | """ |
|
261 | """ | |
262 | org_repo = pull_request.org_repo |
|
262 | org_repo = pull_request.org_repo | |
263 | (org_ref_type, |
|
263 | (org_ref_type, | |
264 | org_ref_name, |
|
264 | org_ref_name, | |
265 | org_ref_rev) = pull_request.org_ref.split(':') |
|
265 | org_ref_rev) = pull_request.org_ref.split(':') | |
266 |
|
266 | |||
267 | other_repo = org_repo |
|
267 | other_repo = org_repo | |
268 | (other_ref_type, |
|
268 | (other_ref_type, | |
269 | other_ref_name, |
|
269 | other_ref_name, | |
270 | other_ref_rev) = pull_request.other_ref.split(':') |
|
270 | other_ref_rev) = pull_request.other_ref.split(':') | |
271 |
|
271 | |||
272 | # despite opening revisions for bookmarks/branches/tags, we always |
|
272 | # despite opening revisions for bookmarks/branches/tags, we always | |
273 | # convert this to rev to prevent changes after bookmark or branch change |
|
273 | # convert this to rev to prevent changes after bookmark or branch change | |
274 | org_ref = ('rev', org_ref_rev) |
|
274 | org_ref = ('rev', org_ref_rev) | |
275 | other_ref = ('rev', other_ref_rev) |
|
275 | other_ref = ('rev', other_ref_rev) | |
276 |
|
276 | |||
277 | c.org_repo = org_repo |
|
277 | c.org_repo = org_repo | |
278 | c.other_repo = other_repo |
|
278 | c.other_repo = other_repo | |
279 |
|
279 | |||
280 | c.fulldiff = fulldiff = request.GET.get('fulldiff') |
|
280 | c.fulldiff = fulldiff = request.GET.get('fulldiff') | |
281 |
|
281 | |||
282 | c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions] |
|
282 | c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions] | |
283 |
|
283 | |||
284 | other_ref = ('rev', getattr(c.cs_ranges[0].parents[0] |
|
|||
285 | if c.cs_ranges[0].parents |
|
|||
286 | else EmptyChangeset(), 'raw_id')) |
|
|||
287 |
|
||||
288 | c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) |
|
284 | c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) | |
289 |
|
285 | |||
290 | c.org_ref = org_ref[1] |
|
286 | c.org_ref = org_ref[1] | |
291 | c.org_ref_type = org_ref[0] |
|
287 | c.org_ref_type = org_ref[0] | |
292 | c.other_ref = other_ref[1] |
|
288 | c.other_ref = other_ref[1] | |
293 | c.other_ref_type = other_ref[0] |
|
289 | c.other_ref_type = other_ref[0] | |
294 |
|
290 | |||
295 | diff_limit = self.cut_off_limit if not fulldiff else None |
|
291 | diff_limit = self.cut_off_limit if not fulldiff else None | |
296 |
|
292 | |||
297 | #we swap org/other ref since we run a simple diff on one repo |
|
293 | #we swap org/other ref since we run a simple diff on one repo | |
298 | _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref) |
|
294 | _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref) | |
299 |
|
295 | |||
300 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', |
|
296 | diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff', | |
301 | diff_limit=diff_limit) |
|
297 | diff_limit=diff_limit) | |
302 | _parsed = diff_processor.prepare() |
|
298 | _parsed = diff_processor.prepare() | |
303 |
|
299 | |||
304 | c.limited_diff = False |
|
300 | c.limited_diff = False | |
305 | if isinstance(_parsed, LimitedDiffContainer): |
|
301 | if isinstance(_parsed, LimitedDiffContainer): | |
306 | c.limited_diff = True |
|
302 | c.limited_diff = True | |
307 |
|
303 | |||
308 | c.files = [] |
|
304 | c.files = [] | |
309 | c.changes = {} |
|
305 | c.changes = {} | |
310 | c.lines_added = 0 |
|
306 | c.lines_added = 0 | |
311 | c.lines_deleted = 0 |
|
307 | c.lines_deleted = 0 | |
312 | for f in _parsed: |
|
308 | for f in _parsed: | |
313 | st = f['stats'] |
|
309 | st = f['stats'] | |
314 | if st[0] != 'b': |
|
310 | if st[0] != 'b': | |
315 | c.lines_added += st[0] |
|
311 | c.lines_added += st[0] | |
316 | c.lines_deleted += st[1] |
|
312 | c.lines_deleted += st[1] | |
317 | fid = h.FID('', f['filename']) |
|
313 | fid = h.FID('', f['filename']) | |
318 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) |
|
314 | c.files.append([fid, f['operation'], f['filename'], f['stats']]) | |
319 | diff = diff_processor.as_html(enable_comments=enable_comments, |
|
315 | diff = diff_processor.as_html(enable_comments=enable_comments, | |
320 | parsed_lines=[f]) |
|
316 | parsed_lines=[f]) | |
321 | c.changes[fid] = [f['operation'], f['filename'], diff] |
|
317 | c.changes[fid] = [f['operation'], f['filename'], diff] | |
322 |
|
318 | |||
323 | def show(self, repo_name, pull_request_id): |
|
319 | def show(self, repo_name, pull_request_id): | |
324 | repo_model = RepoModel() |
|
320 | repo_model = RepoModel() | |
325 | c.users_array = repo_model.get_users_js() |
|
321 | c.users_array = repo_model.get_users_js() | |
326 | c.users_groups_array = repo_model.get_users_groups_js() |
|
322 | c.users_groups_array = repo_model.get_users_groups_js() | |
327 | c.pull_request = PullRequest.get_or_404(pull_request_id) |
|
323 | c.pull_request = PullRequest.get_or_404(pull_request_id) | |
328 | c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request) |
|
324 | c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request) | |
329 | cc_model = ChangesetCommentsModel() |
|
325 | cc_model = ChangesetCommentsModel() | |
330 | cs_model = ChangesetStatusModel() |
|
326 | cs_model = ChangesetStatusModel() | |
331 | _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo, |
|
327 | _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo, | |
332 | pull_request=c.pull_request, |
|
328 | pull_request=c.pull_request, | |
333 | with_revisions=True) |
|
329 | with_revisions=True) | |
334 |
|
330 | |||
335 | cs_statuses = defaultdict(list) |
|
331 | cs_statuses = defaultdict(list) | |
336 | for st in _cs_statuses: |
|
332 | for st in _cs_statuses: | |
337 | cs_statuses[st.author.username] += [st] |
|
333 | cs_statuses[st.author.username] += [st] | |
338 |
|
334 | |||
339 | c.pull_request_reviewers = [] |
|
335 | c.pull_request_reviewers = [] | |
340 | c.pull_request_pending_reviewers = [] |
|
336 | c.pull_request_pending_reviewers = [] | |
341 | for o in c.pull_request.reviewers: |
|
337 | for o in c.pull_request.reviewers: | |
342 | st = cs_statuses.get(o.user.username, None) |
|
338 | st = cs_statuses.get(o.user.username, None) | |
343 | if st: |
|
339 | if st: | |
344 | sorter = lambda k: k.version |
|
340 | sorter = lambda k: k.version | |
345 | st = [(x, list(y)[0]) |
|
341 | st = [(x, list(y)[0]) | |
346 | for x, y in (groupby(sorted(st, key=sorter), sorter))] |
|
342 | for x, y in (groupby(sorted(st, key=sorter), sorter))] | |
347 | else: |
|
343 | else: | |
348 | c.pull_request_pending_reviewers.append(o.user) |
|
344 | c.pull_request_pending_reviewers.append(o.user) | |
349 | c.pull_request_reviewers.append([o.user, st]) |
|
345 | c.pull_request_reviewers.append([o.user, st]) | |
350 |
|
346 | |||
351 | # pull_requests repo_name we opened it against |
|
347 | # pull_requests repo_name we opened it against | |
352 | # ie. other_repo must match |
|
348 | # ie. other_repo must match | |
353 | if repo_name != c.pull_request.other_repo.repo_name: |
|
349 | if repo_name != c.pull_request.other_repo.repo_name: | |
354 | raise HTTPNotFound |
|
350 | raise HTTPNotFound | |
355 |
|
351 | |||
356 | # load compare data into template context |
|
352 | # load compare data into template context | |
357 | enable_comments = not c.pull_request.is_closed() |
|
353 | enable_comments = not c.pull_request.is_closed() | |
358 | self._load_compare_data(c.pull_request, enable_comments=enable_comments) |
|
354 | self._load_compare_data(c.pull_request, enable_comments=enable_comments) | |
359 |
|
355 | |||
360 | # inline comments |
|
356 | # inline comments | |
361 | c.inline_cnt = 0 |
|
357 | c.inline_cnt = 0 | |
362 | c.inline_comments = cc_model.get_inline_comments( |
|
358 | c.inline_comments = cc_model.get_inline_comments( | |
363 | c.rhodecode_db_repo.repo_id, |
|
359 | c.rhodecode_db_repo.repo_id, | |
364 | pull_request=pull_request_id) |
|
360 | pull_request=pull_request_id) | |
365 | # count inline comments |
|
361 | # count inline comments | |
366 | for __, lines in c.inline_comments: |
|
362 | for __, lines in c.inline_comments: | |
367 | for comments in lines.values(): |
|
363 | for comments in lines.values(): | |
368 | c.inline_cnt += len(comments) |
|
364 | c.inline_cnt += len(comments) | |
369 | # comments |
|
365 | # comments | |
370 | c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id, |
|
366 | c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id, | |
371 | pull_request=pull_request_id) |
|
367 | pull_request=pull_request_id) | |
372 |
|
368 | |||
373 | try: |
|
369 | try: | |
374 | cur_status = c.statuses[c.pull_request.revisions[0]][0] |
|
370 | cur_status = c.statuses[c.pull_request.revisions[0]][0] | |
375 | except: |
|
371 | except: | |
376 | log.error(traceback.format_exc()) |
|
372 | log.error(traceback.format_exc()) | |
377 | cur_status = 'undefined' |
|
373 | cur_status = 'undefined' | |
378 | if c.pull_request.is_closed() and 0: |
|
374 | if c.pull_request.is_closed() and 0: | |
379 | c.current_changeset_status = cur_status |
|
375 | c.current_changeset_status = cur_status | |
380 | else: |
|
376 | else: | |
381 | # changeset(pull-request) status calulation based on reviewers |
|
377 | # changeset(pull-request) status calulation based on reviewers | |
382 | c.current_changeset_status = cs_model.calculate_status( |
|
378 | c.current_changeset_status = cs_model.calculate_status( | |
383 | c.pull_request_reviewers, |
|
379 | c.pull_request_reviewers, | |
384 | ) |
|
380 | ) | |
385 | c.changeset_statuses = ChangesetStatus.STATUSES |
|
381 | c.changeset_statuses = ChangesetStatus.STATUSES | |
386 |
|
382 | |||
387 | c.as_form = False |
|
383 | c.as_form = False | |
|
384 | c.ancestor = None # there is one - but right here we don't know which | |||
388 | return render('/pullrequests/pullrequest_show.html') |
|
385 | return render('/pullrequests/pullrequest_show.html') | |
389 |
|
386 | |||
390 | @NotAnonymous() |
|
387 | @NotAnonymous() | |
391 | @jsonify |
|
388 | @jsonify | |
392 | def comment(self, repo_name, pull_request_id): |
|
389 | def comment(self, repo_name, pull_request_id): | |
393 | pull_request = PullRequest.get_or_404(pull_request_id) |
|
390 | pull_request = PullRequest.get_or_404(pull_request_id) | |
394 | if pull_request.is_closed(): |
|
391 | if pull_request.is_closed(): | |
395 | raise HTTPForbidden() |
|
392 | raise HTTPForbidden() | |
396 |
|
393 | |||
397 | status = request.POST.get('changeset_status') |
|
394 | status = request.POST.get('changeset_status') | |
398 | change_status = request.POST.get('change_changeset_status') |
|
395 | change_status = request.POST.get('change_changeset_status') | |
399 | text = request.POST.get('text') |
|
396 | text = request.POST.get('text') | |
400 | close_pr = request.POST.get('save_close') |
|
397 | close_pr = request.POST.get('save_close') | |
401 |
|
398 | |||
402 | allowed_to_change_status = self._get_is_allowed_change_status(pull_request) |
|
399 | allowed_to_change_status = self._get_is_allowed_change_status(pull_request) | |
403 | if status and change_status and allowed_to_change_status: |
|
400 | if status and change_status and allowed_to_change_status: | |
404 | _def = (_('status change -> %s') |
|
401 | _def = (_('status change -> %s') | |
405 | % ChangesetStatus.get_status_lbl(status)) |
|
402 | % ChangesetStatus.get_status_lbl(status)) | |
406 | if close_pr: |
|
403 | if close_pr: | |
407 | _def = _('Closing with') + ' ' + _def |
|
404 | _def = _('Closing with') + ' ' + _def | |
408 | text = text or _def |
|
405 | text = text or _def | |
409 | comm = ChangesetCommentsModel().create( |
|
406 | comm = ChangesetCommentsModel().create( | |
410 | text=text, |
|
407 | text=text, | |
411 | repo=c.rhodecode_db_repo.repo_id, |
|
408 | repo=c.rhodecode_db_repo.repo_id, | |
412 | user=c.rhodecode_user.user_id, |
|
409 | user=c.rhodecode_user.user_id, | |
413 | pull_request=pull_request_id, |
|
410 | pull_request=pull_request_id, | |
414 | f_path=request.POST.get('f_path'), |
|
411 | f_path=request.POST.get('f_path'), | |
415 | line_no=request.POST.get('line'), |
|
412 | line_no=request.POST.get('line'), | |
416 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
413 | status_change=(ChangesetStatus.get_status_lbl(status) | |
417 | if status and change_status |
|
414 | if status and change_status | |
418 | and allowed_to_change_status else None), |
|
415 | and allowed_to_change_status else None), | |
419 | closing_pr=close_pr |
|
416 | closing_pr=close_pr | |
420 | ) |
|
417 | ) | |
421 |
|
418 | |||
422 | action_logger(self.rhodecode_user, |
|
419 | action_logger(self.rhodecode_user, | |
423 | 'user_commented_pull_request:%s' % pull_request_id, |
|
420 | 'user_commented_pull_request:%s' % pull_request_id, | |
424 | c.rhodecode_db_repo, self.ip_addr, self.sa) |
|
421 | c.rhodecode_db_repo, self.ip_addr, self.sa) | |
425 |
|
422 | |||
426 | if allowed_to_change_status: |
|
423 | if allowed_to_change_status: | |
427 | # get status if set ! |
|
424 | # get status if set ! | |
428 | if status and change_status: |
|
425 | if status and change_status: | |
429 | ChangesetStatusModel().set_status( |
|
426 | ChangesetStatusModel().set_status( | |
430 | c.rhodecode_db_repo.repo_id, |
|
427 | c.rhodecode_db_repo.repo_id, | |
431 | status, |
|
428 | status, | |
432 | c.rhodecode_user.user_id, |
|
429 | c.rhodecode_user.user_id, | |
433 | comm, |
|
430 | comm, | |
434 | pull_request=pull_request_id |
|
431 | pull_request=pull_request_id | |
435 | ) |
|
432 | ) | |
436 |
|
433 | |||
437 | if close_pr: |
|
434 | if close_pr: | |
438 | if status in ['rejected', 'approved']: |
|
435 | if status in ['rejected', 'approved']: | |
439 | PullRequestModel().close_pull_request(pull_request_id) |
|
436 | PullRequestModel().close_pull_request(pull_request_id) | |
440 | action_logger(self.rhodecode_user, |
|
437 | action_logger(self.rhodecode_user, | |
441 | 'user_closed_pull_request:%s' % pull_request_id, |
|
438 | 'user_closed_pull_request:%s' % pull_request_id, | |
442 | c.rhodecode_db_repo, self.ip_addr, self.sa) |
|
439 | c.rhodecode_db_repo, self.ip_addr, self.sa) | |
443 | else: |
|
440 | else: | |
444 | h.flash(_('Closing pull request on other statuses than ' |
|
441 | h.flash(_('Closing pull request on other statuses than ' | |
445 | 'rejected or approved forbidden'), |
|
442 | 'rejected or approved forbidden'), | |
446 | category='warning') |
|
443 | category='warning') | |
447 |
|
444 | |||
448 | Session().commit() |
|
445 | Session().commit() | |
449 |
|
446 | |||
450 | if not request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
447 | if not request.environ.get('HTTP_X_PARTIAL_XHR'): | |
451 | return redirect(h.url('pullrequest_show', repo_name=repo_name, |
|
448 | return redirect(h.url('pullrequest_show', repo_name=repo_name, | |
452 | pull_request_id=pull_request_id)) |
|
449 | pull_request_id=pull_request_id)) | |
453 |
|
450 | |||
454 | data = { |
|
451 | data = { | |
455 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), |
|
452 | 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))), | |
456 | } |
|
453 | } | |
457 | if comm: |
|
454 | if comm: | |
458 | c.co = comm |
|
455 | c.co = comm | |
459 | data.update(comm.get_dict()) |
|
456 | data.update(comm.get_dict()) | |
460 | data.update({'rendered_text': |
|
457 | data.update({'rendered_text': | |
461 | render('changeset/changeset_comment_block.html')}) |
|
458 | render('changeset/changeset_comment_block.html')}) | |
462 |
|
459 | |||
463 | return data |
|
460 | return data | |
464 |
|
461 | |||
465 | @NotAnonymous() |
|
462 | @NotAnonymous() | |
466 | @jsonify |
|
463 | @jsonify | |
467 | def delete_comment(self, repo_name, comment_id): |
|
464 | def delete_comment(self, repo_name, comment_id): | |
468 | co = ChangesetComment.get(comment_id) |
|
465 | co = ChangesetComment.get(comment_id) | |
469 | if co.pull_request.is_closed(): |
|
466 | if co.pull_request.is_closed(): | |
470 | #don't allow deleting comments on closed pull request |
|
467 | #don't allow deleting comments on closed pull request | |
471 | raise HTTPForbidden() |
|
468 | raise HTTPForbidden() | |
472 |
|
469 | |||
473 | owner = co.author.user_id == c.rhodecode_user.user_id |
|
470 | owner = co.author.user_id == c.rhodecode_user.user_id | |
474 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: |
|
471 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |
475 | ChangesetCommentsModel().delete(comment=co) |
|
472 | ChangesetCommentsModel().delete(comment=co) | |
476 | Session().commit() |
|
473 | Session().commit() | |
477 | return True |
|
474 | return True | |
478 | else: |
|
475 | else: | |
479 | raise HTTPForbidden() |
|
476 | raise HTTPForbidden() |
@@ -1,398 +1,401 b'' | |||||
1 | """ this is forms validation classes |
|
1 | """ this is forms validation classes | |
2 | http://formencode.org/module-formencode.validators.html |
|
2 | http://formencode.org/module-formencode.validators.html | |
3 | for list off all availible validators |
|
3 | for list off all availible validators | |
4 |
|
4 | |||
5 | we can create our own validators |
|
5 | we can create our own validators | |
6 |
|
6 | |||
7 | The table below outlines the options which can be used in a schema in addition to the validators themselves |
|
7 | The table below outlines the options which can be used in a schema in addition to the validators themselves | |
8 | pre_validators [] These validators will be applied before the schema |
|
8 | pre_validators [] These validators will be applied before the schema | |
9 | chained_validators [] These validators will be applied after the schema |
|
9 | chained_validators [] These validators will be applied after the schema | |
10 | allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present |
|
10 | allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present | |
11 | filter_extra_fields False If True, then keys that aren't associated with a validator are removed |
|
11 | filter_extra_fields False If True, then keys that aren't associated with a validator are removed | |
12 | if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value. |
|
12 | if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value. | |
13 | ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already |
|
13 | ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | <name> = formencode.validators.<name of validator> |
|
16 | <name> = formencode.validators.<name of validator> | |
17 | <name> must equal form name |
|
17 | <name> must equal form name | |
18 | list=[1,2,3,4,5] |
|
18 | list=[1,2,3,4,5] | |
19 | for SELECT use formencode.All(OneOf(list), Int()) |
|
19 | for SELECT use formencode.All(OneOf(list), Int()) | |
20 |
|
20 | |||
21 | """ |
|
21 | """ | |
22 | import logging |
|
22 | import logging | |
23 |
|
23 | |||
24 | import formencode |
|
24 | import formencode | |
25 | from formencode import All |
|
25 | from formencode import All | |
26 |
|
26 | |||
27 | from pylons.i18n.translation import _ |
|
27 | from pylons.i18n.translation import _ | |
28 |
|
28 | |||
29 | from rhodecode.model import validators as v |
|
29 | from rhodecode.model import validators as v | |
30 | from rhodecode import BACKENDS |
|
30 | from rhodecode import BACKENDS | |
31 |
|
31 | |||
32 | log = logging.getLogger(__name__) |
|
32 | log = logging.getLogger(__name__) | |
33 |
|
33 | |||
34 |
|
34 | |||
35 | class LoginForm(formencode.Schema): |
|
35 | class LoginForm(formencode.Schema): | |
36 | allow_extra_fields = True |
|
36 | allow_extra_fields = True | |
37 | filter_extra_fields = True |
|
37 | filter_extra_fields = True | |
38 | username = v.UnicodeString( |
|
38 | username = v.UnicodeString( | |
39 | strip=True, |
|
39 | strip=True, | |
40 | min=1, |
|
40 | min=1, | |
41 | not_empty=True, |
|
41 | not_empty=True, | |
42 | messages={ |
|
42 | messages={ | |
43 | 'empty': _(u'Please enter a login'), |
|
43 | 'empty': _(u'Please enter a login'), | |
44 | 'tooShort': _(u'Enter a value %(min)i characters long or more')} |
|
44 | 'tooShort': _(u'Enter a value %(min)i characters long or more')} | |
45 | ) |
|
45 | ) | |
46 |
|
46 | |||
47 | password = v.UnicodeString( |
|
47 | password = v.UnicodeString( | |
48 | strip=False, |
|
48 | strip=False, | |
49 | min=3, |
|
49 | min=3, | |
50 | not_empty=True, |
|
50 | not_empty=True, | |
51 | messages={ |
|
51 | messages={ | |
52 | 'empty': _(u'Please enter a password'), |
|
52 | 'empty': _(u'Please enter a password'), | |
53 | 'tooShort': _(u'Enter %(min)i characters or more')} |
|
53 | 'tooShort': _(u'Enter %(min)i characters or more')} | |
54 | ) |
|
54 | ) | |
55 |
|
55 | |||
56 | remember = v.StringBoolean(if_missing=False) |
|
56 | remember = v.StringBoolean(if_missing=False) | |
57 |
|
57 | |||
58 | chained_validators = [v.ValidAuth()] |
|
58 | chained_validators = [v.ValidAuth()] | |
59 |
|
59 | |||
60 |
|
60 | |||
61 | def UserForm(edit=False, old_data={}): |
|
61 | def UserForm(edit=False, old_data={}): | |
62 | class _UserForm(formencode.Schema): |
|
62 | class _UserForm(formencode.Schema): | |
63 | allow_extra_fields = True |
|
63 | allow_extra_fields = True | |
64 | filter_extra_fields = True |
|
64 | filter_extra_fields = True | |
65 | username = All(v.UnicodeString(strip=True, min=1, not_empty=True), |
|
65 | username = All(v.UnicodeString(strip=True, min=1, not_empty=True), | |
66 | v.ValidUsername(edit, old_data)) |
|
66 | v.ValidUsername(edit, old_data)) | |
67 | if edit: |
|
67 | if edit: | |
68 | new_password = All( |
|
68 | new_password = All( | |
69 | v.ValidPassword(), |
|
69 | v.ValidPassword(), | |
70 | v.UnicodeString(strip=False, min=6, not_empty=False) |
|
70 | v.UnicodeString(strip=False, min=6, not_empty=False) | |
71 | ) |
|
71 | ) | |
72 | password_confirmation = All( |
|
72 | password_confirmation = All( | |
73 | v.ValidPassword(), |
|
73 | v.ValidPassword(), | |
74 | v.UnicodeString(strip=False, min=6, not_empty=False), |
|
74 | v.UnicodeString(strip=False, min=6, not_empty=False), | |
75 | ) |
|
75 | ) | |
76 | admin = v.StringBoolean(if_missing=False) |
|
76 | admin = v.StringBoolean(if_missing=False) | |
77 | else: |
|
77 | else: | |
78 | password = All( |
|
78 | password = All( | |
79 | v.ValidPassword(), |
|
79 | v.ValidPassword(), | |
80 | v.UnicodeString(strip=False, min=6, not_empty=True) |
|
80 | v.UnicodeString(strip=False, min=6, not_empty=True) | |
81 | ) |
|
81 | ) | |
82 | password_confirmation = All( |
|
82 | password_confirmation = All( | |
83 | v.ValidPassword(), |
|
83 | v.ValidPassword(), | |
84 | v.UnicodeString(strip=False, min=6, not_empty=False) |
|
84 | v.UnicodeString(strip=False, min=6, not_empty=False) | |
85 | ) |
|
85 | ) | |
86 |
|
86 | |||
87 | active = v.StringBoolean(if_missing=False) |
|
87 | active = v.StringBoolean(if_missing=False) | |
88 | firstname = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
88 | firstname = v.UnicodeString(strip=True, min=1, not_empty=False) | |
89 | lastname = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
89 | lastname = v.UnicodeString(strip=True, min=1, not_empty=False) | |
90 | email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) |
|
90 | email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) | |
91 |
|
91 | |||
92 | chained_validators = [v.ValidPasswordsMatch()] |
|
92 | chained_validators = [v.ValidPasswordsMatch()] | |
93 |
|
93 | |||
94 | return _UserForm |
|
94 | return _UserForm | |
95 |
|
95 | |||
96 |
|
96 | |||
97 | def UserGroupForm(edit=False, old_data={}, available_members=[]): |
|
97 | def UserGroupForm(edit=False, old_data={}, available_members=[]): | |
98 | class _UserGroupForm(formencode.Schema): |
|
98 | class _UserGroupForm(formencode.Schema): | |
99 | allow_extra_fields = True |
|
99 | allow_extra_fields = True | |
100 | filter_extra_fields = True |
|
100 | filter_extra_fields = True | |
101 |
|
101 | |||
102 | users_group_name = All( |
|
102 | users_group_name = All( | |
103 | v.UnicodeString(strip=True, min=1, not_empty=True), |
|
103 | v.UnicodeString(strip=True, min=1, not_empty=True), | |
104 | v.ValidUserGroup(edit, old_data) |
|
104 | v.ValidUserGroup(edit, old_data) | |
105 | ) |
|
105 | ) | |
106 |
|
106 | |||
107 | users_group_active = v.StringBoolean(if_missing=False) |
|
107 | users_group_active = v.StringBoolean(if_missing=False) | |
108 |
|
108 | |||
109 | if edit: |
|
109 | if edit: | |
110 | users_group_members = v.OneOf( |
|
110 | users_group_members = v.OneOf( | |
111 | available_members, hideList=False, testValueList=True, |
|
111 | available_members, hideList=False, testValueList=True, | |
112 | if_missing=None, not_empty=False |
|
112 | if_missing=None, not_empty=False | |
113 | ) |
|
113 | ) | |
114 |
|
114 | |||
115 | return _UserGroupForm |
|
115 | return _UserGroupForm | |
116 |
|
116 | |||
117 |
|
117 | |||
118 | def ReposGroupForm(edit=False, old_data={}, available_groups=[], |
|
118 | def ReposGroupForm(edit=False, old_data={}, available_groups=[], | |
119 | can_create_in_root=False): |
|
119 | can_create_in_root=False): | |
120 | class _ReposGroupForm(formencode.Schema): |
|
120 | class _ReposGroupForm(formencode.Schema): | |
121 | allow_extra_fields = True |
|
121 | allow_extra_fields = True | |
122 | filter_extra_fields = False |
|
122 | filter_extra_fields = False | |
123 |
|
123 | |||
124 | group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), |
|
124 | group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | |
125 | v.SlugifyName()) |
|
125 | v.SlugifyName()) | |
126 | group_description = v.UnicodeString(strip=True, min=1, |
|
126 | group_description = v.UnicodeString(strip=True, min=1, | |
127 | not_empty=False) |
|
127 | not_empty=False) | |
128 | if edit: |
|
128 | if edit: | |
129 | #FIXME: do a special check that we cannot move a group to one of |
|
129 | #FIXME: do a special check that we cannot move a group to one of | |
130 | #it's children |
|
130 | #it's children | |
131 | pass |
|
131 | pass | |
132 | group_parent_id = All(v.CanCreateGroup(can_create_in_root), |
|
132 | group_parent_id = All(v.CanCreateGroup(can_create_in_root), | |
133 | v.OneOf(available_groups, hideList=False, |
|
133 | v.OneOf(available_groups, hideList=False, | |
134 | testValueList=True, |
|
134 | testValueList=True, | |
135 | if_missing=None, not_empty=True)) |
|
135 | if_missing=None, not_empty=True)) | |
136 | enable_locking = v.StringBoolean(if_missing=False) |
|
136 | enable_locking = v.StringBoolean(if_missing=False) | |
137 | recursive = v.StringBoolean(if_missing=False) |
|
137 | recursive = v.StringBoolean(if_missing=False) | |
138 | chained_validators = [v.ValidReposGroup(edit, old_data), |
|
138 | chained_validators = [v.ValidReposGroup(edit, old_data), | |
139 | v.ValidPerms('group')] |
|
139 | v.ValidPerms('group')] | |
140 |
|
140 | |||
141 | return _ReposGroupForm |
|
141 | return _ReposGroupForm | |
142 |
|
142 | |||
143 |
|
143 | |||
144 | def RegisterForm(edit=False, old_data={}): |
|
144 | def RegisterForm(edit=False, old_data={}): | |
145 | class _RegisterForm(formencode.Schema): |
|
145 | class _RegisterForm(formencode.Schema): | |
146 | allow_extra_fields = True |
|
146 | allow_extra_fields = True | |
147 | filter_extra_fields = True |
|
147 | filter_extra_fields = True | |
148 | username = All( |
|
148 | username = All( | |
149 | v.ValidUsername(edit, old_data), |
|
149 | v.ValidUsername(edit, old_data), | |
150 | v.UnicodeString(strip=True, min=1, not_empty=True) |
|
150 | v.UnicodeString(strip=True, min=1, not_empty=True) | |
151 | ) |
|
151 | ) | |
152 | password = All( |
|
152 | password = All( | |
153 | v.ValidPassword(), |
|
153 | v.ValidPassword(), | |
154 | v.UnicodeString(strip=False, min=6, not_empty=True) |
|
154 | v.UnicodeString(strip=False, min=6, not_empty=True) | |
155 | ) |
|
155 | ) | |
156 | password_confirmation = All( |
|
156 | password_confirmation = All( | |
157 | v.ValidPassword(), |
|
157 | v.ValidPassword(), | |
158 | v.UnicodeString(strip=False, min=6, not_empty=True) |
|
158 | v.UnicodeString(strip=False, min=6, not_empty=True) | |
159 | ) |
|
159 | ) | |
160 | active = v.StringBoolean(if_missing=False) |
|
160 | active = v.StringBoolean(if_missing=False) | |
161 | firstname = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
161 | firstname = v.UnicodeString(strip=True, min=1, not_empty=False) | |
162 | lastname = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
162 | lastname = v.UnicodeString(strip=True, min=1, not_empty=False) | |
163 | email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) |
|
163 | email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) | |
164 |
|
164 | |||
165 | chained_validators = [v.ValidPasswordsMatch()] |
|
165 | chained_validators = [v.ValidPasswordsMatch()] | |
166 |
|
166 | |||
167 | return _RegisterForm |
|
167 | return _RegisterForm | |
168 |
|
168 | |||
169 |
|
169 | |||
170 | def PasswordResetForm(): |
|
170 | def PasswordResetForm(): | |
171 | class _PasswordResetForm(formencode.Schema): |
|
171 | class _PasswordResetForm(formencode.Schema): | |
172 | allow_extra_fields = True |
|
172 | allow_extra_fields = True | |
173 | filter_extra_fields = True |
|
173 | filter_extra_fields = True | |
174 | email = All(v.ValidSystemEmail(), v.Email(not_empty=True)) |
|
174 | email = All(v.ValidSystemEmail(), v.Email(not_empty=True)) | |
175 | return _PasswordResetForm |
|
175 | return _PasswordResetForm | |
176 |
|
176 | |||
177 |
|
177 | |||
178 | def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), |
|
178 | def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | |
179 | repo_groups=[], landing_revs=[]): |
|
179 | repo_groups=[], landing_revs=[]): | |
180 | class _RepoForm(formencode.Schema): |
|
180 | class _RepoForm(formencode.Schema): | |
181 | allow_extra_fields = True |
|
181 | allow_extra_fields = True | |
182 | filter_extra_fields = False |
|
182 | filter_extra_fields = False | |
183 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), |
|
183 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | |
184 | v.SlugifyName()) |
|
184 | v.SlugifyName()) | |
185 | repo_group = All(v.CanWriteGroup(), |
|
185 | repo_group = All(v.CanWriteGroup(), | |
186 | v.OneOf(repo_groups, hideList=True)) |
|
186 | v.OneOf(repo_groups, hideList=True)) | |
187 | repo_type = v.OneOf(supported_backends) |
|
187 | repo_type = v.OneOf(supported_backends) | |
188 | repo_description = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
188 | repo_description = v.UnicodeString(strip=True, min=1, not_empty=False) | |
189 | repo_private = v.StringBoolean(if_missing=False) |
|
189 | repo_private = v.StringBoolean(if_missing=False) | |
190 | repo_landing_rev = v.OneOf(landing_revs, hideList=True) |
|
190 | repo_landing_rev = v.OneOf(landing_revs, hideList=True) | |
191 | clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False)) |
|
191 | clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False)) | |
192 |
|
192 | |||
193 | repo_enable_statistics = v.StringBoolean(if_missing=False) |
|
193 | repo_enable_statistics = v.StringBoolean(if_missing=False) | |
194 | repo_enable_downloads = v.StringBoolean(if_missing=False) |
|
194 | repo_enable_downloads = v.StringBoolean(if_missing=False) | |
195 | repo_enable_locking = v.StringBoolean(if_missing=False) |
|
195 | repo_enable_locking = v.StringBoolean(if_missing=False) | |
196 |
|
196 | |||
197 | if edit: |
|
197 | if edit: | |
198 | #this is repo owner |
|
198 | #this is repo owner | |
199 | user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser()) |
|
199 | user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser()) | |
200 |
|
200 | |||
201 | chained_validators = [v.ValidCloneUri(), |
|
201 | chained_validators = [v.ValidCloneUri(), | |
202 | v.ValidRepoName(edit, old_data), |
|
202 | v.ValidRepoName(edit, old_data), | |
203 | v.ValidPerms()] |
|
203 | v.ValidPerms()] | |
204 | return _RepoForm |
|
204 | return _RepoForm | |
205 |
|
205 | |||
206 |
|
206 | |||
207 | def RepoFieldForm(): |
|
207 | def RepoFieldForm(): | |
208 | class _RepoFieldForm(formencode.Schema): |
|
208 | class _RepoFieldForm(formencode.Schema): | |
209 | filter_extra_fields = True |
|
209 | filter_extra_fields = True | |
210 | allow_extra_fields = True |
|
210 | allow_extra_fields = True | |
211 |
|
211 | |||
212 | new_field_key = All(v.FieldKey(), |
|
212 | new_field_key = All(v.FieldKey(), | |
213 | v.UnicodeString(strip=True, min=3, not_empty=True)) |
|
213 | v.UnicodeString(strip=True, min=3, not_empty=True)) | |
214 | new_field_value = v.UnicodeString(not_empty=False, if_missing='') |
|
214 | new_field_value = v.UnicodeString(not_empty=False, if_missing='') | |
215 | new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'], |
|
215 | new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'], | |
216 | if_missing='str') |
|
216 | if_missing='str') | |
217 | new_field_label = v.UnicodeString(not_empty=False) |
|
217 | new_field_label = v.UnicodeString(not_empty=False) | |
218 | new_field_desc = v.UnicodeString(not_empty=False) |
|
218 | new_field_desc = v.UnicodeString(not_empty=False) | |
219 |
|
219 | |||
220 | return _RepoFieldForm |
|
220 | return _RepoFieldForm | |
221 |
|
221 | |||
222 |
|
222 | |||
223 | def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), |
|
223 | def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | |
224 | repo_groups=[], landing_revs=[]): |
|
224 | repo_groups=[], landing_revs=[]): | |
225 | class _RepoForm(formencode.Schema): |
|
225 | class _RepoForm(formencode.Schema): | |
226 | allow_extra_fields = True |
|
226 | allow_extra_fields = True | |
227 | filter_extra_fields = False |
|
227 | filter_extra_fields = False | |
228 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), |
|
228 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | |
229 | v.SlugifyName()) |
|
229 | v.SlugifyName()) | |
230 | repo_group = All(v.CanWriteGroup(), |
|
230 | repo_group = All(v.CanWriteGroup(), | |
231 | v.OneOf(repo_groups, hideList=True)) |
|
231 | v.OneOf(repo_groups, hideList=True)) | |
232 | repo_description = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
232 | repo_description = v.UnicodeString(strip=True, min=1, not_empty=False) | |
233 | repo_private = v.StringBoolean(if_missing=False) |
|
233 | repo_private = v.StringBoolean(if_missing=False) | |
234 | repo_landing_rev = v.OneOf(landing_revs, hideList=True) |
|
234 | repo_landing_rev = v.OneOf(landing_revs, hideList=True) | |
235 | clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False)) |
|
235 | clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False)) | |
236 |
|
236 | |||
237 | chained_validators = [v.ValidCloneUri(), |
|
237 | chained_validators = [v.ValidCloneUri(), | |
238 | v.ValidRepoName(edit, old_data), |
|
238 | v.ValidRepoName(edit, old_data), | |
239 | v.ValidPerms(), |
|
239 | v.ValidPerms(), | |
240 | v.ValidSettings()] |
|
240 | v.ValidSettings()] | |
241 | return _RepoForm |
|
241 | return _RepoForm | |
242 |
|
242 | |||
243 |
|
243 | |||
244 | def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), |
|
244 | def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | |
245 | repo_groups=[], landing_revs=[]): |
|
245 | repo_groups=[], landing_revs=[]): | |
246 | class _RepoForkForm(formencode.Schema): |
|
246 | class _RepoForkForm(formencode.Schema): | |
247 | allow_extra_fields = True |
|
247 | allow_extra_fields = True | |
248 | filter_extra_fields = False |
|
248 | filter_extra_fields = False | |
249 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), |
|
249 | repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | |
250 | v.SlugifyName()) |
|
250 | v.SlugifyName()) | |
251 | repo_group = All(v.CanWriteGroup(), |
|
251 | repo_group = All(v.CanWriteGroup(), | |
252 | v.OneOf(repo_groups, hideList=True)) |
|
252 | v.OneOf(repo_groups, hideList=True)) | |
253 | repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends)) |
|
253 | repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends)) | |
254 | description = v.UnicodeString(strip=True, min=1, not_empty=True) |
|
254 | description = v.UnicodeString(strip=True, min=1, not_empty=True) | |
255 | private = v.StringBoolean(if_missing=False) |
|
255 | private = v.StringBoolean(if_missing=False) | |
256 | copy_permissions = v.StringBoolean(if_missing=False) |
|
256 | copy_permissions = v.StringBoolean(if_missing=False) | |
257 | update_after_clone = v.StringBoolean(if_missing=False) |
|
257 | update_after_clone = v.StringBoolean(if_missing=False) | |
258 | fork_parent_id = v.UnicodeString() |
|
258 | fork_parent_id = v.UnicodeString() | |
259 | chained_validators = [v.ValidForkName(edit, old_data)] |
|
259 | chained_validators = [v.ValidForkName(edit, old_data)] | |
260 | landing_rev = v.OneOf(landing_revs, hideList=True) |
|
260 | landing_rev = v.OneOf(landing_revs, hideList=True) | |
261 |
|
261 | |||
262 | return _RepoForkForm |
|
262 | return _RepoForkForm | |
263 |
|
263 | |||
264 |
|
264 | |||
265 | def ApplicationSettingsForm(): |
|
265 | def ApplicationSettingsForm(): | |
266 | class _ApplicationSettingsForm(formencode.Schema): |
|
266 | class _ApplicationSettingsForm(formencode.Schema): | |
267 | allow_extra_fields = True |
|
267 | allow_extra_fields = True | |
268 | filter_extra_fields = False |
|
268 | filter_extra_fields = False | |
269 | rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True) |
|
269 | rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True) | |
270 | rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True) |
|
270 | rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True) | |
271 | rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False) |
|
271 | rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False) | |
272 |
|
272 | |||
273 | return _ApplicationSettingsForm |
|
273 | return _ApplicationSettingsForm | |
274 |
|
274 | |||
275 |
|
275 | |||
276 | def ApplicationVisualisationForm(): |
|
276 | def ApplicationVisualisationForm(): | |
277 | class _ApplicationVisualisationForm(formencode.Schema): |
|
277 | class _ApplicationVisualisationForm(formencode.Schema): | |
278 | allow_extra_fields = True |
|
278 | allow_extra_fields = True | |
279 | filter_extra_fields = False |
|
279 | filter_extra_fields = False | |
280 | rhodecode_show_public_icon = v.StringBoolean(if_missing=False) |
|
280 | rhodecode_show_public_icon = v.StringBoolean(if_missing=False) | |
281 | rhodecode_show_private_icon = v.StringBoolean(if_missing=False) |
|
281 | rhodecode_show_private_icon = v.StringBoolean(if_missing=False) | |
282 | rhodecode_stylify_metatags = v.StringBoolean(if_missing=False) |
|
282 | rhodecode_stylify_metatags = v.StringBoolean(if_missing=False) | |
283 |
|
283 | |||
284 | rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False) |
|
284 | rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False) | |
285 | rhodecode_repository_fields = v.StringBoolean(if_missing=False) |
|
285 | rhodecode_repository_fields = v.StringBoolean(if_missing=False) | |
286 | rhodecode_lightweight_journal = v.StringBoolean(if_missing=False) |
|
286 | rhodecode_lightweight_journal = v.StringBoolean(if_missing=False) | |
287 |
|
287 | |||
288 | return _ApplicationVisualisationForm |
|
288 | return _ApplicationVisualisationForm | |
289 |
|
289 | |||
290 |
|
290 | |||
291 | def ApplicationUiSettingsForm(): |
|
291 | def ApplicationUiSettingsForm(): | |
292 | class _ApplicationUiSettingsForm(formencode.Schema): |
|
292 | class _ApplicationUiSettingsForm(formencode.Schema): | |
293 | allow_extra_fields = True |
|
293 | allow_extra_fields = True | |
294 | filter_extra_fields = False |
|
294 | filter_extra_fields = False | |
295 | web_push_ssl = v.StringBoolean(if_missing=False) |
|
295 | web_push_ssl = v.StringBoolean(if_missing=False) | |
296 | paths_root_path = All( |
|
296 | paths_root_path = All( | |
297 | v.ValidPath(), |
|
297 | v.ValidPath(), | |
298 | v.UnicodeString(strip=True, min=1, not_empty=True) |
|
298 | v.UnicodeString(strip=True, min=1, not_empty=True) | |
299 | ) |
|
299 | ) | |
300 | hooks_changegroup_update = v.StringBoolean(if_missing=False) |
|
300 | hooks_changegroup_update = v.StringBoolean(if_missing=False) | |
301 | hooks_changegroup_repo_size = v.StringBoolean(if_missing=False) |
|
301 | hooks_changegroup_repo_size = v.StringBoolean(if_missing=False) | |
302 | hooks_changegroup_push_logger = v.StringBoolean(if_missing=False) |
|
302 | hooks_changegroup_push_logger = v.StringBoolean(if_missing=False) | |
303 | hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False) |
|
303 | hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False) | |
304 |
|
304 | |||
305 | extensions_largefiles = v.StringBoolean(if_missing=False) |
|
305 | extensions_largefiles = v.StringBoolean(if_missing=False) | |
306 | extensions_hgsubversion = v.StringBoolean(if_missing=False) |
|
306 | extensions_hgsubversion = v.StringBoolean(if_missing=False) | |
307 | extensions_hggit = v.StringBoolean(if_missing=False) |
|
307 | extensions_hggit = v.StringBoolean(if_missing=False) | |
308 |
|
308 | |||
309 | return _ApplicationUiSettingsForm |
|
309 | return _ApplicationUiSettingsForm | |
310 |
|
310 | |||
311 |
|
311 | |||
312 | def DefaultPermissionsForm(repo_perms_choices, group_perms_choices, |
|
312 | def DefaultPermissionsForm(repo_perms_choices, group_perms_choices, | |
313 | register_choices, create_choices, fork_choices): |
|
313 | register_choices, create_choices, fork_choices): | |
314 | class _DefaultPermissionsForm(formencode.Schema): |
|
314 | class _DefaultPermissionsForm(formencode.Schema): | |
315 | allow_extra_fields = True |
|
315 | allow_extra_fields = True | |
316 | filter_extra_fields = True |
|
316 | filter_extra_fields = True | |
317 | overwrite_default_repo = v.StringBoolean(if_missing=False) |
|
317 | overwrite_default_repo = v.StringBoolean(if_missing=False) | |
318 | overwrite_default_group = v.StringBoolean(if_missing=False) |
|
318 | overwrite_default_group = v.StringBoolean(if_missing=False) | |
319 | anonymous = v.StringBoolean(if_missing=False) |
|
319 | anonymous = v.StringBoolean(if_missing=False) | |
320 | default_repo_perm = v.OneOf(repo_perms_choices) |
|
320 | default_repo_perm = v.OneOf(repo_perms_choices) | |
321 | default_group_perm = v.OneOf(group_perms_choices) |
|
321 | default_group_perm = v.OneOf(group_perms_choices) | |
322 | default_register = v.OneOf(register_choices) |
|
322 | default_register = v.OneOf(register_choices) | |
323 | default_create = v.OneOf(create_choices) |
|
323 | default_create = v.OneOf(create_choices) | |
324 | default_fork = v.OneOf(fork_choices) |
|
324 | default_fork = v.OneOf(fork_choices) | |
325 |
|
325 | |||
326 | return _DefaultPermissionsForm |
|
326 | return _DefaultPermissionsForm | |
327 |
|
327 | |||
328 |
|
328 | |||
329 | def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): |
|
329 | def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): | |
330 | class _DefaultsForm(formencode.Schema): |
|
330 | class _DefaultsForm(formencode.Schema): | |
331 | allow_extra_fields = True |
|
331 | allow_extra_fields = True | |
332 | filter_extra_fields = True |
|
332 | filter_extra_fields = True | |
333 | default_repo_type = v.OneOf(supported_backends) |
|
333 | default_repo_type = v.OneOf(supported_backends) | |
334 | default_repo_private = v.StringBoolean(if_missing=False) |
|
334 | default_repo_private = v.StringBoolean(if_missing=False) | |
335 | default_repo_enable_statistics = v.StringBoolean(if_missing=False) |
|
335 | default_repo_enable_statistics = v.StringBoolean(if_missing=False) | |
336 | default_repo_enable_downloads = v.StringBoolean(if_missing=False) |
|
336 | default_repo_enable_downloads = v.StringBoolean(if_missing=False) | |
337 | default_repo_enable_locking = v.StringBoolean(if_missing=False) |
|
337 | default_repo_enable_locking = v.StringBoolean(if_missing=False) | |
338 |
|
338 | |||
339 | return _DefaultsForm |
|
339 | return _DefaultsForm | |
340 |
|
340 | |||
341 |
|
341 | |||
342 | def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, |
|
342 | def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, | |
343 | tls_kind_choices): |
|
343 | tls_kind_choices): | |
344 | class _LdapSettingsForm(formencode.Schema): |
|
344 | class _LdapSettingsForm(formencode.Schema): | |
345 | allow_extra_fields = True |
|
345 | allow_extra_fields = True | |
346 | filter_extra_fields = True |
|
346 | filter_extra_fields = True | |
347 | #pre_validators = [LdapLibValidator] |
|
347 | #pre_validators = [LdapLibValidator] | |
348 | ldap_active = v.StringBoolean(if_missing=False) |
|
348 | ldap_active = v.StringBoolean(if_missing=False) | |
349 | ldap_host = v.UnicodeString(strip=True,) |
|
349 | ldap_host = v.UnicodeString(strip=True,) | |
350 | ldap_port = v.Number(strip=True,) |
|
350 | ldap_port = v.Number(strip=True,) | |
351 | ldap_tls_kind = v.OneOf(tls_kind_choices) |
|
351 | ldap_tls_kind = v.OneOf(tls_kind_choices) | |
352 | ldap_tls_reqcert = v.OneOf(tls_reqcert_choices) |
|
352 | ldap_tls_reqcert = v.OneOf(tls_reqcert_choices) | |
353 | ldap_dn_user = v.UnicodeString(strip=True,) |
|
353 | ldap_dn_user = v.UnicodeString(strip=True,) | |
354 | ldap_dn_pass = v.UnicodeString(strip=True,) |
|
354 | ldap_dn_pass = v.UnicodeString(strip=True,) | |
355 | ldap_base_dn = v.UnicodeString(strip=True,) |
|
355 | ldap_base_dn = v.UnicodeString(strip=True,) | |
356 | ldap_filter = v.UnicodeString(strip=True,) |
|
356 | ldap_filter = v.UnicodeString(strip=True,) | |
357 | ldap_search_scope = v.OneOf(search_scope_choices) |
|
357 | ldap_search_scope = v.OneOf(search_scope_choices) | |
358 | ldap_attr_login = All( |
|
358 | ldap_attr_login = All( | |
359 | v.AttrLoginValidator(), |
|
359 | v.AttrLoginValidator(), | |
360 | v.UnicodeString(strip=True,) |
|
360 | v.UnicodeString(strip=True,) | |
361 | ) |
|
361 | ) | |
362 | ldap_attr_firstname = v.UnicodeString(strip=True,) |
|
362 | ldap_attr_firstname = v.UnicodeString(strip=True,) | |
363 | ldap_attr_lastname = v.UnicodeString(strip=True,) |
|
363 | ldap_attr_lastname = v.UnicodeString(strip=True,) | |
364 | ldap_attr_email = v.UnicodeString(strip=True,) |
|
364 | ldap_attr_email = v.UnicodeString(strip=True,) | |
365 |
|
365 | |||
366 | return _LdapSettingsForm |
|
366 | return _LdapSettingsForm | |
367 |
|
367 | |||
368 |
|
368 | |||
369 | def UserExtraEmailForm(): |
|
369 | def UserExtraEmailForm(): | |
370 | class _UserExtraEmailForm(formencode.Schema): |
|
370 | class _UserExtraEmailForm(formencode.Schema): | |
371 | email = All(v.UniqSystemEmail(), v.Email(not_empty=True)) |
|
371 | email = All(v.UniqSystemEmail(), v.Email(not_empty=True)) | |
372 | return _UserExtraEmailForm |
|
372 | return _UserExtraEmailForm | |
373 |
|
373 | |||
374 |
|
374 | |||
375 | def UserExtraIpForm(): |
|
375 | def UserExtraIpForm(): | |
376 | class _UserExtraIpForm(formencode.Schema): |
|
376 | class _UserExtraIpForm(formencode.Schema): | |
377 | ip = v.ValidIp()(not_empty=True) |
|
377 | ip = v.ValidIp()(not_empty=True) | |
378 | return _UserExtraIpForm |
|
378 | return _UserExtraIpForm | |
379 |
|
379 | |||
380 |
|
380 | |||
381 | def PullRequestForm(repo_id): |
|
381 | def PullRequestForm(repo_id): | |
382 | class _PullRequestForm(formencode.Schema): |
|
382 | class _PullRequestForm(formencode.Schema): | |
383 | allow_extra_fields = True |
|
383 | allow_extra_fields = True | |
384 | filter_extra_fields = True |
|
384 | filter_extra_fields = True | |
385 |
|
385 | |||
386 | user = v.UnicodeString(strip=True, required=True) |
|
386 | user = v.UnicodeString(strip=True, required=True) | |
387 | org_repo = v.UnicodeString(strip=True, required=True) |
|
387 | org_repo = v.UnicodeString(strip=True, required=True) | |
388 | org_ref = v.UnicodeString(strip=True, required=True) |
|
388 | org_ref = v.UnicodeString(strip=True, required=True) | |
389 | other_repo = v.UnicodeString(strip=True, required=True) |
|
389 | other_repo = v.UnicodeString(strip=True, required=True) | |
390 | other_ref = v.UnicodeString(strip=True, required=True) |
|
390 | other_ref = v.UnicodeString(strip=True, required=True) | |
391 | revisions = All(#v.NotReviewedRevisions(repo_id)(), |
|
391 | revisions = All(#v.NotReviewedRevisions(repo_id)(), | |
392 | v.UniqueList(not_empty=True)) |
|
392 | v.UniqueList(not_empty=True)) | |
393 | review_members = v.UniqueList(not_empty=True) |
|
393 | review_members = v.UniqueList(not_empty=True) | |
394 |
|
394 | |||
395 | pullrequest_title = v.UnicodeString(strip=True, required=True, min=3) |
|
395 | pullrequest_title = v.UnicodeString(strip=True, required=True, min=3) | |
396 | pullrequest_desc = v.UnicodeString(strip=True, required=False) |
|
396 | pullrequest_desc = v.UnicodeString(strip=True, required=False) | |
397 |
|
397 | |||
|
398 | ancestor_rev = v.UnicodeString(strip=True, required=True) | |||
|
399 | merge_rev = v.UnicodeString(strip=True, required=True) | |||
|
400 | ||||
398 | return _PullRequestForm |
|
401 | return _PullRequestForm |
@@ -1,255 +1,261 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.model.pull_request |
|
3 | rhodecode.model.pull_request | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | pull request model for RhodeCode |
|
6 | pull request model for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Jun 6, 2012 |
|
8 | :created_on: Jun 6, 2012 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 | import datetime |
|
27 | import datetime | |
28 | import re |
|
28 | import re | |
29 |
|
29 | |||
30 | from pylons.i18n.translation import _ |
|
30 | from pylons.i18n.translation import _ | |
31 |
|
31 | |||
32 | from rhodecode.model.meta import Session |
|
32 | from rhodecode.model.meta import Session | |
33 | from rhodecode.lib import helpers as h, unionrepo |
|
33 | from rhodecode.lib import helpers as h, unionrepo | |
34 | from rhodecode.model import BaseModel |
|
34 | from rhodecode.model import BaseModel | |
35 | from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\ |
|
35 | from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\ | |
36 | ChangesetStatus |
|
36 | ChangesetStatus | |
37 | from rhodecode.model.notification import NotificationModel |
|
37 | from rhodecode.model.notification import NotificationModel | |
38 | from rhodecode.lib.utils2 import safe_unicode |
|
38 | from rhodecode.lib.utils2 import safe_unicode | |
39 |
|
39 | |||
40 | from rhodecode.lib.vcs.utils.hgcompat import scmutil |
|
40 | from rhodecode.lib.vcs.utils.hgcompat import scmutil | |
41 | from rhodecode.lib.vcs.utils import safe_str |
|
41 | from rhodecode.lib.vcs.utils import safe_str | |
42 | from rhodecode.lib.vcs.backends.base import EmptyChangeset |
|
42 | from rhodecode.lib.vcs.backends.base import EmptyChangeset | |
43 |
|
43 | |||
44 | log = logging.getLogger(__name__) |
|
44 | log = logging.getLogger(__name__) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | class PullRequestModel(BaseModel): |
|
47 | class PullRequestModel(BaseModel): | |
48 |
|
48 | |||
49 | cls = PullRequest |
|
49 | cls = PullRequest | |
50 |
|
50 | |||
51 | def __get_pull_request(self, pull_request): |
|
51 | def __get_pull_request(self, pull_request): | |
52 | return self._get_instance(PullRequest, pull_request) |
|
52 | return self._get_instance(PullRequest, pull_request) | |
53 |
|
53 | |||
54 | def get_all(self, repo): |
|
54 | def get_all(self, repo): | |
55 | repo = self._get_repo(repo) |
|
55 | repo = self._get_repo(repo) | |
56 | return PullRequest.query()\ |
|
56 | return PullRequest.query()\ | |
57 | .filter(PullRequest.other_repo == repo)\ |
|
57 | .filter(PullRequest.other_repo == repo)\ | |
58 | .order_by(PullRequest.created_on.desc())\ |
|
58 | .order_by(PullRequest.created_on.desc())\ | |
59 | .all() |
|
59 | .all() | |
60 |
|
60 | |||
61 | def create(self, created_by, org_repo, org_ref, other_repo, other_ref, |
|
61 | def create(self, created_by, org_repo, org_ref, other_repo, other_ref, | |
62 | revisions, reviewers, title, description=None): |
|
62 | revisions, reviewers, title, description=None): | |
63 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
63 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
64 |
|
64 | |||
65 | created_by_user = self._get_user(created_by) |
|
65 | created_by_user = self._get_user(created_by) | |
66 | org_repo = self._get_repo(org_repo) |
|
66 | org_repo = self._get_repo(org_repo) | |
67 | other_repo = self._get_repo(other_repo) |
|
67 | other_repo = self._get_repo(other_repo) | |
68 |
|
68 | |||
69 | new = PullRequest() |
|
69 | new = PullRequest() | |
70 | new.org_repo = org_repo |
|
70 | new.org_repo = org_repo | |
71 | new.org_ref = org_ref |
|
71 | new.org_ref = org_ref | |
72 | new.other_repo = other_repo |
|
72 | new.other_repo = other_repo | |
73 | new.other_ref = other_ref |
|
73 | new.other_ref = other_ref | |
74 | new.revisions = revisions |
|
74 | new.revisions = revisions | |
75 | new.title = title |
|
75 | new.title = title | |
76 | new.description = description |
|
76 | new.description = description | |
77 | new.author = created_by_user |
|
77 | new.author = created_by_user | |
78 | Session().add(new) |
|
78 | Session().add(new) | |
79 | Session().flush() |
|
79 | Session().flush() | |
80 | #members |
|
80 | #members | |
81 | for member in set(reviewers): |
|
81 | for member in set(reviewers): | |
82 | _usr = self._get_user(member) |
|
82 | _usr = self._get_user(member) | |
83 | reviewer = PullRequestReviewers(_usr, new) |
|
83 | reviewer = PullRequestReviewers(_usr, new) | |
84 | Session().add(reviewer) |
|
84 | Session().add(reviewer) | |
85 |
|
85 | |||
86 | #reset state to under-review |
|
86 | #reset state to under-review | |
87 | ChangesetStatusModel().set_status( |
|
87 | ChangesetStatusModel().set_status( | |
88 | repo=org_repo, |
|
88 | repo=org_repo, | |
89 | status=ChangesetStatus.STATUS_UNDER_REVIEW, |
|
89 | status=ChangesetStatus.STATUS_UNDER_REVIEW, | |
90 | user=created_by_user, |
|
90 | user=created_by_user, | |
91 | pull_request=new |
|
91 | pull_request=new | |
92 | ) |
|
92 | ) | |
93 | revision_data = [(x.raw_id, x.message) |
|
93 | revision_data = [(x.raw_id, x.message) | |
94 | for x in map(org_repo.get_changeset, revisions)] |
|
94 | for x in map(org_repo.get_changeset, revisions)] | |
95 | #notification to reviewers |
|
95 | #notification to reviewers | |
96 | notif = NotificationModel() |
|
96 | notif = NotificationModel() | |
97 |
|
97 | |||
98 | pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name, |
|
98 | pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name, | |
99 | pull_request_id=new.pull_request_id, |
|
99 | pull_request_id=new.pull_request_id, | |
100 | qualified=True, |
|
100 | qualified=True, | |
101 | ) |
|
101 | ) | |
102 | subject = safe_unicode( |
|
102 | subject = safe_unicode( | |
103 | h.link_to( |
|
103 | h.link_to( | |
104 | _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \ |
|
104 | _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \ | |
105 | {'user': created_by_user.username, |
|
105 | {'user': created_by_user.username, | |
106 | 'pr_title': new.title, |
|
106 | 'pr_title': new.title, | |
107 | 'pr_id': new.pull_request_id}, |
|
107 | 'pr_id': new.pull_request_id}, | |
108 | pr_url |
|
108 | pr_url | |
109 | ) |
|
109 | ) | |
110 | ) |
|
110 | ) | |
111 | body = description |
|
111 | body = description | |
112 | kwargs = { |
|
112 | kwargs = { | |
113 | 'pr_title': title, |
|
113 | 'pr_title': title, | |
114 | 'pr_user_created': h.person(created_by_user.email), |
|
114 | 'pr_user_created': h.person(created_by_user.email), | |
115 | 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name, |
|
115 | 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name, | |
116 | qualified=True,), |
|
116 | qualified=True,), | |
117 | 'pr_url': pr_url, |
|
117 | 'pr_url': pr_url, | |
118 | 'pr_revisions': revision_data |
|
118 | 'pr_revisions': revision_data | |
119 | } |
|
119 | } | |
120 |
|
120 | |||
121 | notif.create(created_by=created_by_user, subject=subject, body=body, |
|
121 | notif.create(created_by=created_by_user, subject=subject, body=body, | |
122 | recipients=reviewers, |
|
122 | recipients=reviewers, | |
123 | type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs) |
|
123 | type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs) | |
124 | return new |
|
124 | return new | |
125 |
|
125 | |||
126 | def update_reviewers(self, pull_request, reviewers_ids): |
|
126 | def update_reviewers(self, pull_request, reviewers_ids): | |
127 | reviewers_ids = set(reviewers_ids) |
|
127 | reviewers_ids = set(reviewers_ids) | |
128 | pull_request = self.__get_pull_request(pull_request) |
|
128 | pull_request = self.__get_pull_request(pull_request) | |
129 | current_reviewers = PullRequestReviewers.query()\ |
|
129 | current_reviewers = PullRequestReviewers.query()\ | |
130 | .filter(PullRequestReviewers.pull_request== |
|
130 | .filter(PullRequestReviewers.pull_request== | |
131 | pull_request)\ |
|
131 | pull_request)\ | |
132 | .all() |
|
132 | .all() | |
133 | current_reviewers_ids = set([x.user.user_id for x in current_reviewers]) |
|
133 | current_reviewers_ids = set([x.user.user_id for x in current_reviewers]) | |
134 |
|
134 | |||
135 | to_add = reviewers_ids.difference(current_reviewers_ids) |
|
135 | to_add = reviewers_ids.difference(current_reviewers_ids) | |
136 | to_remove = current_reviewers_ids.difference(reviewers_ids) |
|
136 | to_remove = current_reviewers_ids.difference(reviewers_ids) | |
137 |
|
137 | |||
138 | log.debug("Adding %s reviewers" % to_add) |
|
138 | log.debug("Adding %s reviewers" % to_add) | |
139 | log.debug("Removing %s reviewers" % to_remove) |
|
139 | log.debug("Removing %s reviewers" % to_remove) | |
140 |
|
140 | |||
141 | for uid in to_add: |
|
141 | for uid in to_add: | |
142 | _usr = self._get_user(uid) |
|
142 | _usr = self._get_user(uid) | |
143 | reviewer = PullRequestReviewers(_usr, pull_request) |
|
143 | reviewer = PullRequestReviewers(_usr, pull_request) | |
144 | Session().add(reviewer) |
|
144 | Session().add(reviewer) | |
145 |
|
145 | |||
146 | for uid in to_remove: |
|
146 | for uid in to_remove: | |
147 | reviewer = PullRequestReviewers.query()\ |
|
147 | reviewer = PullRequestReviewers.query()\ | |
148 | .filter(PullRequestReviewers.user_id==uid, |
|
148 | .filter(PullRequestReviewers.user_id==uid, | |
149 | PullRequestReviewers.pull_request==pull_request)\ |
|
149 | PullRequestReviewers.pull_request==pull_request)\ | |
150 | .scalar() |
|
150 | .scalar() | |
151 | if reviewer: |
|
151 | if reviewer: | |
152 | Session().delete(reviewer) |
|
152 | Session().delete(reviewer) | |
153 |
|
153 | |||
154 | def delete(self, pull_request): |
|
154 | def delete(self, pull_request): | |
155 | pull_request = self.__get_pull_request(pull_request) |
|
155 | pull_request = self.__get_pull_request(pull_request) | |
156 | Session().delete(pull_request) |
|
156 | Session().delete(pull_request) | |
157 |
|
157 | |||
158 | def close_pull_request(self, pull_request): |
|
158 | def close_pull_request(self, pull_request): | |
159 | pull_request = self.__get_pull_request(pull_request) |
|
159 | pull_request = self.__get_pull_request(pull_request) | |
160 | pull_request.status = PullRequest.STATUS_CLOSED |
|
160 | pull_request.status = PullRequest.STATUS_CLOSED | |
161 | pull_request.updated_on = datetime.datetime.now() |
|
161 | pull_request.updated_on = datetime.datetime.now() | |
162 | Session().add(pull_request) |
|
162 | Session().add(pull_request) | |
163 |
|
163 | |||
164 | def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref): |
|
164 | def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge): | |
165 | """ |
|
165 | """ | |
166 | Returns a list of changesets that can be merged from org_repo@org_ref |
|
166 | Returns a list of changesets that can be merged from org_repo@org_ref | |
167 | to other_repo@other_ref ... and the ancestor that would be used for merge |
|
167 | to other_repo@other_ref ... and the ancestor that would be used for merge | |
168 |
|
168 | |||
169 | :param org_repo: |
|
169 | :param org_repo: | |
170 | :param org_ref: |
|
170 | :param org_ref: | |
171 | :param other_repo: |
|
171 | :param other_repo: | |
172 | :param other_ref: |
|
172 | :param other_ref: | |
173 | :param tmp: |
|
173 | :param tmp: | |
174 | """ |
|
174 | """ | |
175 |
|
175 | |||
176 | ancestor = None |
|
176 | ancestor = None | |
177 |
|
177 | |||
178 | if alias == 'hg': |
|
178 | if alias == 'hg': | |
179 | # lookup up the exact node id |
|
179 | # lookup up the exact node id | |
180 | _revset_predicates = { |
|
180 | _revset_predicates = { | |
181 | 'branch': 'branch', |
|
181 | 'branch': 'branch', | |
182 | 'book': 'bookmark', |
|
182 | 'book': 'bookmark', | |
183 | 'tag': 'tag', |
|
183 | 'tag': 'tag', | |
184 | 'rev': 'id', |
|
184 | 'rev': 'id', | |
185 | } |
|
185 | } | |
186 |
|
186 | |||
187 | org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]], |
|
187 | org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]], | |
188 | safe_str(org_ref[1])) |
|
188 | safe_str(org_ref[1])) | |
189 | if org_ref[1] == EmptyChangeset().raw_id: |
|
189 | if org_ref[1] == EmptyChangeset().raw_id: | |
190 | org_rev = org_ref[1] |
|
190 | org_rev = org_ref[1] | |
191 | else: |
|
191 | else: | |
192 | org_rev = org_repo._repo[scmutil.revrange(org_repo._repo, |
|
192 | org_rev = org_repo._repo[scmutil.revrange(org_repo._repo, | |
193 | [org_rev_spec])[-1]] |
|
193 | [org_rev_spec])[-1]] | |
194 | other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]], |
|
194 | other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]], | |
195 | safe_str(other_ref[1])) |
|
195 | safe_str(other_ref[1])) | |
196 | if other_ref[1] == EmptyChangeset().raw_id: |
|
196 | if other_ref[1] == EmptyChangeset().raw_id: | |
197 | other_rev = other_ref[1] |
|
197 | other_rev = other_ref[1] | |
198 | else: |
|
198 | else: | |
199 | other_rev = other_repo._repo[scmutil.revrange(other_repo._repo, |
|
199 | other_rev = other_repo._repo[scmutil.revrange(other_repo._repo, | |
200 | [other_rev_spec])[-1]] |
|
200 | [other_rev_spec])[-1]] | |
201 |
|
201 | |||
202 | #case two independent repos |
|
202 | #case two independent repos | |
203 | if org_repo != other_repo: |
|
203 | if org_repo != other_repo: | |
204 | hgrepo = unionrepo.unionrepository(other_repo.baseui, |
|
204 | hgrepo = unionrepo.unionrepository(other_repo.baseui, | |
205 | other_repo.path, |
|
205 | other_repo.path, | |
206 | org_repo.path) |
|
206 | org_repo.path) | |
207 | # all the changesets we are looking for will be in other_repo, |
|
207 | # all the changesets we are looking for will be in other_repo, | |
208 | # so rev numbers from hgrepo can be used in other_repo |
|
208 | # so rev numbers from hgrepo can be used in other_repo | |
209 |
|
209 | |||
210 | #no remote compare do it on the same repository |
|
210 | #no remote compare do it on the same repository | |
211 | else: |
|
211 | else: | |
212 | hgrepo = other_repo._repo |
|
212 | hgrepo = other_repo._repo | |
213 |
|
213 | |||
214 | revs = ["ancestors(id('%s')) and not ancestors(id('%s'))" % |
|
214 | if merge: | |
215 | (other_rev, org_rev)] |
|
215 | revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" % | |
216 | changesets = [other_repo.get_changeset(cs) |
|
216 | (other_rev, org_rev, org_rev)] | |
217 | for cs in scmutil.revrange(hgrepo, revs)] |
|
|||
218 |
|
217 | |||
219 | if org_repo != other_repo: |
|
|||
220 | ancestors = scmutil.revrange(hgrepo, |
|
218 | ancestors = scmutil.revrange(hgrepo, | |
221 | ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)]) |
|
219 | ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)]) | |
222 | if len(ancestors) == 1: |
|
220 | if len(ancestors) == 1: | |
223 | ancestor = hgrepo[ancestors[0]].hex() |
|
221 | ancestor = hgrepo[ancestors[0]].hex() | |
|
222 | else: | |||
|
223 | # TODO: have both + and - changesets | |||
|
224 | revs = ["id('%s') :: id('%s') - id('%s')" % | |||
|
225 | (org_rev, other_rev, org_rev)] | |||
|
226 | ||||
|
227 | changesets = [other_repo.get_changeset(cs) | |||
|
228 | for cs in scmutil.revrange(hgrepo, revs)] | |||
224 |
|
229 | |||
225 | elif alias == 'git': |
|
230 | elif alias == 'git': | |
226 | assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos |
|
231 | assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos | |
227 | so, se = org_repo.run_git_command( |
|
232 | so, se = org_repo.run_git_command( | |
228 | 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1], |
|
233 | 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1], | |
229 | other_ref[1]) |
|
234 | other_ref[1]) | |
230 | ) |
|
235 | ) | |
231 | changesets = [org_repo.get_changeset(cs) |
|
236 | changesets = [org_repo.get_changeset(cs) | |
232 | for cs in re.findall(r'[0-9a-fA-F]{40}', so)] |
|
237 | for cs in re.findall(r'[0-9a-fA-F]{40}', so)] | |
233 |
|
238 | |||
234 | return changesets, ancestor |
|
239 | return changesets, ancestor | |
235 |
|
240 | |||
236 | def get_compare_data(self, org_repo, org_ref, other_repo, other_ref): |
|
241 | def get_compare_data(self, org_repo, org_ref, other_repo, other_ref, merge): | |
237 | """ |
|
242 | """ | |
238 | Returns incoming changesets for mercurial repositories |
|
243 | Returns incoming changesets for mercurial repositories | |
239 |
|
244 | |||
240 | :param org_repo: |
|
245 | :param org_repo: | |
241 | :param org_ref: |
|
246 | :param org_ref: | |
242 | :param other_repo: |
|
247 | :param other_repo: | |
243 | :param other_ref: |
|
248 | :param other_ref: | |
244 | """ |
|
249 | """ | |
245 |
|
250 | |||
246 | if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)): |
|
251 | if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)): | |
247 | raise Exception('org_ref must be a two element list/tuple') |
|
252 | raise Exception('org_ref must be a two element list/tuple') | |
248 |
|
253 | |||
249 | if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)): |
|
254 | if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)): | |
250 | raise Exception('other_ref must be a two element list/tuple') |
|
255 | raise Exception('other_ref must be a two element list/tuple') | |
251 |
|
256 | |||
252 | cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias, |
|
257 | cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias, | |
253 | org_repo.scm_instance, org_ref, |
|
258 | org_repo.scm_instance, org_ref, | |
254 |
other_repo.scm_instance, other_ref |
|
259 | other_repo.scm_instance, other_ref, | |
|
260 | merge) | |||
255 | return cs_ranges, ancestor |
|
261 | return cs_ranges, ancestor |
@@ -1,296 +1,296 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | <%inherit file="/base/base.html"/> |
|
3 | <%inherit file="/base/base.html"/> | |
4 |
|
4 | |||
5 | <%def name="title()"> |
|
5 | <%def name="title()"> | |
6 | ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name} |
|
6 | ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name} | |
7 | </%def> |
|
7 | </%def> | |
8 |
|
8 | |||
9 | <%def name="breadcrumbs_links()"> |
|
9 | <%def name="breadcrumbs_links()"> | |
10 | ${h.link_to(_(u'Home'),h.url('/'))} |
|
10 | ${h.link_to(_(u'Home'),h.url('/'))} | |
11 | » |
|
11 | » | |
12 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} |
|
12 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} | |
13 | » |
|
13 | » | |
14 | <% size = c.size if c.size <= c.total_cs else c.total_cs %> |
|
14 | <% size = c.size if c.size <= c.total_cs else c.total_cs %> | |
15 | ${_('changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)} |
|
15 | ${_('changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)} | |
16 | </%def> |
|
16 | </%def> | |
17 |
|
17 | |||
18 | <%def name="page_nav()"> |
|
18 | <%def name="page_nav()"> | |
19 | ${self.menu('changelog')} |
|
19 | ${self.menu('changelog')} | |
20 | </%def> |
|
20 | </%def> | |
21 |
|
21 | |||
22 | <%def name="main()"> |
|
22 | <%def name="main()"> | |
23 | <div class="box"> |
|
23 | <div class="box"> | |
24 | <!-- box / title --> |
|
24 | <!-- box / title --> | |
25 | <div class="title"> |
|
25 | <div class="title"> | |
26 | ${self.breadcrumbs()} |
|
26 | ${self.breadcrumbs()} | |
27 | </div> |
|
27 | </div> | |
28 | <div class="table"> |
|
28 | <div class="table"> | |
29 | % if c.pagination: |
|
29 | % if c.pagination: | |
30 | <div id="graph"> |
|
30 | <div id="graph"> | |
31 | <div id="graph_nodes"> |
|
31 | <div id="graph_nodes"> | |
32 | <canvas id="graph_canvas"></canvas> |
|
32 | <canvas id="graph_canvas"></canvas> | |
33 | </div> |
|
33 | </div> | |
34 | <div id="graph_content"> |
|
34 | <div id="graph_content"> | |
35 | <div class="info_box" style="clear: both;padding: 10px 6px;min-height: 12px;text-align: right;"> |
|
35 | <div class="info_box" style="clear: both;padding: 10px 6px;min-height: 12px;text-align: right;"> | |
36 | <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a> |
|
36 | <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a> | |
37 | <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a> |
|
37 | <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a> | |
38 |
|
38 | |||
39 | %if c.rhodecode_db_repo.fork: |
|
39 | %if c.rhodecode_db_repo.fork: | |
40 | <a id="compare_fork" title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default')}" class="ui-btn small">${_('Compare fork with parent')}</a> |
|
40 | <a id="compare_fork" title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default',merge=1)}" class="ui-btn small">${_('Compare fork with parent')}</a> | |
41 | %endif |
|
41 | %endif | |
42 | %if h.is_hg(c.rhodecode_repo): |
|
42 | %if h.is_hg(c.rhodecode_repo): | |
43 | <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a> |
|
43 | <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a> | |
44 | %endif |
|
44 | %endif | |
45 | </div> |
|
45 | </div> | |
46 | <div class="container_header"> |
|
46 | <div class="container_header"> | |
47 | ${h.form(h.url.current(),method='get')} |
|
47 | ${h.form(h.url.current(),method='get')} | |
48 | <div class="info_box" style="float:left"> |
|
48 | <div class="info_box" style="float:left"> | |
49 | ${h.submit('set',_('Show'),class_="ui-btn")} |
|
49 | ${h.submit('set',_('Show'),class_="ui-btn")} | |
50 | ${h.text('size',size=1,value=c.size)} |
|
50 | ${h.text('size',size=1,value=c.size)} | |
51 | ${_('revisions')} |
|
51 | ${_('revisions')} | |
52 | </div> |
|
52 | </div> | |
53 | ${h.end_form()} |
|
53 | ${h.end_form()} | |
54 | <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div> |
|
54 | <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div> | |
55 | </div> |
|
55 | </div> | |
56 |
|
56 | |||
57 | %for cnt,cs in enumerate(c.pagination): |
|
57 | %for cnt,cs in enumerate(c.pagination): | |
58 | <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}"> |
|
58 | <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}"> | |
59 | <div class="left"> |
|
59 | <div class="left"> | |
60 | <div> |
|
60 | <div> | |
61 | ${h.checkbox(cs.raw_id,class_="changeset_range")} |
|
61 | ${h.checkbox(cs.raw_id,class_="changeset_range")} | |
62 | <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span> |
|
62 | <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span> | |
63 | </div> |
|
63 | </div> | |
64 | <div class="author"> |
|
64 | <div class="author"> | |
65 | <div class="gravatar"> |
|
65 | <div class="gravatar"> | |
66 | <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/> |
|
66 | <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/> | |
67 | </div> |
|
67 | </div> | |
68 | <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div> |
|
68 | <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div> | |
69 | </div> |
|
69 | </div> | |
70 | <div class="date">${h.fmt_date(cs.date)}</div> |
|
70 | <div class="date">${h.fmt_date(cs.date)}</div> | |
71 | </div> |
|
71 | </div> | |
72 | <div class="mid"> |
|
72 | <div class="mid"> | |
73 | <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div> |
|
73 | <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div> | |
74 | <div class="expand"><span class="expandtext">↓ ${_('Show more')} ↓</span></div> |
|
74 | <div class="expand"><span class="expandtext">↓ ${_('Show more')} ↓</span></div> | |
75 | </div> |
|
75 | </div> | |
76 | <div class="right"> |
|
76 | <div class="right"> | |
77 | <div class="changes"> |
|
77 | <div class="changes"> | |
78 | <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div> |
|
78 | <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div> | |
79 | <div class="comments-container"> |
|
79 | <div class="comments-container"> | |
80 | %if len(c.comments.get(cs.raw_id,[])) > 0: |
|
80 | %if len(c.comments.get(cs.raw_id,[])) > 0: | |
81 | <div class="comments-cnt" title="${('comments')}"> |
|
81 | <div class="comments-cnt" title="${('comments')}"> | |
82 | <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}"> |
|
82 | <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}"> | |
83 | <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div> |
|
83 | <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div> | |
84 | <img src="${h.url('/images/icons/comments.png')}"> |
|
84 | <img src="${h.url('/images/icons/comments.png')}"> | |
85 | </a> |
|
85 | </a> | |
86 | </div> |
|
86 | </div> | |
87 | %endif |
|
87 | %endif | |
88 | </div> |
|
88 | </div> | |
89 | <div class="changeset-status-container"> |
|
89 | <div class="changeset-status-container"> | |
90 | %if c.statuses.get(cs.raw_id): |
|
90 | %if c.statuses.get(cs.raw_id): | |
91 | <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div> |
|
91 | <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div> | |
92 | <div class="changeset-status-ico"> |
|
92 | <div class="changeset-status-ico"> | |
93 | %if c.statuses.get(cs.raw_id)[2]: |
|
93 | %if c.statuses.get(cs.raw_id)[2]: | |
94 | <a class="tooltip" title="${_('Click to open associated pull request #%s' % c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a> |
|
94 | <a class="tooltip" title="${_('Click to open associated pull request #%s' % c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a> | |
95 | %else: |
|
95 | %else: | |
96 | <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /> |
|
96 | <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /> | |
97 | %endif |
|
97 | %endif | |
98 | </div> |
|
98 | </div> | |
99 | %endif |
|
99 | %endif | |
100 | </div> |
|
100 | </div> | |
101 | </div> |
|
101 | </div> | |
102 | %if cs.parents: |
|
102 | %if cs.parents: | |
103 | %for p_cs in reversed(cs.parents): |
|
103 | %for p_cs in reversed(cs.parents): | |
104 | <div class="parent">${_('Parent')} |
|
104 | <div class="parent">${_('Parent')} | |
105 | <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id), |
|
105 | <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id), | |
106 | h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span> |
|
106 | h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span> | |
107 | </div> |
|
107 | </div> | |
108 | %endfor |
|
108 | %endfor | |
109 | %else: |
|
109 | %else: | |
110 | <div class="parent">${_('No parents')}</div> |
|
110 | <div class="parent">${_('No parents')}</div> | |
111 | %endif |
|
111 | %endif | |
112 |
|
112 | |||
113 | <span class="logtags"> |
|
113 | <span class="logtags"> | |
114 | %if len(cs.parents)>1: |
|
114 | %if len(cs.parents)>1: | |
115 | <span class="merge">${_('merge')}</span> |
|
115 | <span class="merge">${_('merge')}</span> | |
116 | %endif |
|
116 | %endif | |
117 | %if cs.branch: |
|
117 | %if cs.branch: | |
118 | <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}"> |
|
118 | <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}"> | |
119 | ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))} |
|
119 | ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))} | |
120 | </span> |
|
120 | </span> | |
121 | %endif |
|
121 | %endif | |
122 | %if h.is_hg(c.rhodecode_repo): |
|
122 | %if h.is_hg(c.rhodecode_repo): | |
123 | %for book in cs.bookmarks: |
|
123 | %for book in cs.bookmarks: | |
124 | <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}"> |
|
124 | <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}"> | |
125 | ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))} |
|
125 | ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))} | |
126 | </span> |
|
126 | </span> | |
127 | %endfor |
|
127 | %endfor | |
128 | %endif |
|
128 | %endif | |
129 | %for tag in cs.tags: |
|
129 | %for tag in cs.tags: | |
130 | <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}"> |
|
130 | <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}"> | |
131 | ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span> |
|
131 | ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span> | |
132 | %endfor |
|
132 | %endfor | |
133 | </span> |
|
133 | </span> | |
134 | </div> |
|
134 | </div> | |
135 | </div> |
|
135 | </div> | |
136 |
|
136 | |||
137 | %endfor |
|
137 | %endfor | |
138 | <div class="pagination-wh pagination-left"> |
|
138 | <div class="pagination-wh pagination-left"> | |
139 | ${c.pagination.pager('$link_previous ~2~ $link_next')} |
|
139 | ${c.pagination.pager('$link_previous ~2~ $link_next')} | |
140 | </div> |
|
140 | </div> | |
141 | </div> |
|
141 | </div> | |
142 | </div> |
|
142 | </div> | |
143 |
|
143 | |||
144 | <script type="text/javascript" src="${h.url('/js/graph.js')}"></script> |
|
144 | <script type="text/javascript" src="${h.url('/js/graph.js')}"></script> | |
145 | <script type="text/javascript"> |
|
145 | <script type="text/javascript"> | |
146 | YAHOO.util.Event.onDOMReady(function(){ |
|
146 | YAHOO.util.Event.onDOMReady(function(){ | |
147 |
|
147 | |||
148 | //Monitor range checkboxes and build a link to changesets |
|
148 | //Monitor range checkboxes and build a link to changesets | |
149 | //ranges |
|
149 | //ranges | |
150 | var checkboxes = YUD.getElementsByClassName('changeset_range'); |
|
150 | var checkboxes = YUD.getElementsByClassName('changeset_range'); | |
151 | var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}"; |
|
151 | var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}"; | |
152 | var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}"; |
|
152 | var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}"; | |
153 |
|
153 | |||
154 | var checkbox_checker = function(e){ |
|
154 | var checkbox_checker = function(e){ | |
155 | var clicked_cb = e.currentTarget; |
|
155 | var clicked_cb = e.currentTarget; | |
156 | var checked_checkboxes = []; |
|
156 | var checked_checkboxes = []; | |
157 | for (pos in checkboxes){ |
|
157 | for (pos in checkboxes){ | |
158 | if(checkboxes[pos].checked){ |
|
158 | if(checkboxes[pos].checked){ | |
159 | checked_checkboxes.push(checkboxes[pos]); |
|
159 | checked_checkboxes.push(checkboxes[pos]); | |
160 | } |
|
160 | } | |
161 | } |
|
161 | } | |
162 | if(YUD.get('open_new_pr')){ |
|
162 | if(YUD.get('open_new_pr')){ | |
163 | if(checked_checkboxes.length>1){ |
|
163 | if(checked_checkboxes.length>1){ | |
164 | YUD.setStyle('open_new_pr','display','none'); |
|
164 | YUD.setStyle('open_new_pr','display','none'); | |
165 | } else { |
|
165 | } else { | |
166 | YUD.setStyle('open_new_pr','display',''); |
|
166 | YUD.setStyle('open_new_pr','display',''); | |
167 | if(checked_checkboxes.length>0){ |
|
167 | if(checked_checkboxes.length>0){ | |
168 | YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets']; |
|
168 | YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets']; | |
169 | }else{ |
|
169 | }else{ | |
170 | YUD.get('open_new_pr').innerHTML = _TM['Open new pull request']; |
|
170 | YUD.get('open_new_pr').innerHTML = _TM['Open new pull request']; | |
171 | } |
|
171 | } | |
172 | } |
|
172 | } | |
173 | } |
|
173 | } | |
174 |
|
174 | |||
175 | if(checked_checkboxes.length>0){ |
|
175 | if(checked_checkboxes.length>0){ | |
176 | var rev_end = checked_checkboxes[0].name; |
|
176 | var rev_end = checked_checkboxes[0].name; | |
177 | var rev_start = checked_checkboxes[checked_checkboxes.length-1].name; |
|
177 | var rev_start = checked_checkboxes[checked_checkboxes.length-1].name; | |
178 | var url = url_tmpl.replace('__REVRANGE__', |
|
178 | var url = url_tmpl.replace('__REVRANGE__', | |
179 | rev_start+'...'+rev_end); |
|
179 | rev_start+'...'+rev_end); | |
180 |
|
180 | |||
181 | var link = (rev_start == rev_end) |
|
181 | var link = (rev_start == rev_end) | |
182 | ? _TM['Show selected change __S'] |
|
182 | ? _TM['Show selected change __S'] | |
183 | : _TM['Show selected changes __S -> __E']; |
|
183 | : _TM['Show selected changes __S -> __E']; | |
184 |
|
184 | |||
185 | link = link.replace('__S',rev_start.substr(0,6)); |
|
185 | link = link.replace('__S',rev_start.substr(0,6)); | |
186 | link = link.replace('__E',rev_end.substr(0,6)); |
|
186 | link = link.replace('__E',rev_end.substr(0,6)); | |
187 | YUD.get('rev_range_container').href = url; |
|
187 | YUD.get('rev_range_container').href = url; | |
188 | YUD.get('rev_range_container').innerHTML = link; |
|
188 | YUD.get('rev_range_container').innerHTML = link; | |
189 | YUD.setStyle('rev_range_container','display',''); |
|
189 | YUD.setStyle('rev_range_container','display',''); | |
190 | YUD.setStyle('rev_range_clear','display',''); |
|
190 | YUD.setStyle('rev_range_clear','display',''); | |
191 |
|
191 | |||
192 | YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end); |
|
192 | YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end); | |
193 | YUD.setStyle('compare_fork','display','none'); |
|
193 | YUD.setStyle('compare_fork','display','none'); | |
194 | } |
|
194 | } | |
195 | else{ |
|
195 | else{ | |
196 | YUD.setStyle('rev_range_container','display','none'); |
|
196 | YUD.setStyle('rev_range_container','display','none'); | |
197 | YUD.setStyle('rev_range_clear','display','none'); |
|
197 | YUD.setStyle('rev_range_clear','display','none'); | |
198 | YUD.get('open_new_pr').href = pr_tmpl |
|
198 | YUD.get('open_new_pr').href = pr_tmpl | |
199 | YUD.setStyle('compare_fork','display',''); |
|
199 | YUD.setStyle('compare_fork','display',''); | |
200 | } |
|
200 | } | |
201 | }; |
|
201 | }; | |
202 | YUE.onDOMReady(checkbox_checker); |
|
202 | YUE.onDOMReady(checkbox_checker); | |
203 | YUE.on(checkboxes,'click', checkbox_checker); |
|
203 | YUE.on(checkboxes,'click', checkbox_checker); | |
204 |
|
204 | |||
205 | YUE.on('rev_range_clear','click',function(e){ |
|
205 | YUE.on('rev_range_clear','click',function(e){ | |
206 | for (var i=0; i<checkboxes.length; i++){ |
|
206 | for (var i=0; i<checkboxes.length; i++){ | |
207 | var cb = checkboxes[i]; |
|
207 | var cb = checkboxes[i]; | |
208 | cb.checked = false; |
|
208 | cb.checked = false; | |
209 | } |
|
209 | } | |
210 | YUE.preventDefault(e); |
|
210 | YUE.preventDefault(e); | |
211 | }) |
|
211 | }) | |
212 | var msgs = YUQ('.message'); |
|
212 | var msgs = YUQ('.message'); | |
213 | // get first element height |
|
213 | // get first element height | |
214 | var el = YUQ('#graph_content .container')[0]; |
|
214 | var el = YUQ('#graph_content .container')[0]; | |
215 | var row_h = el.clientHeight; |
|
215 | var row_h = el.clientHeight; | |
216 | for(var i=0;i<msgs.length;i++){ |
|
216 | for(var i=0;i<msgs.length;i++){ | |
217 | var m = msgs[i]; |
|
217 | var m = msgs[i]; | |
218 |
|
218 | |||
219 | var h = m.clientHeight; |
|
219 | var h = m.clientHeight; | |
220 | var pad = YUD.getStyle(m,'padding'); |
|
220 | var pad = YUD.getStyle(m,'padding'); | |
221 | if(h > row_h){ |
|
221 | if(h > row_h){ | |
222 | var offset = row_h - (h+12); |
|
222 | var offset = row_h - (h+12); | |
223 | YUD.setStyle(m.nextElementSibling,'display','block'); |
|
223 | YUD.setStyle(m.nextElementSibling,'display','block'); | |
224 | YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px'); |
|
224 | YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px'); | |
225 | }; |
|
225 | }; | |
226 | } |
|
226 | } | |
227 | YUE.on(YUQ('.expand'),'click',function(e){ |
|
227 | YUE.on(YUQ('.expand'),'click',function(e){ | |
228 | var elem = e.currentTarget.parentNode.parentNode; |
|
228 | var elem = e.currentTarget.parentNode.parentNode; | |
229 | YUD.setStyle(e.currentTarget,'display','none'); |
|
229 | YUD.setStyle(e.currentTarget,'display','none'); | |
230 | YUD.setStyle(elem,'height','auto'); |
|
230 | YUD.setStyle(elem,'height','auto'); | |
231 |
|
231 | |||
232 | //redraw the graph, line_count and jsdata are global vars |
|
232 | //redraw the graph, line_count and jsdata are global vars | |
233 | set_canvas(100); |
|
233 | set_canvas(100); | |
234 |
|
234 | |||
235 | var r = new BranchRenderer(); |
|
235 | var r = new BranchRenderer(); | |
236 | r.render(jsdata,100,line_count); |
|
236 | r.render(jsdata,100,line_count); | |
237 |
|
237 | |||
238 | }) |
|
238 | }) | |
239 |
|
239 | |||
240 | // Fetch changeset details |
|
240 | // Fetch changeset details | |
241 | YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){ |
|
241 | YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){ | |
242 | var id = e.currentTarget.id; |
|
242 | var id = e.currentTarget.id; | |
243 | var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"; |
|
243 | var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"; | |
244 | var url = url.replace('__CS__',id.replace('changed_total_','')); |
|
244 | var url = url.replace('__CS__',id.replace('changed_total_','')); | |
245 | ypjax(url,id,function(){tooltip_activate()}); |
|
245 | ypjax(url,id,function(){tooltip_activate()}); | |
246 | }); |
|
246 | }); | |
247 |
|
247 | |||
248 | // change branch filter |
|
248 | // change branch filter | |
249 | YUE.on(YUD.get('branch_filter'),'change',function(e){ |
|
249 | YUE.on(YUD.get('branch_filter'),'change',function(e){ | |
250 | var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value; |
|
250 | var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value; | |
251 | var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}"; |
|
251 | var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}"; | |
252 | var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}"; |
|
252 | var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}"; | |
253 | var url = url.replace('__BRANCH__',selected_branch); |
|
253 | var url = url.replace('__BRANCH__',selected_branch); | |
254 | if(selected_branch != ''){ |
|
254 | if(selected_branch != ''){ | |
255 | window.location = url; |
|
255 | window.location = url; | |
256 | }else{ |
|
256 | }else{ | |
257 | window.location = url_main; |
|
257 | window.location = url_main; | |
258 | } |
|
258 | } | |
259 |
|
259 | |||
260 | }); |
|
260 | }); | |
261 |
|
261 | |||
262 | function set_canvas(width) { |
|
262 | function set_canvas(width) { | |
263 | var c = document.getElementById('graph_nodes'); |
|
263 | var c = document.getElementById('graph_nodes'); | |
264 | var t = document.getElementById('graph_content'); |
|
264 | var t = document.getElementById('graph_content'); | |
265 | canvas = document.getElementById('graph_canvas'); |
|
265 | canvas = document.getElementById('graph_canvas'); | |
266 | var div_h = t.clientHeight; |
|
266 | var div_h = t.clientHeight; | |
267 | c.style.height=div_h+'px'; |
|
267 | c.style.height=div_h+'px'; | |
268 | canvas.setAttribute('height',div_h); |
|
268 | canvas.setAttribute('height',div_h); | |
269 | c.style.height=width+'px'; |
|
269 | c.style.height=width+'px'; | |
270 | canvas.setAttribute('width',width); |
|
270 | canvas.setAttribute('width',width); | |
271 | }; |
|
271 | }; | |
272 | var heads = 1; |
|
272 | var heads = 1; | |
273 | var line_count = 0; |
|
273 | var line_count = 0; | |
274 | var jsdata = ${c.jsdata|n}; |
|
274 | var jsdata = ${c.jsdata|n}; | |
275 |
|
275 | |||
276 | for (var i=0;i<jsdata.length;i++) { |
|
276 | for (var i=0;i<jsdata.length;i++) { | |
277 | var in_l = jsdata[i][2]; |
|
277 | var in_l = jsdata[i][2]; | |
278 | for (var j in in_l) { |
|
278 | for (var j in in_l) { | |
279 | var m = in_l[j][1]; |
|
279 | var m = in_l[j][1]; | |
280 | if (m > line_count) |
|
280 | if (m > line_count) | |
281 | line_count = m; |
|
281 | line_count = m; | |
282 | } |
|
282 | } | |
283 | } |
|
283 | } | |
284 | set_canvas(100); |
|
284 | set_canvas(100); | |
285 |
|
285 | |||
286 | var r = new BranchRenderer(); |
|
286 | var r = new BranchRenderer(); | |
287 | r.render(jsdata,100,line_count); |
|
287 | r.render(jsdata,100,line_count); | |
288 |
|
288 | |||
289 | }); |
|
289 | }); | |
290 | </script> |
|
290 | </script> | |
291 | %else: |
|
291 | %else: | |
292 | ${_('There are no changes yet')} |
|
292 | ${_('There are no changes yet')} | |
293 | %endif |
|
293 | %endif | |
294 | </div> |
|
294 | </div> | |
295 | </div> |
|
295 | </div> | |
296 | </%def> |
|
296 | </%def> |
@@ -1,28 +1,36 b'' | |||||
1 | ## Changesets table ! |
|
1 | ## Changesets table ! | |
2 | <div class="container"> |
|
2 | <div class="container"> | |
3 | <table class="compare_view_commits noborder"> |
|
|||
4 | %if not c.cs_ranges: |
|
3 | %if not c.cs_ranges: | |
5 | <span class="empty_data">${_('No changesets')}</span> |
|
4 | <span class="empty_data">${_('No changesets')}</span> | |
6 | %else: |
|
5 | %else: | |
|
6 | <table class="compare_view_commits noborder"> | |||
7 | %for cnt, cs in enumerate(c.cs_ranges): |
|
7 | %for cnt, cs in enumerate(c.cs_ranges): | |
8 | <tr> |
|
8 | <tr> | |
9 | <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td> |
|
9 | <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td> | |
10 | <td> |
|
10 | <td> | |
11 | %if cs.raw_id in c.statuses: |
|
11 | %if cs.raw_id in c.statuses: | |
12 | <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div> |
|
12 | <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div> | |
13 | %endif |
|
13 | %endif | |
14 | </td> |
|
14 | </td> | |
15 | <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.other_repo.repo_name,revision=cs.raw_id))} |
|
15 | <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.other_repo.repo_name,revision=cs.raw_id))} | |
16 | %if c.as_form: |
|
16 | %if c.as_form: | |
17 | ${h.hidden('revisions',cs.raw_id)} |
|
17 | ${h.hidden('revisions',cs.raw_id)} | |
18 | %endif |
|
18 | %endif | |
19 | </td> |
|
19 | </td> | |
20 | <td><div class="author">${h.person(cs.author)}</div></td> |
|
20 | <td><div class="author">${h.person(cs.author)}</div></td> | |
21 | <td><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td> |
|
21 | <td><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td> | |
22 | <td><div class="message tooltip" title="${h.tooltip(cs.message)}" style="white-space:normal">${h.urlify_commit(h.shorter(cs.message, 60),c.repo_name)}</div></td> |
|
22 | <td><div class="message tooltip" title="${h.tooltip(cs.message)}" style="white-space:normal">${h.urlify_commit(h.shorter(cs.message, 60),c.repo_name)}</div></td> | |
23 | </tr> |
|
23 | </tr> | |
24 | %endfor |
|
24 | %endfor | |
25 |
|
25 | </table> | ||
|
26 | %if c.ancestor: | |||
|
27 | <span class="ancestor">${_('Ancestor')}: | |||
|
28 | ${h.link_to(c.ancestor,h.url('changeset_home',repo_name=c.repo_name,revision=c.ancestor))} | |||
|
29 | </span> | |||
|
30 | %endif | |||
|
31 | %if c.as_form: | |||
|
32 | ${h.hidden('ancestor_rev',c.ancestor)} | |||
|
33 | ${h.hidden('merge_rev',c.cs_ranges[-1].raw_id)} | |||
|
34 | %endif | |||
26 | %endif |
|
35 | %endif | |
27 | </table> |
|
|||
28 | </div> |
|
36 | </div> |
@@ -1,198 +1,199 b'' | |||||
1 | <%inherit file="/base/base.html"/> |
|
1 | <%inherit file="/base/base.html"/> | |
2 |
|
2 | |||
3 | <%def name="title()"> |
|
3 | <%def name="title()"> | |
4 | ${c.repo_name} ${_('New pull request')} |
|
4 | ${c.repo_name} ${_('New pull request')} | |
5 | </%def> |
|
5 | </%def> | |
6 |
|
6 | |||
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${h.link_to(_(u'Home'),h.url('/'))} |
|
8 | ${h.link_to(_(u'Home'),h.url('/'))} | |
9 | » |
|
9 | » | |
10 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} |
|
10 | ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)} | |
11 | » |
|
11 | » | |
12 | ${_('new pull request')} |
|
12 | ${_('new pull request')} | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="main()"> |
|
15 | <%def name="main()"> | |
16 |
|
16 | |||
17 | <div class="box"> |
|
17 | <div class="box"> | |
18 | <!-- box / title --> |
|
18 | <!-- box / title --> | |
19 | <div class="title"> |
|
19 | <div class="title"> | |
20 | ${self.breadcrumbs()} |
|
20 | ${self.breadcrumbs()} | |
21 | </div> |
|
21 | </div> | |
22 | ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')} |
|
22 | ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')} | |
23 | <div style="float:left;padding:0px 30px 30px 30px"> |
|
23 | <div style="float:left;padding:0px 30px 30px 30px"> | |
24 | ##ORG |
|
24 | ##ORG | |
25 | <div style="float:left"> |
|
25 | <div style="float:left"> | |
26 | <div> |
|
26 | <div> | |
27 | <span style="font-size: 20px"> |
|
27 | <span style="font-size: 20px"> | |
28 | ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref',c.default_org_ref,c.org_refs,class_='refs')} |
|
28 | ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref',c.default_org_ref,c.org_refs,class_='refs')} | |
29 | </span> |
|
29 | </span> | |
30 | <div style="padding:5px 3px 3px 20px;">${c.rhodecode_db_repo.description}</div> |
|
30 | <div style="padding:5px 3px 3px 20px;">${c.rhodecode_db_repo.description}</div> | |
31 | </div> |
|
31 | </div> | |
32 | <div style="clear:both;padding-top: 10px"></div> |
|
32 | <div style="clear:both;padding-top: 10px"></div> | |
33 | </div> |
|
33 | </div> | |
34 | <div style="float:left;font-size:24px;padding:0px 20px"> |
|
34 | <div style="float:left;font-size:24px;padding:0px 20px"> | |
35 | <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/> |
|
35 | <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/> | |
36 | </div> |
|
36 | </div> | |
37 |
|
37 | |||
38 | ##OTHER, most Probably the PARENT OF THIS FORK |
|
38 | ##OTHER, most Probably the PARENT OF THIS FORK | |
39 | <div style="float:left"> |
|
39 | <div style="float:left"> | |
40 | <div> |
|
40 | <div> | |
41 | <span style="font-size: 20px"> |
|
41 | <span style="font-size: 20px"> | |
42 | ${h.select('other_repo',c.default_other_repo,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_other_ref,c.default_other_refs,class_='refs')} |
|
42 | ${h.select('other_repo',c.default_other_repo,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_other_ref,c.default_other_refs,class_='refs')} | |
43 | </span> |
|
43 | </span> | |
44 | <div id="other_repo_desc" style="padding:5px 3px 3px 20px;"></div> |
|
44 | <div id="other_repo_desc" style="padding:5px 3px 3px 20px;"></div> | |
45 | </div> |
|
45 | </div> | |
46 | <div style="clear:both;padding-top: 10px"></div> |
|
46 | <div style="clear:both;padding-top: 10px"></div> | |
47 | </div> |
|
47 | </div> | |
48 | <div style="clear:both;padding-top: 10px"></div> |
|
48 | <div style="clear:both;padding-top: 10px"></div> | |
49 | ## overview pulled by ajax |
|
49 | ## overview pulled by ajax | |
50 | <div style="float:left" id="pull_request_overview"></div> |
|
50 | <div style="float:left" id="pull_request_overview"></div> | |
51 | <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none"> |
|
51 | <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none"> | |
52 | <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a> |
|
52 | <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a> | |
53 | </div> |
|
53 | </div> | |
54 | </div> |
|
54 | </div> | |
55 | <div style="float:left; border-left:1px dashed #eee"> |
|
55 | <div style="float:left; border-left:1px dashed #eee"> | |
56 | <h4>${_('Pull request reviewers')}</h4> |
|
56 | <h4>${_('Pull request reviewers')}</h4> | |
57 | <div id="reviewers" style="padding:0px 0px 0px 15px"> |
|
57 | <div id="reviewers" style="padding:0px 0px 0px 15px"> | |
58 | ## members goes here ! |
|
58 | ## members goes here ! | |
59 | <div class="group_members_wrap"> |
|
59 | <div class="group_members_wrap"> | |
60 | <ul id="review_members" class="group_members"> |
|
60 | <ul id="review_members" class="group_members"> | |
61 | %for member in c.review_members: |
|
61 | %for member in c.review_members: | |
62 | <li id="reviewer_${member.user_id}"> |
|
62 | <li id="reviewer_${member.user_id}"> | |
63 | <div class="reviewers_member"> |
|
63 | <div class="reviewers_member"> | |
64 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div> |
|
64 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div> | |
65 | <div style="float:left">${member.full_name} (${_('owner')})</div> |
|
65 | <div style="float:left">${member.full_name} (${_('owner')})</div> | |
66 | <input type="hidden" value="${member.user_id}" name="review_members" /> |
|
66 | <input type="hidden" value="${member.user_id}" name="review_members" /> | |
67 | <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span> |
|
67 | <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span> | |
68 | </div> |
|
68 | </div> | |
69 | </li> |
|
69 | </li> | |
70 | %endfor |
|
70 | %endfor | |
71 | </ul> |
|
71 | </ul> | |
72 | </div> |
|
72 | </div> | |
73 |
|
73 | |||
74 | <div class='ac'> |
|
74 | <div class='ac'> | |
75 | <div class="reviewer_ac"> |
|
75 | <div class="reviewer_ac"> | |
76 | ${h.text('user', class_='yui-ac-input')} |
|
76 | ${h.text('user', class_='yui-ac-input')} | |
77 | <span class="help-block">${_('Add reviewer to this pull request.')}</span> |
|
77 | <span class="help-block">${_('Add reviewer to this pull request.')}</span> | |
78 | <div id="reviewers_container"></div> |
|
78 | <div id="reviewers_container"></div> | |
79 | </div> |
|
79 | </div> | |
80 | </div> |
|
80 | </div> | |
81 | </div> |
|
81 | </div> | |
82 | </div> |
|
82 | </div> | |
83 | <h3>${_('Create new pull request')}</h3> |
|
83 | <h3>${_('Create new pull request')}</h3> | |
84 |
|
84 | |||
85 | <div class="form"> |
|
85 | <div class="form"> | |
86 | <!-- fields --> |
|
86 | <!-- fields --> | |
87 |
|
87 | |||
88 | <div class="fields"> |
|
88 | <div class="fields"> | |
89 |
|
89 | |||
90 | <div class="field"> |
|
90 | <div class="field"> | |
91 | <div class="label"> |
|
91 | <div class="label"> | |
92 | <label for="pullrequest_title">${_('Title')}:</label> |
|
92 | <label for="pullrequest_title">${_('Title')}:</label> | |
93 | </div> |
|
93 | </div> | |
94 | <div class="input"> |
|
94 | <div class="input"> | |
95 | ${h.text('pullrequest_title',size=30)} |
|
95 | ${h.text('pullrequest_title',size=30)} | |
96 | </div> |
|
96 | </div> | |
97 | </div> |
|
97 | </div> | |
98 |
|
98 | |||
99 | <div class="field"> |
|
99 | <div class="field"> | |
100 | <div class="label label-textarea"> |
|
100 | <div class="label label-textarea"> | |
101 | <label for="pullrequest_desc">${_('description')}:</label> |
|
101 | <label for="pullrequest_desc">${_('description')}:</label> | |
102 | </div> |
|
102 | </div> | |
103 | <div class="textarea text-area editor"> |
|
103 | <div class="textarea text-area editor"> | |
104 | ${h.textarea('pullrequest_desc',size=30)} |
|
104 | ${h.textarea('pullrequest_desc',size=30)} | |
105 | </div> |
|
105 | </div> | |
106 | </div> |
|
106 | </div> | |
107 |
|
107 | |||
108 | <div class="buttons"> |
|
108 | <div class="buttons"> | |
109 | ${h.submit('save',_('Send pull request'),class_="ui-btn large")} |
|
109 | ${h.submit('save',_('Send pull request'),class_="ui-btn large")} | |
110 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} |
|
110 | ${h.reset('reset',_('Reset'),class_="ui-btn large")} | |
111 | </div> |
|
111 | </div> | |
112 | </div> |
|
112 | </div> | |
113 | </div> |
|
113 | </div> | |
114 | ${h.end_form()} |
|
114 | ${h.end_form()} | |
115 |
|
115 | |||
116 | </div> |
|
116 | </div> | |
117 |
|
117 | |||
118 | <script type="text/javascript"> |
|
118 | <script type="text/javascript"> | |
119 | var _USERS_AC_DATA = ${c.users_array|n}; |
|
119 | var _USERS_AC_DATA = ${c.users_array|n}; | |
120 | var _GROUPS_AC_DATA = ${c.users_groups_array|n}; |
|
120 | var _GROUPS_AC_DATA = ${c.users_groups_array|n}; | |
121 | PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA); |
|
121 | PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA); | |
122 |
|
122 | |||
123 | var other_repos_info = ${c.other_repos_info|n}; |
|
123 | var other_repos_info = ${c.other_repos_info|n}; | |
124 |
|
124 | |||
125 | var loadPreview = function(){ |
|
125 | var loadPreview = function(){ | |
126 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none'); |
|
126 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none'); | |
127 | //url template |
|
127 | //url template | |
128 | var url = "${h.url('compare_url', |
|
128 | var url = "${h.url('compare_url', | |
129 | repo_name='__other_repo__', |
|
129 | repo_name='__other_repo__', | |
130 | org_ref_type='__other_ref_type__', |
|
130 | org_ref_type='__other_ref_type__', | |
131 | org_ref='__other_ref__', |
|
131 | org_ref='__other_ref__', | |
132 | other_repo='__org_repo__', |
|
132 | other_repo='__org_repo__', | |
133 | other_ref_type='__org_ref_type__', |
|
133 | other_ref_type='__org_ref_type__', | |
134 | other_ref='__org_ref__', |
|
134 | other_ref='__org_ref__', | |
135 | as_form=True, |
|
135 | as_form=True, | |
|
136 | merge=True, | |||
136 | )}"; |
|
137 | )}"; | |
137 | var org_repo = YUQ('#pull_request_form #org_repo')[0].value; |
|
138 | var org_repo = YUQ('#pull_request_form #org_repo')[0].value; | |
138 | var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':'); |
|
139 | var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':'); | |
139 |
|
140 | |||
140 | var other_repo = YUQ('#pull_request_form #other_repo')[0].value; |
|
141 | var other_repo = YUQ('#pull_request_form #other_repo')[0].value; | |
141 | var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':'); |
|
142 | var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':'); | |
142 |
|
143 | |||
143 | var select_refs = YUQ('#pull_request_form select.refs') |
|
144 | var select_refs = YUQ('#pull_request_form select.refs') | |
144 | var rev_data = { |
|
145 | var rev_data = { | |
145 | 'org_repo': org_repo, |
|
146 | 'org_repo': org_repo, | |
146 | 'org_ref': org_ref[2], |
|
147 | 'org_ref': org_ref[2], | |
147 | 'org_ref_type': 'rev', |
|
148 | 'org_ref_type': 'rev', | |
148 | 'other_repo': other_repo, |
|
149 | 'other_repo': other_repo, | |
149 | 'other_ref': other_ref[2], |
|
150 | 'other_ref': other_ref[2], | |
150 | 'other_ref_type': 'rev', |
|
151 | 'other_ref_type': 'rev', | |
151 | }; // gather the org/other ref and repo here |
|
152 | }; // gather the org/other ref and repo here | |
152 |
|
153 | |||
153 | for (k in rev_data){ |
|
154 | for (k in rev_data){ | |
154 | url = url.replace('__'+k+'__',rev_data[k]); |
|
155 | url = url.replace('__'+k+'__',rev_data[k]); | |
155 | } |
|
156 | } | |
156 |
|
157 | |||
157 | YUD.get('pull_request_overview').innerHTML = "${_('Loading ...')}"; |
|
158 | YUD.get('pull_request_overview').innerHTML = "${_('Loading ...')}"; | |
158 | YUD.get('pull_request_overview_url').href = url; // shouldn't have as_form ... but ... |
|
159 | YUD.get('pull_request_overview_url').href = url; // shouldn't have as_form ... but ... | |
159 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display',''); |
|
160 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display',''); | |
160 | ypjax(url,'pull_request_overview', function(data){ |
|
161 | ypjax(url,'pull_request_overview', function(data){ | |
161 | var sel_box = YUQ('#pull_request_form #other_repo')[0]; |
|
162 | var sel_box = YUQ('#pull_request_form #other_repo')[0]; | |
162 | var repo_name = sel_box.options[sel_box.selectedIndex].value; |
|
163 | var repo_name = sel_box.options[sel_box.selectedIndex].value; | |
163 | var _data = other_repos_info[repo_name]; |
|
164 | var _data = other_repos_info[repo_name]; | |
164 | YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description']; |
|
165 | YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description']; | |
165 | YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs']; |
|
166 | YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs']; | |
166 | // select back the revision that was just compared |
|
167 | // select back the revision that was just compared | |
167 | setSelectValue(YUD.get('other_ref'), rev_data['other_ref']); |
|
168 | setSelectValue(YUD.get('other_ref'), rev_data['other_ref']); | |
168 | // reset && add the reviewer based on selected repo |
|
169 | // reset && add the reviewer based on selected repo | |
169 | YUD.get('review_members').innerHTML = ''; |
|
170 | YUD.get('review_members').innerHTML = ''; | |
170 | addReviewMember(_data.user.user_id, _data.user.firstname, |
|
171 | addReviewMember(_data.user.user_id, _data.user.firstname, | |
171 | _data.user.lastname, _data.user.username, |
|
172 | _data.user.lastname, _data.user.username, | |
172 | _data.user.gravatar_link); |
|
173 | _data.user.gravatar_link); | |
173 | }) |
|
174 | }) | |
174 | } |
|
175 | } | |
175 |
|
176 | |||
176 | ## refresh automatically when something changes (org_repo can't change) |
|
177 | ## refresh automatically when something changes (org_repo can't change) | |
177 |
|
178 | |||
178 | YUE.on('org_ref', 'change', function(e){ |
|
179 | YUE.on('org_ref', 'change', function(e){ | |
179 | loadPreview(); |
|
180 | loadPreview(); | |
180 | }); |
|
181 | }); | |
181 |
|
182 | |||
182 | YUE.on('other_repo', 'change', function(e){ |
|
183 | YUE.on('other_repo', 'change', function(e){ | |
183 | var repo_name = e.currentTarget.value; |
|
184 | var repo_name = e.currentTarget.value; | |
184 | // replace the <select> of changed repo |
|
185 | // replace the <select> of changed repo | |
185 | YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs']; |
|
186 | YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs']; | |
186 | loadPreview(); |
|
187 | loadPreview(); | |
187 | }); |
|
188 | }); | |
188 |
|
189 | |||
189 | YUE.on('other_ref', 'change', function(e){ |
|
190 | YUE.on('other_ref', 'change', function(e){ | |
190 | loadPreview(); |
|
191 | loadPreview(); | |
191 | }); |
|
192 | }); | |
192 |
|
193 | |||
193 | //lazy load overview after 0.5s |
|
194 | //lazy load overview after 0.5s | |
194 | setTimeout(loadPreview, 500) |
|
195 | setTimeout(loadPreview, 500) | |
195 |
|
196 | |||
196 | </script> |
|
197 | </script> | |
197 |
|
198 | |||
198 | </%def> |
|
199 | </%def> |
@@ -1,434 +1,441 b'' | |||||
1 | from rhodecode.tests import * |
|
1 | from rhodecode.tests import * | |
2 | from rhodecode.model.repo import RepoModel |
|
2 | from rhodecode.model.repo import RepoModel | |
3 | from rhodecode.model.meta import Session |
|
3 | from rhodecode.model.meta import Session | |
4 | from rhodecode.model.db import Repository |
|
4 | from rhodecode.model.db import Repository | |
5 | from rhodecode.model.scm import ScmModel |
|
5 | from rhodecode.model.scm import ScmModel | |
6 | from rhodecode.lib.vcs.backends.base import EmptyChangeset |
|
6 | from rhodecode.lib.vcs.backends.base import EmptyChangeset | |
7 |
|
7 | |||
8 |
|
8 | |||
9 | def _fork_repo(fork_name, vcs_type, parent=None): |
|
9 | def _fork_repo(fork_name, vcs_type, parent=None): | |
10 | if vcs_type =='hg': |
|
10 | if vcs_type =='hg': | |
11 | _REPO = HG_REPO |
|
11 | _REPO = HG_REPO | |
12 | elif vcs_type == 'git': |
|
12 | elif vcs_type == 'git': | |
13 | _REPO = GIT_REPO |
|
13 | _REPO = GIT_REPO | |
14 |
|
14 | |||
15 | if parent: |
|
15 | if parent: | |
16 | _REPO = parent |
|
16 | _REPO = parent | |
17 |
|
17 | |||
18 | form_data = dict( |
|
18 | form_data = dict( | |
19 | repo_name=fork_name, |
|
19 | repo_name=fork_name, | |
20 | repo_name_full=fork_name, |
|
20 | repo_name_full=fork_name, | |
21 | repo_group=None, |
|
21 | repo_group=None, | |
22 | repo_type=vcs_type, |
|
22 | repo_type=vcs_type, | |
23 | description='', |
|
23 | description='', | |
24 | private=False, |
|
24 | private=False, | |
25 | copy_permissions=False, |
|
25 | copy_permissions=False, | |
26 | landing_rev='tip', |
|
26 | landing_rev='tip', | |
27 | update_after_clone=False, |
|
27 | update_after_clone=False, | |
28 | fork_parent_id=Repository.get_by_repo_name(_REPO), |
|
28 | fork_parent_id=Repository.get_by_repo_name(_REPO), | |
29 | ) |
|
29 | ) | |
30 | RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN) |
|
30 | RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN) | |
31 |
|
31 | |||
32 | Session().commit() |
|
32 | Session().commit() | |
33 | return Repository.get_by_repo_name(fork_name) |
|
33 | return Repository.get_by_repo_name(fork_name) | |
34 |
|
34 | |||
35 |
|
35 | |||
36 | def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False): |
|
36 | def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False): | |
37 | repo = Repository.get_by_repo_name(repo) |
|
37 | repo = Repository.get_by_repo_name(repo) | |
38 | _cs = parent |
|
38 | _cs = parent | |
39 | if not parent: |
|
39 | if not parent: | |
40 | _cs = EmptyChangeset(alias=vcs_type) |
|
40 | _cs = EmptyChangeset(alias=vcs_type) | |
41 |
|
41 | |||
42 | if newfile: |
|
42 | if newfile: | |
43 | cs = ScmModel().create_node( |
|
43 | cs = ScmModel().create_node( | |
44 | repo=repo.scm_instance, repo_name=repo.repo_name, |
|
44 | repo=repo.scm_instance, repo_name=repo.repo_name, | |
45 | cs=_cs, user=TEST_USER_ADMIN_LOGIN, |
|
45 | cs=_cs, user=TEST_USER_ADMIN_LOGIN, | |
46 | author=TEST_USER_ADMIN_LOGIN, |
|
46 | author=TEST_USER_ADMIN_LOGIN, | |
47 | message=message, |
|
47 | message=message, | |
48 | content=content, |
|
48 | content=content, | |
49 | f_path=filename |
|
49 | f_path=filename | |
50 | ) |
|
50 | ) | |
51 | else: |
|
51 | else: | |
52 | cs = ScmModel().commit_change( |
|
52 | cs = ScmModel().commit_change( | |
53 | repo=repo.scm_instance, repo_name=repo.repo_name, |
|
53 | repo=repo.scm_instance, repo_name=repo.repo_name, | |
54 | cs=parent, user=TEST_USER_ADMIN_LOGIN, |
|
54 | cs=parent, user=TEST_USER_ADMIN_LOGIN, | |
55 | author=TEST_USER_ADMIN_LOGIN, |
|
55 | author=TEST_USER_ADMIN_LOGIN, | |
56 | message=message, |
|
56 | message=message, | |
57 | content=content, |
|
57 | content=content, | |
58 | f_path=filename |
|
58 | f_path=filename | |
59 | ) |
|
59 | ) | |
60 | return cs |
|
60 | return cs | |
61 |
|
61 | |||
62 |
|
62 | |||
63 | class TestCompareController(TestController): |
|
63 | class TestCompareController(TestController): | |
64 |
|
64 | |||
65 | def setUp(self): |
|
65 | def setUp(self): | |
66 | self.r1_id = None |
|
66 | self.r1_id = None | |
67 | self.r2_id = None |
|
67 | self.r2_id = None | |
68 |
|
68 | |||
69 | def tearDown(self): |
|
69 | def tearDown(self): | |
70 | if self.r2_id: |
|
70 | if self.r2_id: | |
71 | RepoModel().delete(self.r2_id) |
|
71 | RepoModel().delete(self.r2_id) | |
72 | if self.r1_id: |
|
72 | if self.r1_id: | |
73 | RepoModel().delete(self.r1_id) |
|
73 | RepoModel().delete(self.r1_id) | |
74 | Session().commit() |
|
74 | Session().commit() | |
75 | Session.remove() |
|
75 | Session.remove() | |
76 |
|
76 | |||
77 | def test_compare_forks_on_branch_extra_commits_hg(self): |
|
77 | def test_compare_forks_on_branch_extra_commits_hg(self): | |
78 | self.log_user() |
|
78 | self.log_user() | |
79 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', |
|
79 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', | |
80 | description='diff-test', |
|
80 | description='diff-test', | |
81 | owner=TEST_USER_ADMIN_LOGIN) |
|
81 | owner=TEST_USER_ADMIN_LOGIN) | |
82 | Session().commit() |
|
82 | Session().commit() | |
83 | self.r1_id = repo1.repo_id |
|
83 | self.r1_id = repo1.repo_id | |
84 | #commit something ! |
|
84 | #commit something ! | |
85 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', |
|
85 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', | |
86 | message='commit1', vcs_type='hg', parent=None, newfile=True) |
|
86 | message='commit1', vcs_type='hg', parent=None, newfile=True) | |
87 |
|
87 | |||
88 | #fork this repo |
|
88 | #fork this repo | |
89 | repo2 = _fork_repo('one-fork', 'hg', parent='one') |
|
89 | repo2 = _fork_repo('one-fork', 'hg', parent='one') | |
90 | self.r2_id = repo2.repo_id |
|
90 | self.r2_id = repo2.repo_id | |
91 |
|
91 | |||
92 | #add two extra commit into fork |
|
92 | #add two extra commit into fork | |
93 | cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n', |
|
93 | cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n', | |
94 | message='commit2', vcs_type='hg', parent=cs0) |
|
94 | message='commit2', vcs_type='hg', parent=cs0) | |
95 |
|
95 | |||
96 | cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n', |
|
96 | cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n', | |
97 | message='commit3', vcs_type='hg', parent=cs1) |
|
97 | message='commit3', vcs_type='hg', parent=cs1) | |
98 |
|
98 | |||
99 | rev1 = 'default' |
|
99 | rev1 = 'default' | |
100 | rev2 = 'default' |
|
100 | rev2 = 'default' | |
101 |
|
101 | |||
102 | response = self.app.get(url(controller='compare', action='index', |
|
102 | response = self.app.get(url(controller='compare', action='index', | |
103 | repo_name=repo1.repo_name, |
|
103 | repo_name=repo1.repo_name, | |
104 | org_ref_type="branch", |
|
104 | org_ref_type="branch", | |
105 | org_ref=rev2, |
|
105 | org_ref=rev2, | |
106 | other_repo=repo2.repo_name, |
|
106 | other_repo=repo2.repo_name, | |
107 | other_ref_type="branch", |
|
107 | other_ref_type="branch", | |
108 | other_ref=rev1, |
|
108 | other_ref=rev1, | |
|
109 | merge='1', | |||
109 | )) |
|
110 | )) | |
110 |
|
111 | |||
111 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) |
|
112 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) | |
112 | response.mustcontain("""Showing 2 commits""") |
|
113 | response.mustcontain("""Showing 2 commits""") | |
113 | response.mustcontain("""1 file changed with 2 insertions and 0 deletions""") |
|
114 | response.mustcontain("""1 file changed with 2 insertions and 0 deletions""") | |
114 |
|
115 | |||
115 | response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""") |
|
116 | response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""") | |
116 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") |
|
117 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") | |
117 |
|
118 | |||
118 | response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) |
|
119 | response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) | |
119 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) |
|
120 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) | |
120 | ## files |
|
121 | ## files | |
121 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) |
|
122 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s&merge=1#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) | |
122 | #swap |
|
123 | #swap | |
123 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) |
|
124 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s&merge=True">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) | |
124 |
|
125 | |||
125 | def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self): |
|
126 | def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self): | |
126 | self.log_user() |
|
127 | self.log_user() | |
127 |
|
128 | |||
128 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', |
|
129 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', | |
129 | description='diff-test', |
|
130 | description='diff-test', | |
130 | owner=TEST_USER_ADMIN_LOGIN) |
|
131 | owner=TEST_USER_ADMIN_LOGIN) | |
131 | Session().commit() |
|
132 | Session().commit() | |
132 | self.r1_id = repo1.repo_id |
|
133 | self.r1_id = repo1.repo_id | |
133 |
|
134 | |||
134 | #commit something ! |
|
135 | #commit something ! | |
135 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', |
|
136 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', | |
136 | message='commit1', vcs_type='hg', parent=None, newfile=True) |
|
137 | message='commit1', vcs_type='hg', parent=None, newfile=True) | |
137 |
|
138 | |||
138 | #fork this repo |
|
139 | #fork this repo | |
139 | repo2 = _fork_repo('one-fork', 'hg', parent='one') |
|
140 | repo2 = _fork_repo('one-fork', 'hg', parent='one') | |
140 | self.r2_id = repo2.repo_id |
|
141 | self.r2_id = repo2.repo_id | |
141 |
|
142 | |||
142 | #now commit something to origin repo |
|
143 | #now commit something to origin repo | |
143 | cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n', |
|
144 | cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n', | |
144 | message='commit2', vcs_type='hg', parent=cs0, newfile=True) |
|
145 | message='commit2', vcs_type='hg', parent=cs0, newfile=True) | |
145 |
|
146 | |||
146 | #add two extra commit into fork |
|
147 | #add two extra commit into fork | |
147 | cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n', |
|
148 | cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n', | |
148 | message='commit2', vcs_type='hg', parent=cs0) |
|
149 | message='commit2', vcs_type='hg', parent=cs0) | |
149 |
|
150 | |||
150 | cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n', |
|
151 | cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n', | |
151 | message='commit3', vcs_type='hg', parent=cs1) |
|
152 | message='commit3', vcs_type='hg', parent=cs1) | |
152 |
|
153 | |||
153 | rev1 = 'default' |
|
154 | rev1 = 'default' | |
154 | rev2 = 'default' |
|
155 | rev2 = 'default' | |
155 |
|
156 | |||
156 | response = self.app.get(url(controller='compare', action='index', |
|
157 | response = self.app.get(url(controller='compare', action='index', | |
157 | repo_name=repo1.repo_name, |
|
158 | repo_name=repo1.repo_name, | |
158 | org_ref_type="branch", |
|
159 | org_ref_type="branch", | |
159 | org_ref=rev2, |
|
160 | org_ref=rev2, | |
160 | other_repo=repo2.repo_name, |
|
161 | other_repo=repo2.repo_name, | |
161 | other_ref_type="branch", |
|
162 | other_ref_type="branch", | |
162 | other_ref=rev1, |
|
163 | other_ref=rev1, | |
|
164 | merge='x', | |||
163 | )) |
|
165 | )) | |
164 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) |
|
166 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1)) | |
165 | response.mustcontain("""Showing 2 commits""") |
|
167 | response.mustcontain("""Showing 2 commits""") | |
166 | response.mustcontain("""1 file changed with 2 insertions and 0 deletions""") |
|
168 | response.mustcontain("""1 file changed with 2 insertions and 0 deletions""") | |
167 |
|
169 | |||
168 | response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""") |
|
170 | response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""") | |
169 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") |
|
171 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") | |
170 |
|
172 | |||
171 | response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) |
|
173 | response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id)) | |
172 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) |
|
174 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id)) | |
173 | ## files |
|
175 | ## files | |
174 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) |
|
176 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s&merge=x#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name)) | |
175 | #swap |
|
177 | #swap | |
176 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) |
|
178 | response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s&merge=True">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name)) | |
177 |
|
179 | |||
178 | def test_compare_cherry_pick_changesets_from_bottom(self): |
|
180 | def test_compare_cherry_pick_changesets_from_bottom(self): | |
179 |
|
181 | |||
180 | # repo1: |
|
182 | # repo1: | |
181 | # cs0: |
|
183 | # cs0: | |
182 | # cs1: |
|
184 | # cs1: | |
183 | # repo1-fork- in which we will cherry pick bottom changesets |
|
185 | # repo1-fork- in which we will cherry pick bottom changesets | |
184 | # cs0: |
|
186 | # cs0: | |
185 | # cs1: |
|
187 | # cs1: | |
186 | # cs2: x |
|
188 | # cs2: x | |
187 | # cs3: x |
|
189 | # cs3: x | |
188 | # cs4: x |
|
190 | # cs4: x | |
189 | # cs5: |
|
191 | # cs5: | |
190 | #make repo1, and cs1+cs2 |
|
192 | #make repo1, and cs1+cs2 | |
191 | self.log_user() |
|
193 | self.log_user() | |
192 |
|
194 | |||
193 | repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg', |
|
195 | repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg', | |
194 | description='diff-test', |
|
196 | description='diff-test', | |
195 | owner=TEST_USER_ADMIN_LOGIN) |
|
197 | owner=TEST_USER_ADMIN_LOGIN) | |
196 | Session().commit() |
|
198 | Session().commit() | |
197 | self.r1_id = repo1.repo_id |
|
199 | self.r1_id = repo1.repo_id | |
198 |
|
200 | |||
199 | #commit something ! |
|
201 | #commit something ! | |
200 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', |
|
202 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', | |
201 | message='commit1', vcs_type='hg', parent=None, |
|
203 | message='commit1', vcs_type='hg', parent=None, | |
202 | newfile=True) |
|
204 | newfile=True) | |
203 | cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n', |
|
205 | cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n', | |
204 | message='commit2', vcs_type='hg', parent=cs0) |
|
206 | message='commit2', vcs_type='hg', parent=cs0) | |
205 | #fork this repo |
|
207 | #fork this repo | |
206 | repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1') |
|
208 | repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1') | |
207 | self.r2_id = repo2.repo_id |
|
209 | self.r2_id = repo2.repo_id | |
208 | #now make cs3-6 |
|
210 | #now make cs3-6 | |
209 | cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n', |
|
211 | cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n', | |
210 | message='commit3', vcs_type='hg', parent=cs1) |
|
212 | message='commit3', vcs_type='hg', parent=cs1) | |
211 | cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n', |
|
213 | cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n', | |
212 | message='commit4', vcs_type='hg', parent=cs2) |
|
214 | message='commit4', vcs_type='hg', parent=cs2) | |
213 | cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n', |
|
215 | cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n', | |
214 | message='commit5', vcs_type='hg', parent=cs3) |
|
216 | message='commit5', vcs_type='hg', parent=cs3) | |
215 | cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', |
|
217 | cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', | |
216 | message='commit6', vcs_type='hg', parent=cs4) |
|
218 | message='commit6', vcs_type='hg', parent=cs4) | |
217 |
|
219 | |||
218 | response = self.app.get(url(controller='compare', action='index', |
|
220 | response = self.app.get(url(controller='compare', action='index', | |
219 | repo_name=repo2.repo_name, |
|
221 | repo_name=repo2.repo_name, | |
220 | org_ref_type="rev", |
|
222 | org_ref_type="rev", | |
221 | org_ref=cs1.short_id, # parent of cs2, in repo2 |
|
223 | org_ref=cs1.short_id, # parent of cs2, in repo2 | |
222 | other_repo=repo1.repo_name, |
|
224 | other_repo=repo1.repo_name, | |
223 | other_ref_type="rev", |
|
225 | other_ref_type="rev", | |
224 | other_ref=cs4.short_id, |
|
226 | other_ref=cs4.short_id, | |
|
227 | merge='True', | |||
225 | )) |
|
228 | )) | |
226 | response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, cs1.short_id, repo1.repo_name, cs4.short_id)) |
|
229 | response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, cs1.short_id, repo1.repo_name, cs4.short_id)) | |
227 | response.mustcontain("""Showing 3 commits""") |
|
230 | response.mustcontain("""Showing 3 commits""") | |
228 | response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") |
|
231 | response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") | |
229 |
|
232 | |||
230 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") |
|
233 | response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""") | |
231 | response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""") |
|
234 | response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""") | |
232 | response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""") |
|
235 | response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""") | |
233 |
|
236 | |||
234 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo1.repo_name, cs2.raw_id, cs2.short_id)) |
|
237 | response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo1.repo_name, cs2.raw_id, cs2.short_id)) | |
235 | response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id)) |
|
238 | response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id)) | |
236 | response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id)) |
|
239 | response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id)) | |
237 | ## files |
|
240 | ## files | |
238 | response.mustcontain("""#C--826e8142e6ba">file1</a>""") |
|
241 | response.mustcontain("""#C--826e8142e6ba">file1</a>""") | |
239 |
|
242 | |||
240 | def test_compare_cherry_pick_changesets_from_top(self): |
|
243 | def test_compare_cherry_pick_changesets_from_top(self): | |
241 | # repo1: |
|
244 | # repo1: | |
242 | # cs0: |
|
245 | # cs0: | |
243 | # cs1: |
|
246 | # cs1: | |
244 | # repo1-fork- in which we will cherry pick bottom changesets |
|
247 | # repo1-fork- in which we will cherry pick bottom changesets | |
245 | # cs0: |
|
248 | # cs0: | |
246 | # cs1: |
|
249 | # cs1: | |
247 | # cs2: |
|
250 | # cs2: | |
248 | # cs3: x |
|
251 | # cs3: x | |
249 | # cs4: x |
|
252 | # cs4: x | |
250 | # cs5: x |
|
253 | # cs5: x | |
251 | # |
|
254 | # | |
252 | #make repo1, and cs1+cs2 |
|
255 | #make repo1, and cs1+cs2 | |
253 | self.log_user() |
|
256 | self.log_user() | |
254 | repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg', |
|
257 | repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg', | |
255 | description='diff-test', |
|
258 | description='diff-test', | |
256 | owner=TEST_USER_ADMIN_LOGIN) |
|
259 | owner=TEST_USER_ADMIN_LOGIN) | |
257 | Session().commit() |
|
260 | Session().commit() | |
258 | self.r1_id = repo1.repo_id |
|
261 | self.r1_id = repo1.repo_id | |
259 |
|
262 | |||
260 | #commit something ! |
|
263 | #commit something ! | |
261 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', |
|
264 | cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n', | |
262 | message='commit1', vcs_type='hg', parent=None, |
|
265 | message='commit1', vcs_type='hg', parent=None, | |
263 | newfile=True) |
|
266 | newfile=True) | |
264 | cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n', |
|
267 | cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n', | |
265 | message='commit2', vcs_type='hg', parent=cs0) |
|
268 | message='commit2', vcs_type='hg', parent=cs0) | |
266 | #fork this repo |
|
269 | #fork this repo | |
267 | repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1') |
|
270 | repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1') | |
268 | self.r2_id = repo2.repo_id |
|
271 | self.r2_id = repo2.repo_id | |
269 | #now make cs3-6 |
|
272 | #now make cs3-6 | |
270 | cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n', |
|
273 | cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n', | |
271 | message='commit3', vcs_type='hg', parent=cs1) |
|
274 | message='commit3', vcs_type='hg', parent=cs1) | |
272 | cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n', |
|
275 | cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n', | |
273 | message='commit4', vcs_type='hg', parent=cs2) |
|
276 | message='commit4', vcs_type='hg', parent=cs2) | |
274 | cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n', |
|
277 | cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n', | |
275 | message='commit5', vcs_type='hg', parent=cs3) |
|
278 | message='commit5', vcs_type='hg', parent=cs3) | |
276 | cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', |
|
279 | cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n', | |
277 | message='commit6', vcs_type='hg', parent=cs4) |
|
280 | message='commit6', vcs_type='hg', parent=cs4) | |
278 | response = self.app.get(url(controller='compare', action='index', |
|
281 | response = self.app.get(url(controller='compare', action='index', | |
279 | repo_name=repo1.repo_name, |
|
282 | repo_name=repo1.repo_name, | |
280 | org_ref_type="rev", |
|
283 | org_ref_type="rev", | |
281 | org_ref=cs2.short_id, # parent of cs3, not in repo2 |
|
284 | org_ref=cs2.short_id, # parent of cs3, not in repo2 | |
282 | other_ref_type="rev", |
|
285 | other_ref_type="rev", | |
283 | other_ref=cs5.short_id, |
|
286 | other_ref=cs5.short_id, | |
|
287 | merge='1', | |||
284 | )) |
|
288 | )) | |
285 |
|
289 | |||
286 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, cs2.short_id, repo1.repo_name, cs5.short_id)) |
|
290 | response.mustcontain('%s@%s -> %s@%s' % (repo1.repo_name, cs2.short_id, repo1.repo_name, cs5.short_id)) | |
287 | response.mustcontain("""Showing 3 commits""") |
|
291 | response.mustcontain("""Showing 3 commits""") | |
288 | response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") |
|
292 | response.mustcontain("""1 file changed with 3 insertions and 0 deletions""") | |
289 |
|
293 | |||
290 | response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""") |
|
294 | response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""") | |
291 | response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""") |
|
295 | response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""") | |
292 | response.mustcontain("""<div class="message tooltip" title="commit6" style="white-space:normal">commit6</div>""") |
|
296 | response.mustcontain("""<div class="message tooltip" title="commit6" style="white-space:normal">commit6</div>""") | |
293 |
|
297 | |||
294 | response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id)) |
|
298 | response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id)) | |
295 | response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id)) |
|
299 | response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id)) | |
296 | response.mustcontain("""<a href="/%s/changeset/%s">r5:%s</a>""" % (repo1.repo_name, cs5.raw_id, cs5.short_id)) |
|
300 | response.mustcontain("""<a href="/%s/changeset/%s">r5:%s</a>""" % (repo1.repo_name, cs5.raw_id, cs5.short_id)) | |
297 | ## files |
|
301 | ## files | |
298 | response.mustcontain("""#C--826e8142e6ba">file1</a>""") |
|
302 | response.mustcontain("""#C--826e8142e6ba">file1</a>""") | |
299 |
|
303 | |||
300 | def test_compare_cherry_pick_changeset_mixed_branches(self): |
|
304 | def test_compare_cherry_pick_changeset_mixed_branches(self): | |
301 | """ |
|
305 | """ | |
302 |
|
306 | |||
303 | """ |
|
307 | """ | |
304 | pass |
|
308 | pass | |
305 | #TODO write this tastecase |
|
309 | #TODO write this tastecase | |
306 |
|
310 | |||
307 | def test_compare_remote_branches_hg(self): |
|
311 | def test_compare_remote_branches_hg(self): | |
308 | self.log_user() |
|
312 | self.log_user() | |
309 |
|
313 | |||
310 | repo2 = _fork_repo(HG_FORK, 'hg') |
|
314 | repo2 = _fork_repo(HG_FORK, 'hg') | |
311 | self.r2_id = repo2.repo_id |
|
315 | self.r2_id = repo2.repo_id | |
312 | rev1 = '56349e29c2af' |
|
316 | rev1 = '56349e29c2af' | |
313 | rev2 = '7d4bc8ec6be5' |
|
317 | rev2 = '7d4bc8ec6be5' | |
314 |
|
318 | |||
315 | response = self.app.get(url(controller='compare', action='index', |
|
319 | response = self.app.get(url(controller='compare', action='index', | |
316 | repo_name=HG_REPO, |
|
320 | repo_name=HG_REPO, | |
317 | org_ref_type="rev", |
|
321 | org_ref_type="rev", | |
318 | org_ref=rev1, |
|
322 | org_ref=rev1, | |
319 | other_ref_type="rev", |
|
323 | other_ref_type="rev", | |
320 | other_ref=rev2, |
|
324 | other_ref=rev2, | |
321 | other_repo=HG_FORK, |
|
325 | other_repo=HG_FORK, | |
|
326 | merge='1', | |||
322 | )) |
|
327 | )) | |
323 | response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2)) |
|
328 | response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2)) | |
324 | ## outgoing changesets between those revisions |
|
329 | ## outgoing changesets between those revisions | |
325 |
|
330 | |||
326 | response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_FORK)) |
|
331 | response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_FORK)) | |
327 | response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_FORK)) |
|
332 | response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_FORK)) | |
328 | response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_FORK, rev2)) |
|
333 | response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_FORK, rev2)) | |
329 |
|
334 | |||
330 | ## files |
|
335 | ## files | |
331 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) |
|
336 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s&merge=1#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) | |
332 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) |
|
337 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s&merge=1#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) | |
333 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) |
|
338 | response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s&merge=1#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK)) | |
334 |
|
339 | |||
335 | def test_org_repo_new_commits_after_forking_simple_diff(self): |
|
340 | def test_org_repo_new_commits_after_forking_simple_diff(self): | |
336 | self.log_user() |
|
341 | self.log_user() | |
337 |
|
342 | |||
338 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', |
|
343 | repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', | |
339 | description='diff-test', |
|
344 | description='diff-test', | |
340 | owner=TEST_USER_ADMIN_LOGIN) |
|
345 | owner=TEST_USER_ADMIN_LOGIN) | |
341 |
|
346 | |||
342 | Session().commit() |
|
347 | Session().commit() | |
343 | self.r1_id = repo1.repo_id |
|
348 | self.r1_id = repo1.repo_id | |
344 | r1_name = repo1.repo_name |
|
349 | r1_name = repo1.repo_name | |
345 |
|
350 | |||
346 | #commit something initially ! |
|
351 | #commit something initially ! | |
347 | cs0 = ScmModel().create_node( |
|
352 | cs0 = ScmModel().create_node( | |
348 | repo=repo1.scm_instance, repo_name=r1_name, |
|
353 | repo=repo1.scm_instance, repo_name=r1_name, | |
349 | cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, |
|
354 | cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, | |
350 | author=TEST_USER_ADMIN_LOGIN, |
|
355 | author=TEST_USER_ADMIN_LOGIN, | |
351 | message='commit1', |
|
356 | message='commit1', | |
352 | content='line1', |
|
357 | content='line1', | |
353 | f_path='file1' |
|
358 | f_path='file1' | |
354 | ) |
|
359 | ) | |
355 | Session().commit() |
|
360 | Session().commit() | |
356 | self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id]) |
|
361 | self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id]) | |
357 | #fork the repo1 |
|
362 | #fork the repo1 | |
358 | repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg', |
|
363 | repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg', | |
359 | description='compare-test', |
|
364 | description='compare-test', | |
360 | clone_uri=repo1.repo_full_path, |
|
365 | clone_uri=repo1.repo_full_path, | |
361 | owner=TEST_USER_ADMIN_LOGIN, fork_of='one') |
|
366 | owner=TEST_USER_ADMIN_LOGIN, fork_of='one') | |
362 | Session().commit() |
|
367 | Session().commit() | |
363 | self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id]) |
|
368 | self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id]) | |
364 | self.r2_id = repo2.repo_id |
|
369 | self.r2_id = repo2.repo_id | |
365 | r2_name = repo2.repo_name |
|
370 | r2_name = repo2.repo_name | |
366 |
|
371 | |||
367 | #make 3 new commits in fork |
|
372 | #make 3 new commits in fork | |
368 | cs1 = ScmModel().create_node( |
|
373 | cs1 = ScmModel().create_node( | |
369 | repo=repo2.scm_instance, repo_name=r2_name, |
|
374 | repo=repo2.scm_instance, repo_name=r2_name, | |
370 | cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN, |
|
375 | cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN, | |
371 | author=TEST_USER_ADMIN_LOGIN, |
|
376 | author=TEST_USER_ADMIN_LOGIN, | |
372 | message='commit1-fork', |
|
377 | message='commit1-fork', | |
373 | content='file1-line1-from-fork', |
|
378 | content='file1-line1-from-fork', | |
374 | f_path='file1-fork' |
|
379 | f_path='file1-fork' | |
375 | ) |
|
380 | ) | |
376 | cs2 = ScmModel().create_node( |
|
381 | cs2 = ScmModel().create_node( | |
377 | repo=repo2.scm_instance, repo_name=r2_name, |
|
382 | repo=repo2.scm_instance, repo_name=r2_name, | |
378 | cs=cs1, user=TEST_USER_ADMIN_LOGIN, |
|
383 | cs=cs1, user=TEST_USER_ADMIN_LOGIN, | |
379 | author=TEST_USER_ADMIN_LOGIN, |
|
384 | author=TEST_USER_ADMIN_LOGIN, | |
380 | message='commit2-fork', |
|
385 | message='commit2-fork', | |
381 | content='file2-line1-from-fork', |
|
386 | content='file2-line1-from-fork', | |
382 | f_path='file2-fork' |
|
387 | f_path='file2-fork' | |
383 | ) |
|
388 | ) | |
384 | cs3 = ScmModel().create_node( |
|
389 | cs3 = ScmModel().create_node( | |
385 | repo=repo2.scm_instance, repo_name=r2_name, |
|
390 | repo=repo2.scm_instance, repo_name=r2_name, | |
386 | cs=cs2, user=TEST_USER_ADMIN_LOGIN, |
|
391 | cs=cs2, user=TEST_USER_ADMIN_LOGIN, | |
387 | author=TEST_USER_ADMIN_LOGIN, |
|
392 | author=TEST_USER_ADMIN_LOGIN, | |
388 | message='commit3-fork', |
|
393 | message='commit3-fork', | |
389 | content='file3-line1-from-fork', |
|
394 | content='file3-line1-from-fork', | |
390 | f_path='file3-fork' |
|
395 | f_path='file3-fork' | |
391 | ) |
|
396 | ) | |
392 |
|
397 | |||
393 | #compare ! |
|
398 | #compare ! | |
394 | rev1 = 'default' |
|
399 | rev1 = 'default' | |
395 | rev2 = 'default' |
|
400 | rev2 = 'default' | |
396 |
|
401 | |||
397 | response = self.app.get(url(controller='compare', action='index', |
|
402 | response = self.app.get(url(controller='compare', action='index', | |
398 | repo_name=r2_name, |
|
403 | repo_name=r2_name, | |
399 | org_ref_type="branch", |
|
404 | org_ref_type="branch", | |
400 | org_ref=rev1, |
|
405 | org_ref=rev1, | |
401 | other_ref_type="branch", |
|
406 | other_ref_type="branch", | |
402 | other_ref=rev2, |
|
407 | other_ref=rev2, | |
403 | other_repo=r1_name, |
|
408 | other_repo=r1_name, | |
|
409 | merge='1', | |||
404 | )) |
|
410 | )) | |
405 | response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) |
|
411 | response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) | |
406 | response.mustcontain('No files') |
|
412 | response.mustcontain('No files') | |
407 | response.mustcontain('No changesets') |
|
413 | response.mustcontain('No changesets') | |
408 |
|
414 | |||
409 | #add new commit into parent ! |
|
415 | #add new commit into parent ! | |
410 | cs0 = ScmModel().create_node( |
|
416 | cs0 = ScmModel().create_node( | |
411 | repo=repo1.scm_instance, repo_name=r1_name, |
|
417 | repo=repo1.scm_instance, repo_name=r1_name, | |
412 | cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, |
|
418 | cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, | |
413 | author=TEST_USER_ADMIN_LOGIN, |
|
419 | author=TEST_USER_ADMIN_LOGIN, | |
414 | message='commit2-parent', |
|
420 | message='commit2-parent', | |
415 | content='line1-added-after-fork', |
|
421 | content='line1-added-after-fork', | |
416 | f_path='file2' |
|
422 | f_path='file2' | |
417 | ) |
|
423 | ) | |
418 | #compare ! |
|
424 | #compare ! | |
419 | rev1 = 'default' |
|
425 | rev1 = 'default' | |
420 | rev2 = 'default' |
|
426 | rev2 = 'default' | |
421 | response = self.app.get(url(controller='compare', action='index', |
|
427 | response = self.app.get(url(controller='compare', action='index', | |
422 | repo_name=r2_name, |
|
428 | repo_name=r2_name, | |
423 | org_ref_type="branch", |
|
429 | org_ref_type="branch", | |
424 | org_ref=rev1, |
|
430 | org_ref=rev1, | |
425 | other_ref_type="branch", |
|
431 | other_ref_type="branch", | |
426 | other_ref=rev2, |
|
432 | other_ref=rev2, | |
427 | other_repo=r1_name, |
|
433 | other_repo=r1_name, | |
|
434 | merge='1', | |||
428 | )) |
|
435 | )) | |
429 |
|
436 | |||
430 | response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) |
|
437 | response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) | |
431 |
|
438 | |||
432 | response.mustcontain("""commit2-parent""") |
|
439 | response.mustcontain("""commit2-parent""") | |
433 | response.mustcontain("""1 file changed with 1 insertions and 0 deletions""") |
|
440 | response.mustcontain("""1 file changed with 1 insertions and 0 deletions""") | |
434 | response.mustcontain("""line1-added-after-fork""") |
|
441 | response.mustcontain("""line1-added-after-fork""") |
General Comments 0
You need to be logged in to leave comments.
Login now