##// END OF EJS Templates
compare: cleanup of as_form handling...
Mads Kiilerich -
r3442:b3680a20 beta
parent child Browse files
Show More
@@ -1,192 +1,192 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
86
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 = (other_ref_type, other_ref)
89 other_ref = (other_ref_type, other_ref)
90 other_repo = request.GET.get('other_repo', org_repo)
90 other_repo = request.GET.get('other_repo', org_repo)
91 c.fulldiff = fulldiff = request.GET.get('fulldiff')
91 c.fulldiff = fulldiff = request.GET.get('fulldiff')
92 rev_start = request.GET.get('rev_start')
92 rev_start = request.GET.get('rev_start')
93 rev_end = request.GET.get('rev_end')
93 rev_end = request.GET.get('rev_end')
94
94 # partial uses compare_cs.html template directly
95 c.swap_url = h.url('compare_url', as_form=request.GET.get('as_form'),
95 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
96 # as_form puts hidden input field with changeset revisions
97 c.as_form = partial and request.GET.get('as_form')
98 # swap url for compare_diff page - never partial and never as_form
99 c.swap_url = h.url('compare_url',
96 repo_name=other_repo,
100 repo_name=other_repo,
97 org_ref_type=other_ref[0], org_ref=other_ref[1],
101 org_ref_type=other_ref[0], org_ref=other_ref[1],
98 other_repo=org_repo,
102 other_repo=org_repo,
99 other_ref_type=org_ref[0], other_ref=org_ref[1])
103 other_ref_type=org_ref[0], other_ref=org_ref[1])
100
104
101 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
102
103 org_repo = Repository.get_by_repo_name(org_repo)
105 org_repo = Repository.get_by_repo_name(org_repo)
104 other_repo = Repository.get_by_repo_name(other_repo)
106 other_repo = Repository.get_by_repo_name(other_repo)
105
107
106 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
108 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
107 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
109 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
108
110
109 if org_repo is None:
111 if org_repo is None:
110 log.error('Could not find org repo %s' % org_repo)
112 log.error('Could not find org repo %s' % org_repo)
111 raise HTTPNotFound
113 raise HTTPNotFound
112 if other_repo is None:
114 if other_repo is None:
113 log.error('Could not find other repo %s' % other_repo)
115 log.error('Could not find other repo %s' % other_repo)
114 raise HTTPNotFound
116 raise HTTPNotFound
115
117
116 if org_repo != other_repo and h.is_git(org_repo):
118 if org_repo != other_repo and h.is_git(org_repo):
117 log.error('compare of two remote repos not available for GIT REPOS')
119 log.error('compare of two remote repos not available for GIT REPOS')
118 raise HTTPNotFound
120 raise HTTPNotFound
119
121
120 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
122 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
121 log.error('compare of two different kind of remote repos not available')
123 log.error('compare of two different kind of remote repos not available')
122 raise HTTPNotFound
124 raise HTTPNotFound
123
125
124 c.org_repo = org_repo
126 c.org_repo = org_repo
125 c.other_repo = other_repo
127 c.other_repo = other_repo
126 c.org_ref = org_ref[1]
128 c.org_ref = org_ref[1]
127 c.other_ref = other_ref[1]
129 c.other_ref = other_ref[1]
128 c.org_ref_type = org_ref[0]
130 c.org_ref_type = org_ref[0]
129 c.other_ref_type = other_ref[0]
131 c.other_ref_type = other_ref[0]
130
132
131 if rev_start and rev_end:
133 if rev_start and rev_end:
132 # swap revs with cherry picked ones, save them for display
134 # swap revs with cherry picked ones, save them for display
133 #org_ref = ('rev', rev_start)
135 #org_ref = ('rev', rev_start)
134 #other_ref = ('rev', rev_end)
136 #other_ref = ('rev', rev_end)
135 c.org_ref = rev_start[:12]
137 c.org_ref = rev_start[:12]
136 c.other_ref = rev_end[:12]
138 c.other_ref = rev_end[:12]
137 # get parent of
139 # get parent of
138 # rev start to include it in the diff
140 # rev start to include it in the diff
139 _cs = other_repo.scm_instance.get_changeset(rev_start)
141 _cs = other_repo.scm_instance.get_changeset(rev_start)
140 rev_start = _cs.parents[0].raw_id if _cs.parents else EmptyChangeset().raw_id
142 rev_start = _cs.parents[0].raw_id if _cs.parents else EmptyChangeset().raw_id
141 org_ref = ('rev', rev_start)
143 org_ref = ('rev', rev_start)
142 other_ref = ('rev', rev_end)
144 other_ref = ('rev', rev_end)
143 #if we cherry pick it's not remote, make the other_repo org_repo
145 #if we cherry pick it's not remote, make the other_repo org_repo
144 org_repo = other_repo
146 org_repo = other_repo
145
147
146 c.cs_ranges, ancestor = PullRequestModel().get_compare_data(
148 c.cs_ranges, ancestor = PullRequestModel().get_compare_data(
147 org_repo, org_ref, other_repo, other_ref)
149 org_repo, org_ref, other_repo, other_ref)
148
150
149 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
151 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
150 c.cs_ranges])
152 c.cs_ranges])
151 # defines that we need hidden inputs with changesets
152 c.as_form = request.GET.get('as_form', False)
153 if partial:
153 if partial:
154 return render('compare/compare_cs.html')
154 return render('compare/compare_cs.html')
155
155
156 if ancestor and org_repo != other_repo:
156 if ancestor and org_repo != other_repo:
157 # case we want a simple diff without incoming changesets,
157 # case we want a simple diff without incoming changesets,
158 # previewing what will be merged.
158 # previewing what will be merged.
159 # Make the diff on the forked repo, with
159 # Make the diff on the forked repo, with
160 # revision that is common ancestor
160 # revision that is common ancestor
161 log.debug('Using ancestor %s as org_ref instead of %s'
161 log.debug('Using ancestor %s as org_ref instead of %s'
162 % (ancestor, org_ref))
162 % (ancestor, org_ref))
163 org_ref = ('rev', ancestor)
163 org_ref = ('rev', ancestor)
164 org_repo = other_repo
164 org_repo = other_repo
165
165
166 diff_limit = self.cut_off_limit if not fulldiff else None
166 diff_limit = self.cut_off_limit if not fulldiff else None
167
167
168 _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref)
168 _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref)
169
169
170 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
170 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
171 diff_limit=diff_limit)
171 diff_limit=diff_limit)
172 _parsed = diff_processor.prepare()
172 _parsed = diff_processor.prepare()
173
173
174 c.limited_diff = False
174 c.limited_diff = False
175 if isinstance(_parsed, LimitedDiffContainer):
175 if isinstance(_parsed, LimitedDiffContainer):
176 c.limited_diff = True
176 c.limited_diff = True
177
177
178 c.files = []
178 c.files = []
179 c.changes = {}
179 c.changes = {}
180 c.lines_added = 0
180 c.lines_added = 0
181 c.lines_deleted = 0
181 c.lines_deleted = 0
182 for f in _parsed:
182 for f in _parsed:
183 st = f['stats']
183 st = f['stats']
184 if st[0] != 'b':
184 if st[0] != 'b':
185 c.lines_added += st[0]
185 c.lines_added += st[0]
186 c.lines_deleted += st[1]
186 c.lines_deleted += st[1]
187 fid = h.FID('', f['filename'])
187 fid = h.FID('', f['filename'])
188 c.files.append([fid, f['operation'], f['filename'], f['stats']])
188 c.files.append([fid, f['operation'], f['filename'], f['stats']])
189 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
189 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
190 c.changes[fid] = [f['operation'], f['filename'], diff]
190 c.changes[fid] = [f['operation'], f['filename'], diff]
191
191
192 return render('compare/compare_diff.html')
192 return render('compare/compare_diff.html')
@@ -1,485 +1,484 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):
70 def _get_repo_refs(self, repo):
71 hist_l = []
71 hist_l = []
72
72
73 branches_group = ([('branch:%s:%s' % (k, v), k) for
73 branches_group = ([('branch:%s:%s' % (k, v), k) for
74 k, v in repo.branches.iteritems()], _("Branches"))
74 k, v in repo.branches.iteritems()], _("Branches"))
75 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
75 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
76 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
76 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
77 tags_group = ([('tag:%s:%s' % (k, v), k) for
77 tags_group = ([('tag:%s:%s' % (k, v), k) for
78 k, v in repo.tags.iteritems()
78 k, v in repo.tags.iteritems()
79 if k != 'tip'], _("Tags"))
79 if k != 'tip'], _("Tags"))
80
80
81 tip = repo.tags['tip']
81 tip = repo.tags['tip']
82 tipref = 'tag:tip:%s' % tip
82 tipref = 'tag:tip:%s' % tip
83 colontip = ':' + tip
83 colontip = ':' + tip
84 tips = [x[1] for x in branches_group[0] + bookmarks_group[0] + tags_group[0]
84 tips = [x[1] for x in branches_group[0] + bookmarks_group[0] + tags_group[0]
85 if x[0].endswith(colontip)]
85 if x[0].endswith(colontip)]
86 tags_group[0].append((tipref, 'tip (%s)' % ', '.join(tips)))
86 tags_group[0].append((tipref, 'tip (%s)' % ', '.join(tips)))
87
87
88 hist_l.append(bookmarks_group)
88 hist_l.append(bookmarks_group)
89 hist_l.append(branches_group)
89 hist_l.append(branches_group)
90 hist_l.append(tags_group)
90 hist_l.append(tags_group)
91
91
92 return hist_l, tipref
92 return hist_l, tipref
93
93
94 def _get_is_allowed_change_status(self, pull_request):
94 def _get_is_allowed_change_status(self, pull_request):
95 owner = self.rhodecode_user.user_id == pull_request.user_id
95 owner = self.rhodecode_user.user_id == pull_request.user_id
96 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
96 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
97 pull_request.reviewers]
97 pull_request.reviewers]
98 return (self.rhodecode_user.admin or owner or reviewer)
98 return (self.rhodecode_user.admin or owner or reviewer)
99
99
100 def show_all(self, repo_name):
100 def show_all(self, repo_name):
101 c.pull_requests = PullRequestModel().get_all(repo_name)
101 c.pull_requests = PullRequestModel().get_all(repo_name)
102 c.repo_name = repo_name
102 c.repo_name = repo_name
103 return render('/pullrequests/pullrequest_show_all.html')
103 return render('/pullrequests/pullrequest_show_all.html')
104
104
105 @NotAnonymous()
105 @NotAnonymous()
106 def index(self):
106 def index(self):
107 org_repo = c.rhodecode_db_repo
107 org_repo = c.rhodecode_db_repo
108
108
109 if org_repo.scm_instance.alias != 'hg':
109 if org_repo.scm_instance.alias != 'hg':
110 log.error('Review not available for GIT REPOS')
110 log.error('Review not available for GIT REPOS')
111 raise HTTPNotFound
111 raise HTTPNotFound
112
112
113 try:
113 try:
114 org_repo.scm_instance.get_changeset()
114 org_repo.scm_instance.get_changeset()
115 except EmptyRepositoryError, e:
115 except EmptyRepositoryError, e:
116 h.flash(h.literal(_('There are no changesets yet')),
116 h.flash(h.literal(_('There are no changesets yet')),
117 category='warning')
117 category='warning')
118 redirect(url('summary_home', repo_name=org_repo.repo_name))
118 redirect(url('summary_home', repo_name=org_repo.repo_name))
119
119
120 other_repos_info = {}
120 other_repos_info = {}
121
121
122 c.org_repos = []
122 c.org_repos = []
123 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
123 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
124 c.default_org_repo = org_repo.repo_name
124 c.default_org_repo = org_repo.repo_name
125 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance)
125 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance)
126
126
127 c.other_repos = []
127 c.other_repos = []
128 # add org repo to other so we can open pull request against itself
128 # add org repo to other so we can open pull request against itself
129 c.other_repos.extend(c.org_repos)
129 c.other_repos.extend(c.org_repos)
130 c.default_other_repo = org_repo.repo_name
130 c.default_other_repo = org_repo.repo_name
131 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance)
131 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance)
132 usr_data = lambda usr: dict(user_id=usr.user_id,
132 usr_data = lambda usr: dict(user_id=usr.user_id,
133 username=usr.username,
133 username=usr.username,
134 firstname=usr.firstname,
134 firstname=usr.firstname,
135 lastname=usr.lastname,
135 lastname=usr.lastname,
136 gravatar_link=h.gravatar_url(usr.email, 14))
136 gravatar_link=h.gravatar_url(usr.email, 14))
137 other_repos_info[org_repo.repo_name] = {
137 other_repos_info[org_repo.repo_name] = {
138 'user': usr_data(org_repo.user),
138 'user': usr_data(org_repo.user),
139 'description': org_repo.description,
139 'description': org_repo.description,
140 'revs': h.select('other_ref', c.default_other_ref,
140 'revs': h.select('other_ref', c.default_other_ref,
141 c.default_other_refs, class_='refs')
141 c.default_other_refs, class_='refs')
142 }
142 }
143
143
144 # gather forks and add to this list ... even though it is rare to
144 # gather forks and add to this list ... even though it is rare to
145 # request forks to pull their parent
145 # request forks to pull their parent
146 for fork in org_repo.forks:
146 for fork in org_repo.forks:
147 c.other_repos.append((fork.repo_name, fork.repo_name))
147 c.other_repos.append((fork.repo_name, fork.repo_name))
148 refs, default_ref = self._get_repo_refs(fork.scm_instance)
148 refs, default_ref = self._get_repo_refs(fork.scm_instance)
149 other_repos_info[fork.repo_name] = {
149 other_repos_info[fork.repo_name] = {
150 'user': usr_data(fork.user),
150 'user': usr_data(fork.user),
151 'description': fork.description,
151 'description': fork.description,
152 'revs': h.select('other_ref', default_ref, refs, class_='refs')
152 'revs': h.select('other_ref', default_ref, refs, class_='refs')
153 }
153 }
154
154
155 # add parents of this fork also, but only if it's not empty
155 # add parents of this fork also, but only if it's not empty
156 if org_repo.parent and org_repo.parent.scm_instance.revisions:
156 if org_repo.parent and org_repo.parent.scm_instance.revisions:
157 c.default_other_repo = org_repo.parent.repo_name
157 c.default_other_repo = org_repo.parent.repo_name
158 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance)
158 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance)
159 c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name))
159 c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name))
160 other_repos_info[org_repo.parent.repo_name] = {
160 other_repos_info[org_repo.parent.repo_name] = {
161 'user': usr_data(org_repo.parent.user),
161 'user': usr_data(org_repo.parent.user),
162 'description': org_repo.parent.description,
162 'description': org_repo.parent.description,
163 'revs': h.select('other_ref', c.default_other_ref,
163 'revs': h.select('other_ref', c.default_other_ref,
164 c.default_other_refs, class_='refs')
164 c.default_other_refs, class_='refs')
165 }
165 }
166
166
167 c.other_repos_info = json.dumps(other_repos_info)
167 c.other_repos_info = json.dumps(other_repos_info)
168 # other repo owner
168 # other repo owner
169 c.review_members = []
169 c.review_members = []
170 return render('/pullrequests/pullrequest.html')
170 return render('/pullrequests/pullrequest.html')
171
171
172 @NotAnonymous()
172 @NotAnonymous()
173 def create(self, repo_name):
173 def create(self, repo_name):
174 repo = RepoModel()._get_repo(repo_name)
174 repo = RepoModel()._get_repo(repo_name)
175 try:
175 try:
176 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
176 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
177 except formencode.Invalid, errors:
177 except formencode.Invalid, errors:
178 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
179 if errors.error_dict.get('revisions'):
179 if errors.error_dict.get('revisions'):
180 msg = 'Revisions: %s' % errors.error_dict['revisions']
180 msg = 'Revisions: %s' % errors.error_dict['revisions']
181 elif errors.error_dict.get('pullrequest_title'):
181 elif errors.error_dict.get('pullrequest_title'):
182 msg = _('Pull request requires a title with min. 3 chars')
182 msg = _('Pull request requires a title with min. 3 chars')
183 else:
183 else:
184 msg = _('error during creation of pull request')
184 msg = _('error during creation of pull request')
185
185
186 h.flash(msg, 'error')
186 h.flash(msg, 'error')
187 return redirect(url('pullrequest_home', repo_name=repo_name))
187 return redirect(url('pullrequest_home', repo_name=repo_name))
188
188
189 org_repo = _form['org_repo']
189 org_repo = _form['org_repo']
190 org_ref = _form['org_ref']
190 org_ref = _form['org_ref']
191 other_repo = _form['other_repo']
191 other_repo = _form['other_repo']
192 other_ref = _form['other_ref']
192 other_ref = _form['other_ref']
193 revisions = _form['revisions']
193 revisions = _form['revisions']
194 reviewers = _form['review_members']
194 reviewers = _form['review_members']
195
195
196 # if we have cherry picked pull request we don't care what is in
196 # if we have cherry picked pull request we don't care what is in
197 # org_ref/other_ref
197 # org_ref/other_ref
198 rev_start = request.POST.get('rev_start')
198 rev_start = request.POST.get('rev_start')
199 rev_end = request.POST.get('rev_end')
199 rev_end = request.POST.get('rev_end')
200
200
201 if rev_start and rev_end:
201 if rev_start and rev_end:
202 # this is swapped to simulate that rev_end is a revision from
202 # this is swapped to simulate that rev_end is a revision from
203 # parent of the fork
203 # parent of the fork
204 org_ref = 'rev:%s:%s' % (rev_end, rev_end)
204 org_ref = 'rev:%s:%s' % (rev_end, rev_end)
205 other_ref = 'rev:%s:%s' % (rev_start, rev_start)
205 other_ref = 'rev:%s:%s' % (rev_start, rev_start)
206
206
207 title = _form['pullrequest_title']
207 title = _form['pullrequest_title']
208 description = _form['pullrequest_desc']
208 description = _form['pullrequest_desc']
209
209
210 try:
210 try:
211 pull_request = PullRequestModel().create(
211 pull_request = PullRequestModel().create(
212 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
212 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
213 other_ref, revisions, reviewers, title, description
213 other_ref, revisions, reviewers, title, description
214 )
214 )
215 Session().commit()
215 Session().commit()
216 h.flash(_('Successfully opened new pull request'),
216 h.flash(_('Successfully opened new pull request'),
217 category='success')
217 category='success')
218 except Exception:
218 except Exception:
219 h.flash(_('Error occurred during sending pull request'),
219 h.flash(_('Error occurred during sending pull request'),
220 category='error')
220 category='error')
221 log.error(traceback.format_exc())
221 log.error(traceback.format_exc())
222 return redirect(url('pullrequest_home', repo_name=repo_name))
222 return redirect(url('pullrequest_home', repo_name=repo_name))
223
223
224 return redirect(url('pullrequest_show', repo_name=other_repo,
224 return redirect(url('pullrequest_show', repo_name=other_repo,
225 pull_request_id=pull_request.pull_request_id))
225 pull_request_id=pull_request.pull_request_id))
226
226
227 @NotAnonymous()
227 @NotAnonymous()
228 @jsonify
228 @jsonify
229 def update(self, repo_name, pull_request_id):
229 def update(self, repo_name, pull_request_id):
230 pull_request = PullRequest.get_or_404(pull_request_id)
230 pull_request = PullRequest.get_or_404(pull_request_id)
231 if pull_request.is_closed():
231 if pull_request.is_closed():
232 raise HTTPForbidden()
232 raise HTTPForbidden()
233 #only owner or admin can update it
233 #only owner or admin can update it
234 owner = pull_request.author.user_id == c.rhodecode_user.user_id
234 owner = pull_request.author.user_id == c.rhodecode_user.user_id
235 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
235 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
236 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
236 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
237 request.POST.get('reviewers_ids', '').split(',')))
237 request.POST.get('reviewers_ids', '').split(',')))
238
238
239 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
239 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
240 Session().commit()
240 Session().commit()
241 return True
241 return True
242 raise HTTPForbidden()
242 raise HTTPForbidden()
243
243
244 @NotAnonymous()
244 @NotAnonymous()
245 @jsonify
245 @jsonify
246 def delete(self, repo_name, pull_request_id):
246 def delete(self, repo_name, pull_request_id):
247 pull_request = PullRequest.get_or_404(pull_request_id)
247 pull_request = PullRequest.get_or_404(pull_request_id)
248 #only owner can delete it !
248 #only owner can delete it !
249 if pull_request.author.user_id == c.rhodecode_user.user_id:
249 if pull_request.author.user_id == c.rhodecode_user.user_id:
250 PullRequestModel().delete(pull_request)
250 PullRequestModel().delete(pull_request)
251 Session().commit()
251 Session().commit()
252 h.flash(_('Successfully deleted pull request'),
252 h.flash(_('Successfully deleted pull request'),
253 category='success')
253 category='success')
254 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
254 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
255 raise HTTPForbidden()
255 raise HTTPForbidden()
256
256
257 def _load_compare_data(self, pull_request, enable_comments=True):
257 def _load_compare_data(self, pull_request, enable_comments=True):
258 """
258 """
259 Load context data needed for generating compare diff
259 Load context data needed for generating compare diff
260
260
261 :param pull_request:
261 :param pull_request:
262 :type pull_request:
262 :type pull_request:
263 """
263 """
264 rev_start = request.GET.get('rev_start')
264 rev_start = request.GET.get('rev_start')
265 rev_end = request.GET.get('rev_end')
265 rev_end = request.GET.get('rev_end')
266
266
267 org_repo = pull_request.org_repo
267 org_repo = pull_request.org_repo
268 (org_ref_type,
268 (org_ref_type,
269 org_ref_name,
269 org_ref_name,
270 org_ref_rev) = pull_request.org_ref.split(':')
270 org_ref_rev) = pull_request.org_ref.split(':')
271
271
272 other_repo = org_repo
272 other_repo = org_repo
273 (other_ref_type,
273 (other_ref_type,
274 other_ref_name,
274 other_ref_name,
275 other_ref_rev) = pull_request.other_ref.split(':')
275 other_ref_rev) = pull_request.other_ref.split(':')
276
276
277 # despite opening revisions for bookmarks/branches/tags, we always
277 # despite opening revisions for bookmarks/branches/tags, we always
278 # convert this to rev to prevent changes after book or branch change
278 # convert this to rev to prevent changes after book or branch change
279 org_ref = ('rev', org_ref_rev)
279 org_ref = ('rev', org_ref_rev)
280 other_ref = ('rev', other_ref_rev)
280 other_ref = ('rev', other_ref_rev)
281
281
282 c.org_repo = org_repo
282 c.org_repo = org_repo
283 c.other_repo = other_repo
283 c.other_repo = other_repo
284
284
285 c.fulldiff = fulldiff = request.GET.get('fulldiff')
285 c.fulldiff = fulldiff = request.GET.get('fulldiff')
286
286
287 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
287 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
288
288
289 other_ref = ('rev', getattr(c.cs_ranges[0].parents[0]
289 other_ref = ('rev', getattr(c.cs_ranges[0].parents[0]
290 if c.cs_ranges[0].parents
290 if c.cs_ranges[0].parents
291 else EmptyChangeset(), 'raw_id'))
291 else EmptyChangeset(), 'raw_id'))
292
292
293 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
293 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
294 # defines that we need hidden inputs with changesets
295 c.as_form = request.GET.get('as_form', False)
296
294
297 c.org_ref = org_ref[1]
295 c.org_ref = org_ref[1]
298 c.org_ref_type = org_ref[0]
296 c.org_ref_type = org_ref[0]
299 c.other_ref = other_ref[1]
297 c.other_ref = other_ref[1]
300 c.other_ref_type = other_ref[0]
298 c.other_ref_type = other_ref[0]
301
299
302 diff_limit = self.cut_off_limit if not fulldiff else None
300 diff_limit = self.cut_off_limit if not fulldiff else None
303
301
304 #we swap org/other ref since we run a simple diff on one repo
302 #we swap org/other ref since we run a simple diff on one repo
305 _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref)
303 _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref)
306
304
307 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
305 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
308 diff_limit=diff_limit)
306 diff_limit=diff_limit)
309 _parsed = diff_processor.prepare()
307 _parsed = diff_processor.prepare()
310
308
311 c.limited_diff = False
309 c.limited_diff = False
312 if isinstance(_parsed, LimitedDiffContainer):
310 if isinstance(_parsed, LimitedDiffContainer):
313 c.limited_diff = True
311 c.limited_diff = True
314
312
315 c.files = []
313 c.files = []
316 c.changes = {}
314 c.changes = {}
317 c.lines_added = 0
315 c.lines_added = 0
318 c.lines_deleted = 0
316 c.lines_deleted = 0
319 for f in _parsed:
317 for f in _parsed:
320 st = f['stats']
318 st = f['stats']
321 if st[0] != 'b':
319 if st[0] != 'b':
322 c.lines_added += st[0]
320 c.lines_added += st[0]
323 c.lines_deleted += st[1]
321 c.lines_deleted += st[1]
324 fid = h.FID('', f['filename'])
322 fid = h.FID('', f['filename'])
325 c.files.append([fid, f['operation'], f['filename'], f['stats']])
323 c.files.append([fid, f['operation'], f['filename'], f['stats']])
326 diff = diff_processor.as_html(enable_comments=enable_comments,
324 diff = diff_processor.as_html(enable_comments=enable_comments,
327 parsed_lines=[f])
325 parsed_lines=[f])
328 c.changes[fid] = [f['operation'], f['filename'], diff]
326 c.changes[fid] = [f['operation'], f['filename'], diff]
329
327
330 def show(self, repo_name, pull_request_id):
328 def show(self, repo_name, pull_request_id):
331 repo_model = RepoModel()
329 repo_model = RepoModel()
332 c.users_array = repo_model.get_users_js()
330 c.users_array = repo_model.get_users_js()
333 c.users_groups_array = repo_model.get_users_groups_js()
331 c.users_groups_array = repo_model.get_users_groups_js()
334 c.pull_request = PullRequest.get_or_404(pull_request_id)
332 c.pull_request = PullRequest.get_or_404(pull_request_id)
335 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
333 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
336 cc_model = ChangesetCommentsModel()
334 cc_model = ChangesetCommentsModel()
337 cs_model = ChangesetStatusModel()
335 cs_model = ChangesetStatusModel()
338 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
336 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
339 pull_request=c.pull_request,
337 pull_request=c.pull_request,
340 with_revisions=True)
338 with_revisions=True)
341
339
342 cs_statuses = defaultdict(list)
340 cs_statuses = defaultdict(list)
343 for st in _cs_statuses:
341 for st in _cs_statuses:
344 cs_statuses[st.author.username] += [st]
342 cs_statuses[st.author.username] += [st]
345
343
346 c.pull_request_reviewers = []
344 c.pull_request_reviewers = []
347 c.pull_request_pending_reviewers = []
345 c.pull_request_pending_reviewers = []
348 for o in c.pull_request.reviewers:
346 for o in c.pull_request.reviewers:
349 st = cs_statuses.get(o.user.username, None)
347 st = cs_statuses.get(o.user.username, None)
350 if st:
348 if st:
351 sorter = lambda k: k.version
349 sorter = lambda k: k.version
352 st = [(x, list(y)[0])
350 st = [(x, list(y)[0])
353 for x, y in (groupby(sorted(st, key=sorter), sorter))]
351 for x, y in (groupby(sorted(st, key=sorter), sorter))]
354 else:
352 else:
355 c.pull_request_pending_reviewers.append(o.user)
353 c.pull_request_pending_reviewers.append(o.user)
356 c.pull_request_reviewers.append([o.user, st])
354 c.pull_request_reviewers.append([o.user, st])
357
355
358 # pull_requests repo_name we opened it against
356 # pull_requests repo_name we opened it against
359 # ie. other_repo must match
357 # ie. other_repo must match
360 if repo_name != c.pull_request.other_repo.repo_name:
358 if repo_name != c.pull_request.other_repo.repo_name:
361 raise HTTPNotFound
359 raise HTTPNotFound
362
360
363 # load compare data into template context
361 # load compare data into template context
364 enable_comments = not c.pull_request.is_closed()
362 enable_comments = not c.pull_request.is_closed()
365 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
363 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
366
364
367 # inline comments
365 # inline comments
368 c.inline_cnt = 0
366 c.inline_cnt = 0
369 c.inline_comments = cc_model.get_inline_comments(
367 c.inline_comments = cc_model.get_inline_comments(
370 c.rhodecode_db_repo.repo_id,
368 c.rhodecode_db_repo.repo_id,
371 pull_request=pull_request_id)
369 pull_request=pull_request_id)
372 # count inline comments
370 # count inline comments
373 for __, lines in c.inline_comments:
371 for __, lines in c.inline_comments:
374 for comments in lines.values():
372 for comments in lines.values():
375 c.inline_cnt += len(comments)
373 c.inline_cnt += len(comments)
376 # comments
374 # comments
377 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
375 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
378 pull_request=pull_request_id)
376 pull_request=pull_request_id)
379
377
380 try:
378 try:
381 cur_status = c.statuses[c.pull_request.revisions[0]][0]
379 cur_status = c.statuses[c.pull_request.revisions[0]][0]
382 except:
380 except:
383 log.error(traceback.format_exc())
381 log.error(traceback.format_exc())
384 cur_status = 'undefined'
382 cur_status = 'undefined'
385 if c.pull_request.is_closed() and 0:
383 if c.pull_request.is_closed() and 0:
386 c.current_changeset_status = cur_status
384 c.current_changeset_status = cur_status
387 else:
385 else:
388 # changeset(pull-request) status calulation based on reviewers
386 # changeset(pull-request) status calulation based on reviewers
389 c.current_changeset_status = cs_model.calculate_status(
387 c.current_changeset_status = cs_model.calculate_status(
390 c.pull_request_reviewers,
388 c.pull_request_reviewers,
391 )
389 )
392 c.changeset_statuses = ChangesetStatus.STATUSES
390 c.changeset_statuses = ChangesetStatus.STATUSES
393
391
392 c.as_form = False
394 return render('/pullrequests/pullrequest_show.html')
393 return render('/pullrequests/pullrequest_show.html')
395
394
396 @NotAnonymous()
395 @NotAnonymous()
397 @jsonify
396 @jsonify
398 def comment(self, repo_name, pull_request_id):
397 def comment(self, repo_name, pull_request_id):
399 pull_request = PullRequest.get_or_404(pull_request_id)
398 pull_request = PullRequest.get_or_404(pull_request_id)
400 if pull_request.is_closed():
399 if pull_request.is_closed():
401 raise HTTPForbidden()
400 raise HTTPForbidden()
402
401
403 status = request.POST.get('changeset_status')
402 status = request.POST.get('changeset_status')
404 change_status = request.POST.get('change_changeset_status')
403 change_status = request.POST.get('change_changeset_status')
405 text = request.POST.get('text')
404 text = request.POST.get('text')
406 close_pr = request.POST.get('save_close')
405 close_pr = request.POST.get('save_close')
407
406
408 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
407 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
409 if status and change_status and allowed_to_change_status:
408 if status and change_status and allowed_to_change_status:
410 _def = (_('status change -> %s')
409 _def = (_('status change -> %s')
411 % ChangesetStatus.get_status_lbl(status))
410 % ChangesetStatus.get_status_lbl(status))
412 if close_pr:
411 if close_pr:
413 _def = _('Closing with') + ' ' + _def
412 _def = _('Closing with') + ' ' + _def
414 text = text or _def
413 text = text or _def
415 comm = ChangesetCommentsModel().create(
414 comm = ChangesetCommentsModel().create(
416 text=text,
415 text=text,
417 repo=c.rhodecode_db_repo.repo_id,
416 repo=c.rhodecode_db_repo.repo_id,
418 user=c.rhodecode_user.user_id,
417 user=c.rhodecode_user.user_id,
419 pull_request=pull_request_id,
418 pull_request=pull_request_id,
420 f_path=request.POST.get('f_path'),
419 f_path=request.POST.get('f_path'),
421 line_no=request.POST.get('line'),
420 line_no=request.POST.get('line'),
422 status_change=(ChangesetStatus.get_status_lbl(status)
421 status_change=(ChangesetStatus.get_status_lbl(status)
423 if status and change_status
422 if status and change_status
424 and allowed_to_change_status else None),
423 and allowed_to_change_status else None),
425 closing_pr=close_pr
424 closing_pr=close_pr
426 )
425 )
427
426
428 action_logger(self.rhodecode_user,
427 action_logger(self.rhodecode_user,
429 'user_commented_pull_request:%s' % pull_request_id,
428 'user_commented_pull_request:%s' % pull_request_id,
430 c.rhodecode_db_repo, self.ip_addr, self.sa)
429 c.rhodecode_db_repo, self.ip_addr, self.sa)
431
430
432 if allowed_to_change_status:
431 if allowed_to_change_status:
433 # get status if set !
432 # get status if set !
434 if status and change_status:
433 if status and change_status:
435 ChangesetStatusModel().set_status(
434 ChangesetStatusModel().set_status(
436 c.rhodecode_db_repo.repo_id,
435 c.rhodecode_db_repo.repo_id,
437 status,
436 status,
438 c.rhodecode_user.user_id,
437 c.rhodecode_user.user_id,
439 comm,
438 comm,
440 pull_request=pull_request_id
439 pull_request=pull_request_id
441 )
440 )
442
441
443 if close_pr:
442 if close_pr:
444 if status in ['rejected', 'approved']:
443 if status in ['rejected', 'approved']:
445 PullRequestModel().close_pull_request(pull_request_id)
444 PullRequestModel().close_pull_request(pull_request_id)
446 action_logger(self.rhodecode_user,
445 action_logger(self.rhodecode_user,
447 'user_closed_pull_request:%s' % pull_request_id,
446 'user_closed_pull_request:%s' % pull_request_id,
448 c.rhodecode_db_repo, self.ip_addr, self.sa)
447 c.rhodecode_db_repo, self.ip_addr, self.sa)
449 else:
448 else:
450 h.flash(_('Closing pull request on other statuses than '
449 h.flash(_('Closing pull request on other statuses than '
451 'rejected or approved forbidden'),
450 'rejected or approved forbidden'),
452 category='warning')
451 category='warning')
453
452
454 Session().commit()
453 Session().commit()
455
454
456 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
455 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
457 return redirect(h.url('pullrequest_show', repo_name=repo_name,
456 return redirect(h.url('pullrequest_show', repo_name=repo_name,
458 pull_request_id=pull_request_id))
457 pull_request_id=pull_request_id))
459
458
460 data = {
459 data = {
461 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
460 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
462 }
461 }
463 if comm:
462 if comm:
464 c.co = comm
463 c.co = comm
465 data.update(comm.get_dict())
464 data.update(comm.get_dict())
466 data.update({'rendered_text':
465 data.update({'rendered_text':
467 render('changeset/changeset_comment_block.html')})
466 render('changeset/changeset_comment_block.html')})
468
467
469 return data
468 return data
470
469
471 @NotAnonymous()
470 @NotAnonymous()
472 @jsonify
471 @jsonify
473 def delete_comment(self, repo_name, comment_id):
472 def delete_comment(self, repo_name, comment_id):
474 co = ChangesetComment.get(comment_id)
473 co = ChangesetComment.get(comment_id)
475 if co.pull_request.is_closed():
474 if co.pull_request.is_closed():
476 #don't allow deleting comments on closed pull request
475 #don't allow deleting comments on closed pull request
477 raise HTTPForbidden()
476 raise HTTPForbidden()
478
477
479 owner = co.author.user_id == c.rhodecode_user.user_id
478 owner = co.author.user_id == c.rhodecode_user.user_id
480 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
479 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
481 ChangesetCommentsModel().delete(comment=co)
480 ChangesetCommentsModel().delete(comment=co)
482 Session().commit()
481 Session().commit()
483 return True
482 return True
484 else:
483 else:
485 raise HTTPForbidden()
484 raise HTTPForbidden()
@@ -1,445 +1,445 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 ))
109 ))
110
110
111 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
111 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
112 response.mustcontain("""Showing 2 commits""")
112 response.mustcontain("""Showing 2 commits""")
113 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
113 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
114
114
115 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
115 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>""")
116 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
117
117
118 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
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">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
119 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
120 ## files
120 ## 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))
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 #swap
122 #swap
123 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?as_form=None&amp;other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name))
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
124
125 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
125 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
126 self.log_user()
126 self.log_user()
127
127
128 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
128 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
129 description='diff-test',
129 description='diff-test',
130 owner=TEST_USER_ADMIN_LOGIN)
130 owner=TEST_USER_ADMIN_LOGIN)
131 Session().commit()
131 Session().commit()
132 self.r1_id = repo1.repo_id
132 self.r1_id = repo1.repo_id
133
133
134 #commit something !
134 #commit something !
135 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
135 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
136 message='commit1', vcs_type='hg', parent=None, newfile=True)
136 message='commit1', vcs_type='hg', parent=None, newfile=True)
137
137
138 #fork this repo
138 #fork this repo
139 repo2 = _fork_repo('one-fork', 'hg', parent='one')
139 repo2 = _fork_repo('one-fork', 'hg', parent='one')
140 self.r2_id = repo2.repo_id
140 self.r2_id = repo2.repo_id
141
141
142 #now commit something to origin repo
142 #now commit something to origin repo
143 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
143 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
144 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
144 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
145
145
146 #add two extra commit into fork
146 #add two extra commit into fork
147 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
147 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
148 message='commit2', vcs_type='hg', parent=cs0)
148 message='commit2', vcs_type='hg', parent=cs0)
149
149
150 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
150 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
151 message='commit3', vcs_type='hg', parent=cs1)
151 message='commit3', vcs_type='hg', parent=cs1)
152
152
153 rev1 = 'default'
153 rev1 = 'default'
154 rev2 = 'default'
154 rev2 = 'default'
155
155
156 response = self.app.get(url(controller='compare', action='index',
156 response = self.app.get(url(controller='compare', action='index',
157 repo_name=repo1.repo_name,
157 repo_name=repo1.repo_name,
158 org_ref_type="branch",
158 org_ref_type="branch",
159 org_ref=rev2,
159 org_ref=rev2,
160 other_repo=repo2.repo_name,
160 other_repo=repo2.repo_name,
161 other_ref_type="branch",
161 other_ref_type="branch",
162 other_ref=rev1,
162 other_ref=rev1,
163 ))
163 ))
164 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
164 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
165 response.mustcontain("""Showing 2 commits""")
165 response.mustcontain("""Showing 2 commits""")
166 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
166 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
167
167
168 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
168 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>""")
169 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
170
170
171 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
171 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))
172 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
173 ## files
173 ## 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))
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))
175 #swap
175 #swap
176 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?as_form=None&amp;other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name))
176 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name))
177
177
178 def test_compare_cherry_pick_changesets_from_bottom(self):
178 def test_compare_cherry_pick_changesets_from_bottom(self):
179
179
180 # repo1:
180 # repo1:
181 # cs0:
181 # cs0:
182 # cs1:
182 # cs1:
183 # repo1-fork- in which we will cherry pick bottom changesets
183 # repo1-fork- in which we will cherry pick bottom changesets
184 # cs0:
184 # cs0:
185 # cs1:
185 # cs1:
186 # cs2: x
186 # cs2: x
187 # cs3: x
187 # cs3: x
188 # cs4: x
188 # cs4: x
189 # cs5:
189 # cs5:
190 #make repo1, and cs1+cs2
190 #make repo1, and cs1+cs2
191 self.log_user()
191 self.log_user()
192
192
193 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
193 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
194 description='diff-test',
194 description='diff-test',
195 owner=TEST_USER_ADMIN_LOGIN)
195 owner=TEST_USER_ADMIN_LOGIN)
196 Session().commit()
196 Session().commit()
197 self.r1_id = repo1.repo_id
197 self.r1_id = repo1.repo_id
198
198
199 #commit something !
199 #commit something !
200 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
200 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
201 message='commit1', vcs_type='hg', parent=None,
201 message='commit1', vcs_type='hg', parent=None,
202 newfile=True)
202 newfile=True)
203 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
203 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
204 message='commit2', vcs_type='hg', parent=cs0)
204 message='commit2', vcs_type='hg', parent=cs0)
205 #fork this repo
205 #fork this repo
206 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
206 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
207 self.r2_id = repo2.repo_id
207 self.r2_id = repo2.repo_id
208 #now make cs3-6
208 #now make cs3-6
209 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
209 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
210 message='commit3', vcs_type='hg', parent=cs1)
210 message='commit3', vcs_type='hg', parent=cs1)
211 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
211 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
212 message='commit4', vcs_type='hg', parent=cs2)
212 message='commit4', vcs_type='hg', parent=cs2)
213 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
213 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
214 message='commit5', vcs_type='hg', parent=cs3)
214 message='commit5', vcs_type='hg', parent=cs3)
215 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
215 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
216 message='commit6', vcs_type='hg', parent=cs4)
216 message='commit6', vcs_type='hg', parent=cs4)
217
217
218 rev1 = 'tip'
218 rev1 = 'tip'
219 rev2 = 'tip'
219 rev2 = 'tip'
220
220
221 response = self.app.get(url(controller='compare', action='index',
221 response = self.app.get(url(controller='compare', action='index',
222 repo_name=repo2.repo_name,
222 repo_name=repo2.repo_name,
223 org_ref_type="tag",
223 org_ref_type="tag",
224 org_ref=rev1,
224 org_ref=rev1,
225 other_repo=repo1.repo_name,
225 other_repo=repo1.repo_name,
226 other_ref_type="tag",
226 other_ref_type="tag",
227 other_ref=rev2,
227 other_ref=rev2,
228 rev_start=cs2.raw_id,
228 rev_start=cs2.raw_id,
229 rev_end=cs4.raw_id,
229 rev_end=cs4.raw_id,
230 ))
230 ))
231 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs2.short_id, repo1.repo_name, cs4.short_id))
231 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs2.short_id, repo1.repo_name, cs4.short_id))
232 response.mustcontain("""Showing 3 commits""")
232 response.mustcontain("""Showing 3 commits""")
233 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
233 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
234
234
235 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
235 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
236 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
236 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
237 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
237 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
238
238
239 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo1.repo_name, cs2.raw_id, cs2.short_id))
239 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo1.repo_name, cs2.raw_id, cs2.short_id))
240 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
240 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
241 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
241 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
242 ## files
242 ## files
243 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
243 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
244
244
245 def test_compare_cherry_pick_changesets_from_top(self):
245 def test_compare_cherry_pick_changesets_from_top(self):
246 # repo1:
246 # repo1:
247 # cs0:
247 # cs0:
248 # cs1:
248 # cs1:
249 # repo1-fork- in which we will cherry pick bottom changesets
249 # repo1-fork- in which we will cherry pick bottom changesets
250 # cs0:
250 # cs0:
251 # cs1:
251 # cs1:
252 # cs2:
252 # cs2:
253 # cs3: x
253 # cs3: x
254 # cs4: x
254 # cs4: x
255 # cs5: x
255 # cs5: x
256 #
256 #
257 #make repo1, and cs1+cs2
257 #make repo1, and cs1+cs2
258 self.log_user()
258 self.log_user()
259 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
259 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
260 description='diff-test',
260 description='diff-test',
261 owner=TEST_USER_ADMIN_LOGIN)
261 owner=TEST_USER_ADMIN_LOGIN)
262 Session().commit()
262 Session().commit()
263 self.r1_id = repo1.repo_id
263 self.r1_id = repo1.repo_id
264
264
265 #commit something !
265 #commit something !
266 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
266 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
267 message='commit1', vcs_type='hg', parent=None,
267 message='commit1', vcs_type='hg', parent=None,
268 newfile=True)
268 newfile=True)
269 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
269 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
270 message='commit2', vcs_type='hg', parent=cs0)
270 message='commit2', vcs_type='hg', parent=cs0)
271 #fork this repo
271 #fork this repo
272 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
272 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
273 self.r2_id = repo2.repo_id
273 self.r2_id = repo2.repo_id
274 #now make cs3-6
274 #now make cs3-6
275 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
275 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
276 message='commit3', vcs_type='hg', parent=cs1)
276 message='commit3', vcs_type='hg', parent=cs1)
277 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
277 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
278 message='commit4', vcs_type='hg', parent=cs2)
278 message='commit4', vcs_type='hg', parent=cs2)
279 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
279 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
280 message='commit5', vcs_type='hg', parent=cs3)
280 message='commit5', vcs_type='hg', parent=cs3)
281 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
281 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
282 message='commit6', vcs_type='hg', parent=cs4)
282 message='commit6', vcs_type='hg', parent=cs4)
283 rev1 = 'tip'
283 rev1 = 'tip'
284 rev2 = 'tip'
284 rev2 = 'tip'
285
285
286 response = self.app.get(url(controller='compare', action='index',
286 response = self.app.get(url(controller='compare', action='index',
287 repo_name=repo2.repo_name,
287 repo_name=repo2.repo_name,
288 org_ref_type="tag",
288 org_ref_type="tag",
289 org_ref=rev1,
289 org_ref=rev1,
290 other_repo=repo1.repo_name,
290 other_repo=repo1.repo_name,
291 other_ref_type="tag",
291 other_ref_type="tag",
292 other_ref=rev2,
292 other_ref=rev2,
293 rev_start=cs3.raw_id,
293 rev_start=cs3.raw_id,
294 rev_end=cs5.raw_id,
294 rev_end=cs5.raw_id,
295 ))
295 ))
296
296
297 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs3.short_id, repo1.repo_name, cs5.short_id))
297 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs3.short_id, repo1.repo_name, cs5.short_id))
298 response.mustcontain("""Showing 3 commits""")
298 response.mustcontain("""Showing 3 commits""")
299 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
299 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
300
300
301 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
301 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
302 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
302 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
303 response.mustcontain("""<div class="message tooltip" title="commit6" style="white-space:normal">commit6</div>""")
303 response.mustcontain("""<div class="message tooltip" title="commit6" style="white-space:normal">commit6</div>""")
304
304
305 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
305 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
306 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
306 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
307 response.mustcontain("""<a href="/%s/changeset/%s">r5:%s</a>""" % (repo1.repo_name, cs5.raw_id, cs5.short_id))
307 response.mustcontain("""<a href="/%s/changeset/%s">r5:%s</a>""" % (repo1.repo_name, cs5.raw_id, cs5.short_id))
308 ## files
308 ## files
309 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
309 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
310
310
311 def test_compare_cherry_pick_changeset_mixed_branches(self):
311 def test_compare_cherry_pick_changeset_mixed_branches(self):
312 """
312 """
313
313
314 """
314 """
315 pass
315 pass
316 #TODO write this tastecase
316 #TODO write this tastecase
317
317
318 def test_compare_remote_branches_hg(self):
318 def test_compare_remote_branches_hg(self):
319 self.log_user()
319 self.log_user()
320
320
321 repo2 = _fork_repo(HG_FORK, 'hg')
321 repo2 = _fork_repo(HG_FORK, 'hg')
322 self.r2_id = repo2.repo_id
322 self.r2_id = repo2.repo_id
323 rev1 = '56349e29c2af'
323 rev1 = '56349e29c2af'
324 rev2 = '7d4bc8ec6be5'
324 rev2 = '7d4bc8ec6be5'
325
325
326 response = self.app.get(url(controller='compare', action='index',
326 response = self.app.get(url(controller='compare', action='index',
327 repo_name=HG_REPO,
327 repo_name=HG_REPO,
328 org_ref_type="rev",
328 org_ref_type="rev",
329 org_ref=rev1,
329 org_ref=rev1,
330 other_ref_type="rev",
330 other_ref_type="rev",
331 other_ref=rev2,
331 other_ref=rev2,
332 other_repo=HG_FORK,
332 other_repo=HG_FORK,
333 ))
333 ))
334 response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
334 response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
335 ## outgoing changesets between those revisions
335 ## outgoing changesets between those revisions
336
336
337 response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_FORK))
337 response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_FORK))
338 response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_FORK))
338 response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_FORK))
339 response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_FORK, rev2))
339 response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_FORK, rev2))
340
340
341 ## files
341 ## files
342 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))
342 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))
343 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))
343 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))
344 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))
344 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))
345
345
346 def test_org_repo_new_commits_after_forking_simple_diff(self):
346 def test_org_repo_new_commits_after_forking_simple_diff(self):
347 self.log_user()
347 self.log_user()
348
348
349 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
349 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
350 description='diff-test',
350 description='diff-test',
351 owner=TEST_USER_ADMIN_LOGIN)
351 owner=TEST_USER_ADMIN_LOGIN)
352
352
353 Session().commit()
353 Session().commit()
354 self.r1_id = repo1.repo_id
354 self.r1_id = repo1.repo_id
355 r1_name = repo1.repo_name
355 r1_name = repo1.repo_name
356
356
357 #commit something initially !
357 #commit something initially !
358 cs0 = ScmModel().create_node(
358 cs0 = ScmModel().create_node(
359 repo=repo1.scm_instance, repo_name=r1_name,
359 repo=repo1.scm_instance, repo_name=r1_name,
360 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
360 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
361 author=TEST_USER_ADMIN_LOGIN,
361 author=TEST_USER_ADMIN_LOGIN,
362 message='commit1',
362 message='commit1',
363 content='line1',
363 content='line1',
364 f_path='file1'
364 f_path='file1'
365 )
365 )
366 Session().commit()
366 Session().commit()
367 self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
367 self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
368 #fork the repo1
368 #fork the repo1
369 repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
369 repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
370 description='compare-test',
370 description='compare-test',
371 clone_uri=repo1.repo_full_path,
371 clone_uri=repo1.repo_full_path,
372 owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
372 owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
373 Session().commit()
373 Session().commit()
374 self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
374 self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
375 self.r2_id = repo2.repo_id
375 self.r2_id = repo2.repo_id
376 r2_name = repo2.repo_name
376 r2_name = repo2.repo_name
377
377
378 #make 3 new commits in fork
378 #make 3 new commits in fork
379 cs1 = ScmModel().create_node(
379 cs1 = ScmModel().create_node(
380 repo=repo2.scm_instance, repo_name=r2_name,
380 repo=repo2.scm_instance, repo_name=r2_name,
381 cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
381 cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
382 author=TEST_USER_ADMIN_LOGIN,
382 author=TEST_USER_ADMIN_LOGIN,
383 message='commit1-fork',
383 message='commit1-fork',
384 content='file1-line1-from-fork',
384 content='file1-line1-from-fork',
385 f_path='file1-fork'
385 f_path='file1-fork'
386 )
386 )
387 cs2 = ScmModel().create_node(
387 cs2 = ScmModel().create_node(
388 repo=repo2.scm_instance, repo_name=r2_name,
388 repo=repo2.scm_instance, repo_name=r2_name,
389 cs=cs1, user=TEST_USER_ADMIN_LOGIN,
389 cs=cs1, user=TEST_USER_ADMIN_LOGIN,
390 author=TEST_USER_ADMIN_LOGIN,
390 author=TEST_USER_ADMIN_LOGIN,
391 message='commit2-fork',
391 message='commit2-fork',
392 content='file2-line1-from-fork',
392 content='file2-line1-from-fork',
393 f_path='file2-fork'
393 f_path='file2-fork'
394 )
394 )
395 cs3 = ScmModel().create_node(
395 cs3 = ScmModel().create_node(
396 repo=repo2.scm_instance, repo_name=r2_name,
396 repo=repo2.scm_instance, repo_name=r2_name,
397 cs=cs2, user=TEST_USER_ADMIN_LOGIN,
397 cs=cs2, user=TEST_USER_ADMIN_LOGIN,
398 author=TEST_USER_ADMIN_LOGIN,
398 author=TEST_USER_ADMIN_LOGIN,
399 message='commit3-fork',
399 message='commit3-fork',
400 content='file3-line1-from-fork',
400 content='file3-line1-from-fork',
401 f_path='file3-fork'
401 f_path='file3-fork'
402 )
402 )
403
403
404 #compare !
404 #compare !
405 rev1 = 'default'
405 rev1 = 'default'
406 rev2 = 'default'
406 rev2 = 'default'
407
407
408 response = self.app.get(url(controller='compare', action='index',
408 response = self.app.get(url(controller='compare', action='index',
409 repo_name=r2_name,
409 repo_name=r2_name,
410 org_ref_type="branch",
410 org_ref_type="branch",
411 org_ref=rev1,
411 org_ref=rev1,
412 other_ref_type="branch",
412 other_ref_type="branch",
413 other_ref=rev2,
413 other_ref=rev2,
414 other_repo=r1_name,
414 other_repo=r1_name,
415 ))
415 ))
416 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
416 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
417 response.mustcontain('No files')
417 response.mustcontain('No files')
418 response.mustcontain('No changesets')
418 response.mustcontain('No changesets')
419
419
420 #add new commit into parent !
420 #add new commit into parent !
421 cs0 = ScmModel().create_node(
421 cs0 = ScmModel().create_node(
422 repo=repo1.scm_instance, repo_name=r1_name,
422 repo=repo1.scm_instance, repo_name=r1_name,
423 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
423 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
424 author=TEST_USER_ADMIN_LOGIN,
424 author=TEST_USER_ADMIN_LOGIN,
425 message='commit2-parent',
425 message='commit2-parent',
426 content='line1-added-after-fork',
426 content='line1-added-after-fork',
427 f_path='file2'
427 f_path='file2'
428 )
428 )
429 #compare !
429 #compare !
430 rev1 = 'default'
430 rev1 = 'default'
431 rev2 = 'default'
431 rev2 = 'default'
432 response = self.app.get(url(controller='compare', action='index',
432 response = self.app.get(url(controller='compare', action='index',
433 repo_name=r2_name,
433 repo_name=r2_name,
434 org_ref_type="branch",
434 org_ref_type="branch",
435 org_ref=rev1,
435 org_ref=rev1,
436 other_ref_type="branch",
436 other_ref_type="branch",
437 other_ref=rev2,
437 other_ref=rev2,
438 other_repo=r1_name,
438 other_repo=r1_name,
439 ))
439 ))
440
440
441 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
441 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
442
442
443 response.mustcontain("""commit2-parent""")
443 response.mustcontain("""commit2-parent""")
444 response.mustcontain("""1 file changed with 1 insertions and 0 deletions""")
444 response.mustcontain("""1 file changed with 1 insertions and 0 deletions""")
445 response.mustcontain("""line1-added-after-fork""")
445 response.mustcontain("""line1-added-after-fork""")
General Comments 0
You need to be logged in to leave comments. Login now