Merge lp:~cjwatson/launchpad/git-mp-move-outside-ref into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17527
Proposed branch: lp:~cjwatson/launchpad/git-mp-move-outside-ref
Merge into: lp:launchpad
Diff against target: 260 lines (+76/-15)
13 files modified
lib/lp/code/browser/branch.py (+1/-3)
lib/lp/code/browser/configure.zcml (+1/-1)
lib/lp/code/browser/gitref.py (+5/-3)
lib/lp/code/browser/gitrepository.py (+11/-0)
lib/lp/code/interfaces/branch.py (+3/-0)
lib/lp/code/interfaces/branchmergeproposal.py (+4/-0)
lib/lp/code/interfaces/gitref.py (+3/-0)
lib/lp/code/interfaces/gitrepository.py (+3/-0)
lib/lp/code/model/branch.py (+7/-0)
lib/lp/code/model/branchmergeproposal.py (+7/-0)
lib/lp/code/model/gitref.py (+4/-0)
lib/lp/code/model/gitrepository.py (+4/-0)
lib/lp/code/model/tests/test_branchmergeproposal.py (+23/-8)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-mp-move-outside-ref
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+260105@code.launchpad.net

Commit message

Place Git-based merge proposals under their source repository, not the (removable) source reference.

Description of the change

Place Git-based merge proposals under their source repository, not the (removable) source reference.

