Merge lp:~jelmer/launchpad/no-code-import-approval into lp:launchpad

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/launchpad/no-code-import-approval
Merge into: lp:launchpad
Diff against target: 557 lines (+77/-122)
12 files modified
lib/lp/code/doc/codeimport-event.txt (+5/-5)
lib/lp/code/doc/codeimport.txt (+1/-5)
lib/lp/code/model/codeimport.py (+4/-8)
lib/lp/code/model/tests/test_branch.py (+0/-2)
lib/lp/code/model/tests/test_codeimport.py (+12/-40)
lib/lp/code/model/tests/test_codeimportjob.py (+16/-11)
lib/lp/code/stories/codeimport/xx-admin-codeimport.txt (+3/-20)
lib/lp/code/stories/codeimport/xx-create-codeimport.txt (+9/-4)
lib/lp/code/stories/codeimport/xx-edit-codeimport.txt (+3/-1)
lib/lp/code/stories/webservice/xx-code-import.txt (+2/-2)
lib/lp/testing/factory.py (+11/-13)
utilities/sourcedeps.cache (+11/-11)
To merge this branch: bzr merge lp:~jelmer/launchpad/no-code-import-approval
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+73186@code.launchpad.net

This proposal has been superseded by a proposal from 2011-08-28.

Description of the change

