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