I decided to leave a redirection around for a while even though it probably isn't very necessary, but we can remove that after a month or two.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/branch.py'
2--- lib/lp/code/browser/branch.py 2015-04-22 16:41:41 +0000
3+++ lib/lp/code/browser/branch.py 2015-05-26 12:18:43 +0000
4@@ -215,9 +215,7 @@
5 except ValueError:
6 # Not a number.
7 return None
8- for proposal in self.context.landing_targets:
9- if proposal.id == id:
10- return proposal
11+ return self.context.getMergeProposalByID(id)
12
13 @stepto("+code-import")
14 def traverse_code_import(self):
15
16=== modified file 'lib/lp/code/browser/configure.zcml'
17--- lib/lp/code/browser/configure.zcml 2015-05-19 11:31:59 +0000
18+++ lib/lp/code/browser/configure.zcml 2015-05-26 12:18:43 +0000
19@@ -253,7 +253,7 @@
20 <browser:url
21 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
22 path_expression="string:+merge/${id}"
23- attribute_to_parent="merge_source"
24+ attribute_to_parent="parent"
25 rootsite="code"/>
26 <browser:page
27 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposalListingBatchNavigator"
28
29=== modified file 'lib/lp/code/browser/gitref.py'
30--- lib/lp/code/browser/gitref.py 2015-05-05 10:15:19 +0000
31+++ lib/lp/code/browser/gitref.py 2015-05-26 12:18:43 +0000
32@@ -56,6 +56,8 @@
33 from lp.services.webapp.authorization import check_permission
34
35
36+# XXX cjwatson 2015-05-26: We can get rid of this after a short while, since
37+# it's just a compatibility redirection.
38 class GitRefNavigation(Navigation):
39
40 usedfor = IGitRef
41@@ -68,9 +70,9 @@
42 except ValueError:
43 # Not a number.
44 return None
45- for proposal in self.context.landing_targets:
46- if proposal.id == id:
47- return proposal
48+ proposal = self.context.getMergeProposalByID(id)
49+ if proposal is not None:
50+ return self.redirectSubTree(canonical_url(proposal))
51
52
53 class GitRefContextMenu(ContextMenu):
54
55=== modified file 'lib/lp/code/browser/gitrepository.py'
56--- lib/lp/code/browser/gitrepository.py 2015-05-19 11:31:59 +0000
57+++ lib/lp/code/browser/gitrepository.py 2015-05-26 12:18:43 +0000
58@@ -37,6 +37,7 @@
59 Link,
60 Navigation,
61 NavigationMenu,
62+ stepthrough,
63 stepto,
64 )
65 from lp.services.webapp.authorization import (
66@@ -88,6 +89,16 @@
67 return ref
68 raise NotFoundError
69
70+ @stepthrough("+merge")
71+ def traverse_merge_proposal(self, id):
72+ """Traverse to an `IBranchMergeProposal`."""
73+ try:
74+ id = int(id)
75+ except ValueError:
76+ # Not a number.
77+ return None
78+ return self.context.getMergeProposalByID(id)
79+
80
81 class GitRepositoryEditMenu(NavigationMenu):
82 """Edit menu for `IGitRepository`."""
83
84=== modified file 'lib/lp/code/interfaces/branch.py'
85--- lib/lp/code/interfaces/branch.py 2015-05-19 11:27:02 +0000
86+++ lib/lp/code/interfaces/branch.py 2015-05-26 12:18:43 +0000
87@@ -649,6 +649,9 @@
88 merged_revnos=None, eager_load=False):
89 """Return matching BranchMergeProposals."""
90
91+ def getMergeProposalByID(id):
92+ """Return this branch's merge proposal with this id, or None."""
93+
94 def scheduleDiffUpdates():
95 """Create UpdatePreviewDiffJobs for this branch's targets."""
96
97
98=== modified file 'lib/lp/code/interfaces/branchmergeproposal.py'
99--- lib/lp/code/interfaces/branchmergeproposal.py 2015-04-22 14:52:10 +0000
100+++ lib/lp/code/interfaces/branchmergeproposal.py 2015-05-26 12:18:43 +0000
101@@ -215,6 +215,10 @@
102 merge_prerequisite = Attribute(
103 "The branch that the source branch branched from (VCS-agnostic).")
104
105+ parent = Attribute(
106+ "The parent object for use in navigation: source branch for Bazaar, "
107+ "or source repository for Git.")
108+
109
110 class IBranchMergeProposalView(Interface):
111
112
113=== modified file 'lib/lp/code/interfaces/gitref.py'
114--- lib/lp/code/interfaces/gitref.py 2015-05-21 16:36:14 +0000
115+++ lib/lp/code/interfaces/gitref.py 2015-05-26 12:18:43 +0000
116@@ -285,6 +285,9 @@
117 merged_revision_ids=None, eager_load=False):
118 """Return matching BranchMergeProposals."""
119
120+ def getMergeProposalByID(id):
121+ """Return this reference's merge proposal with this id, or None."""
122+
123 pending_writes = Attribute(
124 "Whether there are recent changes in this repository that have not "
125 "yet been scanned.")
126
127=== modified file 'lib/lp/code/interfaces/gitrepository.py'
128--- lib/lp/code/interfaces/gitrepository.py 2015-05-19 11:29:24 +0000
129+++ lib/lp/code/interfaces/gitrepository.py 2015-05-26 12:18:43 +0000
130@@ -461,6 +461,9 @@
131 "A collection of the merge proposals that are dependent on this "
132 "repository.")
133
134+ def getMergeProposalByID(id):
135+ """Return this repository's merge proposal with this id, or None."""
136+
137 def isRepositoryMergeable(other):
138 """Is the other repository mergeable into this one (or vice versa)?"""
139
140
141=== modified file 'lib/lp/code/model/branch.py'
142--- lib/lp/code/model/branch.py 2015-05-19 11:27:02 +0000
143+++ lib/lp/code/model/branch.py 2015-05-26 12:18:43 +0000
144@@ -502,6 +502,13 @@
145 status, target_branch=self, merged_revnos=merged_revnos,
146 eager_load=eager_load)
147
148+ def getMergeProposalByID(self, id):
149+ """See `IBranch`."""
150+ return Store.of(self).find(
151+ BranchMergeProposal,
152+ BranchMergeProposal.id == id,
153+ BranchMergeProposal.source_branch == self).one()
154+
155 def isBranchMergeable(self, target_branch):
156 """See `IBranch`."""
157 # In some imaginary time we may actually check to see if this branch
158
159=== modified file 'lib/lp/code/model/branchmergeproposal.py'
160--- lib/lp/code/model/branchmergeproposal.py 2015-05-13 09:36:02 +0000
161+++ lib/lp/code/model/branchmergeproposal.py 2015-05-26 12:18:43 +0000
162@@ -283,6 +283,13 @@
163 else:
164 return self.prerequisite_git_ref
165
166+ @property
167+ def parent(self):
168+ if self.source_branch is not None:
169+ return self.source_branch
170+ else:
171+ return self.source_git_repository
172+
173 description = StringCol(default=None)
174
175 whiteboard = StringCol(default=None)
176
177=== modified file 'lib/lp/code/model/gitref.py'
178--- lib/lp/code/model/gitref.py 2015-05-21 16:36:14 +0000
179+++ lib/lp/code/model/gitref.py 2015-05-26 12:18:43 +0000
180@@ -203,6 +203,10 @@
181 status, target_repository=self.repository, target_path=self.path,
182 merged_revision_ids=merged_revision_ids, eager_load=eager_load)
183
184+ def getMergeProposalByID(self, id):
185+ """See `IGitRef`."""
186+ return self.landing_targets.find(BranchMergeProposal.id == id).one()
187+
188 @property
189 def pending_writes(self):
190 """See `IGitRef`."""
191
192=== modified file 'lib/lp/code/model/gitrepository.py'
193--- lib/lp/code/model/gitrepository.py 2015-05-23 16:29:29 +0000
194+++ lib/lp/code/model/gitrepository.py 2015-05-26 12:18:43 +0000
195@@ -791,6 +791,10 @@
196 Not(BranchMergeProposal.queue_status.is_in(
197 BRANCH_MERGE_PROPOSAL_FINAL_STATES)))
198
199+ def getMergeProposalByID(self, id):
200+ """See `IGitRepository`."""
201+ return self.landing_targets.find(BranchMergeProposal.id == id).one()
202+
203 def isRepositoryMergeable(self, other):
204 """See `IGitRepository`."""
205 return self.namespace.areRepositoriesMergeable(other.namespace)
206
207=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
208--- lib/lp/code/model/tests/test_branchmergeproposal.py 2015-04-21 16:53:45 +0000
209+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2015-05-26 12:18:43 +0000
210@@ -96,28 +96,43 @@
211 verifyObject(IBranchMergeProposal, bmp)
212
213
214-class TestBranchMergeProposalCanonicalUrl(TestCaseWithFactory):
215+class TestBranchMergeProposalCanonicalUrlMixin:
216 """Tests canonical_url for merge proposals."""
217
218 layer = DatabaseFunctionalLayer
219
220 def test_BranchMergeProposal_canonical_url_base(self):
221- # The URL for a merge proposal starts with the source branch.
222- bmp = self.factory.makeBranchMergeProposal()
223+ # The URL for a merge proposal starts with the parent (source branch
224+ # or source Git repository).
225+ bmp = self._makeBranchMergeProposal()
226 url = canonical_url(bmp)
227- source_branch_url = canonical_url(bmp.source_branch)
228- self.assertTrue(url.startswith(source_branch_url))
229+ parent_url = canonical_url(bmp.parent)
230+ self.assertTrue(url.startswith(parent_url))
231
232 def test_BranchMergeProposal_canonical_url_rest(self):
233 # The rest of the URL for a merge proposal is +merge followed by the
234 # db id.
235- bmp = self.factory.makeBranchMergeProposal()
236+ bmp = self._makeBranchMergeProposal()
237 url = canonical_url(bmp)
238- source_branch_url = canonical_url(bmp.source_branch)
239- rest = url[len(source_branch_url):]
240+ parent_url = canonical_url(bmp.parent)
241+ rest = url[len(parent_url):]
242 self.assertEqual('/+merge/%s' % bmp.id, rest)
243
244
245+class TestBranchMergeProposalCanonicalUrlBzr(
246+ TestBranchMergeProposalCanonicalUrlMixin, TestCaseWithFactory):
247+
248+ def _makeBranchMergeProposal(self):
249+ return self.factory.makeBranchMergeProposal()
250+
251+
252+class TestBranchMergeProposalCanonicalUrlGit(
253+ TestBranchMergeProposalCanonicalUrlMixin, TestCaseWithFactory):
254+
255+ def _makeBranchMergeProposal(self):
256+ return self.factory.makeBranchMergeProposalForGit()
257+
258+
259 class TestBranchMergeProposalPrivacy(TestCaseWithFactory):
260 """Ensure that BranchMergeProposal implements privacy."""
261