Remove the requirement for svn and cvs imports to be pre-approved by a Launchpad admin, fixing bug #418932.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/doc/codeimport-event.txt'
2--- lib/lp/code/doc/codeimport-event.txt 2010-10-18 22:24:59 +0000
3+++ lib/lp/code/doc/codeimport-event.txt 2011-08-28 19:23:29 +0000
4@@ -120,7 +120,7 @@
5 >>> print_items(svn_create_event)
6 CODE_IMPORT <muted>
7 OWNER ...
8- REVIEW_STATUS u'NEW'
9+ REVIEW_STATUS u'REVIEWED'
10 ASSIGNEE None
11 UPDATE_INTERVAL None
12 URL u'svn://svn.example.com/trunk'
13@@ -144,7 +144,7 @@
14 >>> print_items(cvs_create_event)
15 CODE_IMPORT <muted>
16 OWNER ...
17- REVIEW_STATUS u'NEW'
18+ REVIEW_STATUS u'REVIEWED'
19 ASSIGNEE None
20 UPDATE_INTERVAL None
21 CVS_ROOT u':pserver:anonymous@cvs.example.com:/cvsroot'
22@@ -206,7 +206,7 @@
23
24 >>> from lp.code.enums import CodeImportReviewStatus
25 >>> removeSecurityProxy(svn_import).review_status = (
26- ... CodeImportReviewStatus.REVIEWED)
27+ ... CodeImportReviewStatus.SUSPENDED)
28
29 After applying changes, the newModify method can create an event that
30 details the changes that have been applied.
31@@ -234,8 +234,8 @@
32 >>> print_items(modify_event)
33 CODE_IMPORT <muted>
34 OWNER ...
35- REVIEW_STATUS u'REVIEWED'
36- OLD_REVIEW_STATUS u'NEW'
37+ REVIEW_STATUS u'SUSPENDED'
38+ OLD_REVIEW_STATUS u'REVIEWED'
39 ASSIGNEE None
40 UPDATE_INTERVAL None
41 URL u'svn://svn.example.com/trunk'
42
43=== modified file 'lib/lp/code/doc/codeimport.txt'
44--- lib/lp/code/doc/codeimport.txt 2011-06-28 17:13:42 +0000
45+++ lib/lp/code/doc/codeimport.txt 2011-08-28 19:23:29 +0000
46@@ -243,7 +243,7 @@
47 >>> code_import = factory.makeProductCodeImport(
48 ... svn_branch_url='http://svn.example.com/project')
49 >>> print code_import.review_status.title
50- Pending Review
51+ Reviewed
52
53 When an import operator updates the code import emails are sent out to
54 the branch subscribers and members of VCS Imports that describe the
55@@ -275,8 +275,6 @@
56 To: david.allouche@canonical.com, ...
57 Subject: Code import product.../name... status: Reviewed
58 <BLANKLINE>
59- The import has been approved and an import will start shortly.
60- <BLANKLINE>
61 ... is now being imported from:
62 http://svn.example.com/project/trunk
63 instead of:
64@@ -293,8 +291,6 @@
65 To: import@example.com
66 Subject: Code import product.../name... status: Reviewed
67 <BLANKLINE>
68- The import has been approved and an import will start shortly.
69- <BLANKLINE>
70 ... is now being imported from:
71 http://svn.example.com/project/trunk
72 instead of:
73
74=== modified file 'lib/lp/code/model/codeimport.py'
75--- lib/lp/code/model/codeimport.py 2011-08-24 21:17:35 +0000
76+++ lib/lp/code/model/codeimport.py 2011-08-28 19:23:29 +0000
77@@ -198,7 +198,8 @@
78 setattr(self, name, value)
79 if 'review_status' in data:
80 if data['review_status'] == CodeImportReviewStatus.REVIEWED:
81- CodeImportJobWorkflow().newJob(self)
82+ if self.import_job is None:
83+ CodeImportJobWorkflow().newJob(self)
84 else:
85 self._removeJob()
86 event = event_set.newModify(self, user, token)
87@@ -264,13 +265,8 @@
88 "Don't know how to sanity check source details for unknown "
89 "rcs_type %s"%rcs_type)
90 if review_status is None:
91- # Auto approve git and hg imports.
92- if rcs_type in (
93- RevisionControlSystems.GIT, RevisionControlSystems.HG,
94- RevisionControlSystems.BZR):
95- review_status = CodeImportReviewStatus.REVIEWED
96- else:
97- review_status = CodeImportReviewStatus.NEW
98+ # Auto approve imports.
99+ review_status = CodeImportReviewStatus.REVIEWED
100 if not target.supports_code_imports:
101 raise AssertionError("%r doesn't support code imports" % target)
102 if owner is None:
103
104=== modified file 'lib/lp/code/model/tests/test_branch.py'
105--- lib/lp/code/model/tests/test_branch.py 2011-08-24 16:21:07 +0000
106+++ lib/lp/code/model/tests/test_branch.py 2011-08-28 19:23:29 +0000
107@@ -1510,7 +1510,6 @@
108 """break_links allows deleting a code import branch."""
109 code_import = self.factory.makeCodeImport()
110 code_import_id = code_import.id
111- self.factory.makeCodeImportJob(code_import)
112 code_import.branch.destroySelf(break_references=True)
113 self.assertRaises(
114 SQLObjectNotFound, CodeImport.get, code_import_id)
115@@ -1575,7 +1574,6 @@
116 """DeleteCodeImport.__call__ must delete the CodeImport."""
117 code_import = self.factory.makeCodeImport()
118 code_import_id = code_import.id
119- self.factory.makeCodeImportJob(code_import)
120 DeleteCodeImport(code_import)()
121 self.assertRaises(
122 SQLObjectNotFound, CodeImport.get, code_import_id)
123
124=== modified file 'lib/lp/code/model/tests/test_codeimport.py'
125--- lib/lp/code/model/tests/test_codeimport.py 2011-08-28 08:36:14 +0000
126+++ lib/lp/code/model/tests/test_codeimport.py 2011-08-28 19:23:29 +0000
127@@ -56,20 +56,6 @@
128
129 layer = DatabaseFunctionalLayer
130
131- def test_new_svn_import(self):
132- """A new subversion code import should have NEW status."""
133- code_import = CodeImportSet().new(
134- registrant=self.factory.makePerson(),
135- target=IBranchTarget(self.factory.makeProduct()),
136- branch_name='imported',
137- rcs_type=RevisionControlSystems.SVN,
138- url=self.factory.getUniqueURL())
139- self.assertEqual(
140- CodeImportReviewStatus.NEW,
141- code_import.review_status)
142- # No job is created for the import.
143- self.assertIs(None, code_import.import_job)
144-
145 def test_new_svn_import_svn_scheme(self):
146 """A subversion import can use the svn:// scheme."""
147 code_import = CodeImportSet().new(
148@@ -79,10 +65,10 @@
149 rcs_type=RevisionControlSystems.SVN,
150 url=self.factory.getUniqueURL(scheme="svn"))
151 self.assertEqual(
152- CodeImportReviewStatus.NEW,
153+ CodeImportReviewStatus.REVIEWED,
154 code_import.review_status)
155 # No job is created for the import.
156- self.assertIs(None, code_import.import_job)
157+ self.assertIsNot(None, code_import.import_job)
158
159 def test_reviewed_svn_import(self):
160 """A specific review status can be set for a new import."""
161@@ -92,30 +78,15 @@
162 branch_name='imported',
163 rcs_type=RevisionControlSystems.SVN,
164 url=self.factory.getUniqueURL(),
165- review_status=CodeImportReviewStatus.REVIEWED)
166+ review_status=None)
167 self.assertEqual(
168 CodeImportReviewStatus.REVIEWED,
169 code_import.review_status)
170 # A job is created for the import.
171 self.assertIsNot(None, code_import.import_job)
172
173- def test_new_cvs_import(self):
174- """A new CVS code import should have NEW status."""
175- code_import = CodeImportSet().new(
176- registrant=self.factory.makePerson(),
177- target=IBranchTarget(self.factory.makeProduct()),
178- branch_name='imported',
179- rcs_type=RevisionControlSystems.CVS,
180- cvs_root=self.factory.getUniqueURL(),
181- cvs_module='module')
182- self.assertEqual(
183- CodeImportReviewStatus.NEW,
184- code_import.review_status)
185- # No job is created for the import.
186- self.assertIs(None, code_import.import_job)
187-
188- def test_reviewed_cvs_import(self):
189- """A specific review status can be set for a new import."""
190+ def test_cvs_import_reviewed(self):
191+ """A new CVS code import should have REVIEWED status."""
192 code_import = CodeImportSet().new(
193 registrant=self.factory.makePerson(),
194 target=IBranchTarget(self.factory.makeProduct()),
195@@ -123,7 +94,7 @@
196 rcs_type=RevisionControlSystems.CVS,
197 cvs_root=self.factory.getUniqueURL(),
198 cvs_module='module',
199- review_status=CodeImportReviewStatus.REVIEWED)
200+ review_status=None)
201 self.assertEqual(
202 CodeImportReviewStatus.REVIEWED,
203 code_import.review_status)
204@@ -275,8 +246,7 @@
205 """Ensure deleting CodeImport objects deletes associated jobs."""
206 code_import = self.factory.makeCodeImport()
207 login_person(getUtility(ILaunchpadCelebrities).vcs_imports.teamowner)
208- code_import_job = self.factory.makeCodeImportJob(code_import)
209- job_id = code_import_job.id
210+ job_id = code_import.import_job.id
211 CodeImportJobSet().getById(job_id)
212 job = CodeImportJobSet().getById(job_id)
213 assert job is not None
214@@ -644,7 +614,8 @@
215 # tryFailingImportAgain only succeeds for imports that are FAILING.
216 outcomes = {}
217 for status in CodeImportReviewStatus.items:
218- code_import = self.factory.makeCodeImport()
219+ code_import = self.factory.makeCodeImport(
220+ review_status=CodeImportReviewStatus.NEW)
221 code_import.updateFromData(
222 {'review_status': status}, self.factory.makePerson())
223 try:
224@@ -726,9 +697,10 @@
225 self.assertEqual(requester, e.requesting_user)
226
227 def test_exception_on_disabled(self):
228- # get an SVN request, which isn't reviewed by default
229+ # get an SVN request which is suspended
230 code_import = self.factory.makeCodeImport(
231- svn_branch_url=self.factory.getUniqueURL())
232+ svn_branch_url=self.factory.getUniqueURL(),
233+ review_status=CodeImportReviewStatus.SUSPENDED)
234 requester = self.factory.makePerson()
235 # which leads to an exception if we try and ask for an import
236 self.assertRaises(
237
238=== modified file 'lib/lp/code/model/tests/test_codeimportjob.py'
239--- lib/lp/code/model/tests/test_codeimportjob.py 2011-06-29 12:00:39 +0000
240+++ lib/lp/code/model/tests/test_codeimportjob.py 2011-08-28 19:23:29 +0000
241@@ -115,7 +115,8 @@
242
243 def makeJob(self, state, date_due_delta, requesting_user=None):
244 """Create a CodeImportJob object from a spec."""
245- code_import = self.factory.makeCodeImport()
246+ code_import = self.factory.makeCodeImport(
247+ review_status=CodeImportReviewStatus.NEW)
248 job = self.factory.makeCodeImportJob(code_import)
249 if state == CodeImportJobState.RUNNING:
250 getUtility(ICodeImportJobWorkflow).startJob(job, self.machine)
251@@ -387,11 +388,12 @@
252 def test_wrongReviewStatus(self):
253 # CodeImportJobWorkflow.newJob fails if the CodeImport review_status
254 # is different from REVIEWED.
255- new_import = self.factory.makeCodeImport()
256+ new_import = self.factory.makeCodeImport(
257+ review_status=CodeImportReviewStatus.SUSPENDED)
258 branch_name = new_import.branch.unique_name
259 # Testing newJob failure.
260 self.assertFailure(
261- "Review status of %s is not REVIEWED: NEW" % (branch_name,),
262+ "Review status of %s is not REVIEWED: SUSPENDED" % (branch_name,),
263 getUtility(ICodeImportJobWorkflow).newJob, new_import)
264
265 def test_existingJob(self):
266@@ -417,14 +419,17 @@
267 # If there is no CodeImportResult for the CodeImport, then the new
268 # CodeImportJob has date_due set to UTC_NOW.
269 code_import = self.getCodeImportForDateDueTest()
270- job = getUtility(ICodeImportJobWorkflow).newJob(code_import)
271- self.assertSqlAttributeEqualsDate(job, 'date_due', UTC_NOW)
272+ self.assertSqlAttributeEqualsDate(code_import.import_job, 'date_due',
273+ UTC_NOW)
274
275 def test_dateDueRecentPreviousResult(self):
276 # If there is a CodeImportResult for the CodeImport that is more
277 # recent than the effective_update_interval, then the new
278 # CodeImportJob has date_due set in the future.
279 code_import = self.getCodeImportForDateDueTest()
280+ # A code import job is automatically started when a reviewed code import
281+ # is created. Remove it, so a "clean" one can be created later.
282+ removeSecurityProxy(code_import).import_job.destroySelf()
283 # Create a CodeImportResult that started a long time ago. This one
284 # must be superseded by the more recent one created below.
285 machine = self.factory.makeCodeImportMachine()
286@@ -469,8 +474,8 @@
287 date_job_started=datetime(2000, 1, 1, 12, 0, 0, tzinfo=UTC),
288 date_created=datetime(2000, 1, 1, 12, 5, 0, tzinfo=UTC))
289 # When we create the job, its date due must be set to UTC_NOW.
290- job = getUtility(ICodeImportJobWorkflow).newJob(code_import)
291- self.assertSqlAttributeEqualsDate(job, 'date_due', UTC_NOW)
292+ self.assertSqlAttributeEqualsDate(code_import.import_job, 'date_due',
293+ UTC_NOW)
294
295
296 class TestCodeImportJobWorkflowDeletePendingJob(TestCaseWithFactory,
297@@ -500,7 +505,8 @@
298 def test_noJob(self):
299 # CodeImportJobWorkflow.deletePendingJob fails if the
300 # CodeImport is not associated to a CodeImportJob.
301- new_import = self.factory.makeCodeImport()
302+ new_import = self.factory.makeCodeImport(
303+ review_status=CodeImportReviewStatus.NEW)
304 branch_name = new_import.branch.unique_name
305 # Testing deletePendingJob failure.
306 self.assertFailure(
307@@ -578,7 +584,7 @@
308 # CodeImportJobWorkflow.requestJob sets requesting_user and
309 # date_due if the current date_due is in the future.
310 code_import = self.factory.makeCodeImport()
311- pending_job = self.factory.makeCodeImportJob(code_import)
312+ pending_job = code_import.import_job
313 person = self.factory.makePerson()
314 # Set date_due in the future. ICodeImportJob does not allow setting
315 # date_due, so we must use removeSecurityProxy.
316@@ -877,8 +883,7 @@
317 unchecked_result_fields.difference_update(['log_file', 'status'])
318
319 code_import = self.factory.makeCodeImport()
320- removeSecurityProxy(code_import).review_status = \
321- CodeImportReviewStatus.REVIEWED
322+ removeSecurityProxy(code_import).import_job.destroySelf()
323 self.assertFinishJobPassesThroughJobField(
324 'code_import', 'code_import', code_import)
325 unchecked_result_fields.remove('code_import')
326
327=== modified file 'lib/lp/code/stories/codeimport/xx-admin-codeimport.txt'
328--- lib/lp/code/stories/codeimport/xx-admin-codeimport.txt 2010-04-28 02:49:58 +0000
329+++ lib/lp/code/stories/codeimport/xx-admin-codeimport.txt 2011-08-28 19:23:29 +0000
330@@ -61,9 +61,11 @@
331 ... print extract_text(div)
332 >>> import_browser.open(svn_import_location)
333 >>> print_import_details(import_browser)
334- Import Status: Pending Review
335+ Import Status: Reviewed
336 This branch is an import of the Subversion branch
337 from svn://svn.example.com/fooix/trunk.
338+ The next import is scheduled to run
339+ as soon as possible.
340 Edit import source or review import
341
342
343@@ -76,7 +78,6 @@
344 >>> import_browser.getLink('Edit import source or review import').click()
345 >>> print_submit_buttons(import_browser.contents)
346 Update
347- Approve
348 Mark Invalid
349 Suspend
350 Mark Failing
351@@ -190,24 +191,6 @@
352 The code import has been updated.
353
354
355-Approving an import
356-+++++++++++++++++++
357-
358-When a code import is approved, a pending job is created for it.
359-
360- >>> import_browser.open(svn_import_location + '/+edit-import')
361- >>> import_browser.getControl('Approve').click()
362- >>> print_import_details(import_browser)
363- Import Status: Reviewed
364- ...
365- The next import is scheduled to run as soon as possible.
366- Edit import source or review import
367-
368- >>> for message in get_feedback_messages(import_browser.contents):
369- ... print extract_text(message)
370- The code import has been approved.
371-
372-
373 Invalidating an import
374 ++++++++++++++++++++++
375
376
377=== modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
378--- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-09-28 19:25:54 +0000
379+++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2011-08-28 19:23:29 +0000
380@@ -72,9 +72,11 @@
381 When the user clicks continue, the import branch is created
382
383 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))
384- Import Status: Pending Review
385+ Import Status: Reviewed
386 This branch is an import of the Subversion branch
387 from http://svn.example.com/firefox/trunk.
388+ The next import is scheduled to run
389+ as soon as possible.
390 >>> browser.getLink("http://svn.example.com/firefox/trunk")
391 <Link text='http://svn.example.com/firefox/trunk'
392 url='http://svn.example.com/firefox/trunk'>
393@@ -98,9 +100,11 @@
394 >>> browser.getControl('Project').value = "firefox"
395 >>> browser.getControl('Request Import').click()
396 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))
397- Import Status: Pending Review
398+ Import Status: Reviewed
399 This branch is an import of the Subversion branch
400 from http://user:password@svn.example.com/firefox/trunk.
401+ The next import is scheduled to run
402+ as soon as possible.
403
404
405 Requesting a Git import
406@@ -165,10 +169,11 @@
407 >>> browser.getControl('Request Import').click()
408
409 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))
410- Import Status: Pending Review
411+ Import Status: Reviewed
412 This branch is an import of the CVS module firefox from
413 :pserver:anonymous@cvs.example.com:/mozilla/cvs.
414-
415+ The next import is scheduled to run
416+ as soon as possible.
417
418 Requesting a CVS import with invalid information
419 ================================================
420
421=== modified file 'lib/lp/code/stories/codeimport/xx-edit-codeimport.txt'
422--- lib/lp/code/stories/codeimport/xx-edit-codeimport.txt 2010-03-18 15:39:58 +0000
423+++ lib/lp/code/stories/codeimport/xx-edit-codeimport.txt 2011-08-28 19:23:29 +0000
424@@ -43,9 +43,11 @@
425 ... print extract_text(div)
426 >>> anon_browser.open(svn_import_location)
427 >>> print_import_details(anon_browser)
428- Import Status: Pending Review
429+ Import Status: Reviewed
430 This branch is an import of the Subversion branch
431 from svn://svn.example.com/fooix/trunk.
432+ The next import is scheduled to run
433+ as soon as possible.
434
435 Because it's an svn:// URL, it doesn't get linkified:
436
437
438=== modified file 'lib/lp/code/stories/webservice/xx-code-import.txt'
439--- lib/lp/code/stories/webservice/xx-code-import.txt 2010-04-16 05:03:03 +0000
440+++ lib/lp/code/stories/webservice/xx-code-import.txt 2011-08-28 19:23:29 +0000
441@@ -55,7 +55,7 @@
442 >>> print representation['branch_link']
443 http://.../~import-owner/scruff/import
444 >>> print representation['review_status']
445- Pending Review
446+ Reviewed
447 >>> print representation['rcs_type']
448 Subversion via CSCVS
449 >>> print representation['url']
450@@ -105,7 +105,7 @@
451 >>> print representation['branch_link']
452 http://.../~import-owner/scruffbuntu/manic/scruff/import
453 >>> print representation['review_status']
454- Pending Review
455+ Reviewed
456 >>> print representation['rcs_type']
457 Subversion via CSCVS
458 >>> print representation['url']
459
460=== modified file 'lib/lp/testing/factory.py'
461--- lib/lp/testing/factory.py 2011-08-27 16:28:52 +0000
462+++ lib/lp/testing/factory.py 2011-08-28 19:23:29 +0000
463@@ -2150,34 +2150,32 @@
464 else:
465 assert rcs_type in (RevisionControlSystems.SVN,
466 RevisionControlSystems.BZR_SVN)
467- code_import = code_import_set.new(
468+ return code_import_set.new(
469 registrant, target, branch_name, rcs_type=rcs_type,
470- url=svn_branch_url)
471+ url=svn_branch_url, review_status=review_status)
472 elif git_repo_url is not None:
473 assert rcs_type in (None, RevisionControlSystems.GIT)
474- code_import = code_import_set.new(
475+ return code_import_set.new(
476 registrant, target, branch_name,
477 rcs_type=RevisionControlSystems.GIT,
478- url=git_repo_url)
479+ url=git_repo_url, review_status=review_status)
480 elif hg_repo_url is not None:
481- code_import = code_import_set.new(
482+ return code_import_set.new(
483 registrant, target, branch_name,
484 rcs_type=RevisionControlSystems.HG,
485- url=hg_repo_url)
486+ url=hg_repo_url, review_status=review_status)
487 elif bzr_branch_url is not None:
488- code_import = code_import_set.new(
489+ return code_import_set.new(
490 registrant, target, branch_name,
491 rcs_type=RevisionControlSystems.BZR,
492- url=bzr_branch_url)
493+ url=bzr_branch_url, review_status=review_status)
494 else:
495 assert rcs_type in (None, RevisionControlSystems.CVS)
496- code_import = code_import_set.new(
497+ return code_import_set.new(
498 registrant, target, branch_name,
499 rcs_type=RevisionControlSystems.CVS,
500- cvs_root=cvs_root, cvs_module=cvs_module)
501- if review_status:
502- removeSecurityProxy(code_import).review_status = review_status
503- return code_import
504+ cvs_root=cvs_root, cvs_module=cvs_module,
505+ review_status=review_status)
506
507 def makeChangelog(self, spn=None, versions=[]):
508 """Create and return a LFA of a valid Debian-style changelog."""
509
510=== modified file 'utilities/sourcedeps.cache'
511--- utilities/sourcedeps.cache 2011-08-19 14:03:25 +0000
512+++ utilities/sourcedeps.cache 2011-08-28 19:23:29 +0000
513@@ -1,8 +1,4 @@
514 {
515- "bzr-builder": [
516- 68,
517- "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
518- ],
519 "testresources": [
520 16,
521 "robertc@robertcollins.net-20050911111209-ee5da49011cf936a"
522@@ -31,14 +27,18 @@
523 24,
524 "launchpad@pqm.canonical.com-20100601182722-wo7h2fh0fvyw3aaq"
525 ],
526- "lpreview": [
527- 23,
528- "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
529- ],
530 "bzr-git": [
531 259,
532 "launchpad@pqm.canonical.com-20110601140035-gl5merbechngjw5s"
533 ],
534+ "loggerhead": [
535+ 454,
536+ "gavin@gromper.net-20110811101303-ka12yvnq2p48e2t8"
537+ ],
538+ "bzr-builder": [
539+ 68,
540+ "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
541+ ],
542 "bzr-loom": [
543 49,
544 "launchpad@pqm.canonical.com-20110601122412-54vo3k8yae9i2zve"
545@@ -47,9 +47,9 @@
546 4,
547 "sinzui-20090526164636-1swugzupwvjgomo4"
548 ],
549- "loggerhead": [
550- 452,
551- "andrew.bennetts@canonical.com-20110628173100-owrifrnckvoi60af"
552+ "lpreview": [
553+ 23,
554+ "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
555 ],
556 "difftacular": [
557 6,