Merge lp:~mwhudson/launchpad/incremental-code-imports-bug-512683 into lp:launchpad/db-devel

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~mwhudson/launchpad/incremental-code-imports-bug-512683
Merge into: lp:launchpad/db-devel
Diff against target: 504 lines (+174/-67)
13 files modified
lib/canonical/config/schema-lazr.conf (+4/-0)
lib/canonical/launchpad/testing/codeimporthelpers.py (+2/-6)
lib/lp/code/browser/branch.py (+2/-0)
lib/lp/code/enums.py (+7/-0)
lib/lp/code/model/codeimportjob.py (+4/-1)
lib/lp/code/model/tests/test_codeimportjob.py (+15/-0)
lib/lp/code/stories/codeimport/xx-codeimport-results.txt (+21/-16)
lib/lp/codehosting/codeimport/tests/test_worker.py (+42/-8)
lib/lp/codehosting/codeimport/tests/test_workermonitor.py (+15/-0)
lib/lp/codehosting/codeimport/worker.py (+32/-9)
lib/lp/codehosting/codeimport/workermonitor.py (+3/-0)
lib/lp/testing/factory.py (+25/-25)
utilities/sourcedeps.conf (+2/-2)
To merge this branch: bzr merge lp:~mwhudson/launchpad/incremental-code-imports-bug-512683
Reviewer Review Type Date Requested Status
Tim Penhey (community) Approve
Review via email: mp+19674@code.launchpad.net

Commit message

Support incremental imports for git -- repositories will be imported 1000 revisions at a time.

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Hi there,

This branch adds support to incrementally pull 5000 revisions at a time for git imports.

Not much more to say really, I hope the implementation mostly makes sense (at least if you know this area of the code).

Cheers,
mwh

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Oh, the new icon looks like this: http://people.canonical.com/~mwh/partial-success.png

Do we need a UI review? I guess we probably should.

Revision history for this message
Tim Penhey (thumper) wrote :

lib/lp/codehosting/codeimport/worker.py

> + if bazaar_tree.branch.last_revision() \
> + == foreign_branch.last_revision():

Our coding standard does suggest to use braces rather than line continuation
characters.

Or even:

    last_revision = bazaar_tree.branch.last_revision()
    if (last_revision == foreign_branch.last_revision()):

Everything else looks good!

  merge approved

review: Approve
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Hi Tim,

This branch failed tests because some other tests (the distrobrancher ones, I think) failed when run with the bzr-git plugin loaded. So I added a layer that prevents any tests from being run in the same process after the code import worker tests. Can you review this approach please? The interdiff is here: http://pastebin.ubuntu.com/381384/

With this change, the branch passed all tests in ec2.

Cheers,
mwh

Revision history for this message
Tim Penhey (thumper) wrote :

Add a comment about why this works, and land it. We can discuss the unplugability of the foreign branch bits at a later date.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/config/schema-lazr.conf'
2--- lib/canonical/config/schema-lazr.conf 2010-02-20 13:16:38 +0000
3+++ lib/canonical/config/schema-lazr.conf 2010-02-22 05:40:42 +0000
4@@ -439,6 +439,10 @@
5 # in a row.
6 consecutive_failure_limit: 5
7
8+# Import only this many revisions at once.
9+# Only applies to git imports for now.
10+revisions_import_limit: 1000
11+
12
13 [codeimportdispatcher]
14 # The directory where the code import worker should be directed to
15
16=== added file 'lib/canonical/launchpad/images/yes-gray.png'
17Binary files lib/canonical/launchpad/images/yes-gray.png 1970-01-01 00:00:00 +0000 and lib/canonical/launchpad/images/yes-gray.png 2010-02-22 05:40:42 +0000 differ
18=== modified file 'lib/canonical/launchpad/testing/codeimporthelpers.py'
19--- lib/canonical/launchpad/testing/codeimporthelpers.py 2009-06-25 05:30:52 +0000
20+++ lib/canonical/launchpad/testing/codeimporthelpers.py 2010-02-22 05:40:42 +0000
21@@ -119,17 +119,13 @@
22 return code_import
23
24
25-def make_all_result_types(code_import=None, factory=None, machine=None):
26+def make_all_result_types(code_import, factory, machine, start, count):
27 """Make a code import result of each possible type for the code import."""
28- if factory is None:
29- factory = LaunchpadObjectFactory()
30- if code_import is None:
31- code_import = factory.makeCodeImport()
32 start_dates = time_counter(
33 datetime(2007,12,1,12, tzinfo=UTC), timedelta(days=1))
34 end_dates = time_counter(
35 datetime(2007,12,1,13, tzinfo=UTC), timedelta(days=1, hours=1))
36- for result_status in CodeImportResultStatus.items:
37+ for result_status in sorted(CodeImportResultStatus.items)[start:start+count]:
38 factory.makeCodeImportResult(
39 code_import, result_status, start_dates.next(), end_dates.next(),
40 machine=machine)
41
42=== modified file 'lib/lp/code/browser/branch.py'
43--- lib/lp/code/browser/branch.py 2010-02-01 03:49:23 +0000
44+++ lib/lp/code/browser/branch.py 2010-02-22 05:40:42 +0000
45@@ -514,6 +514,8 @@
46 """The icon to represent the `CodeImportResultStatus` `status`."""
47 if status in CodeImportResultStatus.successes:
48 return "/@@/yes"
49+ elif status == CodeImportResultStatus.SUCCESS_PARTIAL:
50+ return "/@@/yes-gray"
51 else:
52 return "/@@/no"
53
54
55=== modified file 'lib/lp/code/enums.py'
56--- lib/lp/code/enums.py 2010-02-01 03:49:23 +0000
57+++ lib/lp/code/enums.py 2010-02-22 05:40:42 +0000
58@@ -806,6 +806,13 @@
59 import.
60 """)
61
62+ SUCCESS_PARTIAL = DBItem(120, """
63+ Partial Success
64+
65+ Import job successfully imported some but not all of the foreign
66+ revisions.
67+ """)
68+
69 FAILURE = DBItem(200, """
70 Failure
71
72
73=== modified file 'lib/lp/code/model/codeimportjob.py'
74--- lib/lp/code/model/codeimportjob.py 2010-01-27 02:48:13 +0000
75+++ lib/lp/code/model/codeimportjob.py 2010-02-22 05:40:42 +0000
76@@ -286,7 +286,10 @@
77 dict(review_status=CodeImportReviewStatus.FAILING), None)
78 # Only start a new one if the import is still in the REVIEWED state.
79 if code_import.review_status == CodeImportReviewStatus.REVIEWED:
80- self.newJob(code_import)
81+ extra = {}
82+ if status == CodeImportResultStatus.SUCCESS_PARTIAL:
83+ extra['date_due'] = UTC_NOW
84+ self.newJob(code_import, **extra)
85 # If the status was successful, update date_last_successful.
86 if status in [CodeImportResultStatus.SUCCESS,
87 CodeImportResultStatus.SUCCESS_NOCHANGE]:
88
89=== modified file 'lib/lp/code/model/tests/test_codeimportjob.py'
90--- lib/lp/code/model/tests/test_codeimportjob.py 2010-02-01 03:55:59 +0000
91+++ lib/lp/code/model/tests/test_codeimportjob.py 2010-02-22 05:40:42 +0000
92@@ -776,6 +776,21 @@
93 new_job.date_due - running_job.date_due,
94 code_import.effective_update_interval)
95
96+ def test_partialSuccessCreatesNewJobDueNow(self):
97+ # If called with a status of SUCCESS_PARTIAL, finishJob() creates a
98+ # new CodeImportJob for the given CodeImport that is due to run right
99+ # now.
100+ running_job = self.makeRunningJob()
101+ code_import = running_job.code_import
102+ self.switchDbUser()
103+ getUtility(ICodeImportJobWorkflow).finishJob(
104+ running_job, CodeImportResultStatus.SUCCESS_PARTIAL, None)
105+ new_job = code_import.import_job
106+ self.assert_(new_job is not None)
107+ self.assertEqual(new_job.state, CodeImportJobState.PENDING)
108+ self.assertEqual(new_job.machine, None)
109+ self.assertSqlAttributeEqualsDate(new_job, 'date_due', UTC_NOW)
110+
111 def test_doesntCreateNewJobIfCodeImportNotReviewed(self):
112 # finishJob() creates a new CodeImportJob for the given CodeImport,
113 # unless the CodeImport has been suspended or marked invalid.
114
115=== modified file 'lib/lp/code/stories/codeimport/xx-codeimport-results.txt'
116--- lib/lp/code/stories/codeimport/xx-codeimport-results.txt 2010-01-27 03:49:06 +0000
117+++ lib/lp/code/stories/codeimport/xx-codeimport-results.txt 2010-02-22 05:40:42 +0000
118@@ -6,29 +6,28 @@
119 >>> login('test@canonical.com')
120 >>> from canonical.launchpad.testing.codeimporthelpers import (
121 ... make_all_result_types)
122- >>> code_import = factory.makeCodeImport()
123+ >>> code_import_1 = factory.makeCodeImport()
124+ >>> code_import_2 = factory.makeCodeImport()
125
126 The make_all_result_types helper method adds a code import result of
127-each possible status value.
128+each possible status value. There are more status values than are
129+shown on the branch page, so we create two imports in order to test
130+how each result type is rendered.
131
132 >>> odin = factory.makeCodeImportMachine(hostname='odin')
133- >>> make_all_result_types(code_import, factory, machine=odin)
134- >>> branch_url = canonical_url(code_import.branch)
135+ >>> make_all_result_types(code_import_1, factory, machine=odin, start=0, count=7)
136+ >>> branch_url_1 = canonical_url(code_import_1.branch)
137+ >>> make_all_result_types(code_import_2, factory, machine=odin, start=7, count=7)
138+ >>> branch_url_2 = canonical_url(code_import_2.branch)
139 >>> logout()
140
141 For each import result, the start date and finish date is shown along
142 with the duration. A link to the log file is shown if there was a log
143 file stored with the result.
144
145- >>> browser.open(branch_url)
146+ >>> browser.open(branch_url_1)
147 >>> import_results = find_tag_by_id(browser.contents, 'import-results')
148 >>> print extract_text(import_results).replace('—', '--')
149- Import started on 2007-12-10 on odin and finished on 2007-12-10
150- taking ten hours -- see the log
151- Import started on 2007-12-09 on odin and finished on 2007-12-09
152- taking nine hours -- see the log
153- Import started on 2007-12-08 on odin and finished on 2007-12-08
154- taking eight hours -- see the log
155 Import started on 2007-12-07 on odin and finished on 2007-12-07
156 taking seven hours -- see the log
157 Import started on 2007-12-06 on odin and finished on 2007-12-06
158@@ -46,19 +45,25 @@
159
160 Each of the lines is prefixed with a tick if the result status was
161 success, or a cross if the status was a failure. The title of the image
162-is the text of the failure type.
163+is the text of the failure or success type.
164
165 >>> # The ordering here is dependant on the order the status values
166 >>> # are declared in the enumeration.
167 >>> for img in import_results.fetch('img'):
168 ... print img
169- <img src="/@@/no" title="Job killed" />
170- <img src="/@@/no" title="Job reclaimed" />
171- <img src="/@@/no" title="Bazaar Update Failed" />
172- <img src="/@@/no" title="Source Update Failed" />
173 <img src="/@@/no" title="Bazaar Import Failed" />
174 <img src="/@@/no" title="Source Checkout Failed" />
175 <img src="/@@/no" title="Internal Failure" />
176 <img src="/@@/no" title="Failure" />
177+ <img src="/@@/yes-gray" title="Partial Success" />
178 <img src="/@@/yes" title="Success with no changes" />
179 <img src="/@@/yes" title="Success" />
180+
181+ >>> browser.open(branch_url_2)
182+ >>> import_results = find_tag_by_id(browser.contents, 'import-results')
183+ >>> for img in import_results.fetch('img'):
184+ ... print img
185+ <img src="/@@/no" title="Job killed" />
186+ <img src="/@@/no" title="Job reclaimed" />
187+ <img src="/@@/no" title="Bazaar Update Failed" />
188+ <img src="/@@/no" title="Source Update Failed" />
189
190=== modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py'
191--- lib/lp/codehosting/codeimport/tests/test_worker.py 2010-02-03 19:29:27 +0000
192+++ lib/lp/codehosting/codeimport/tests/test_worker.py 2010-02-22 05:40:42 +0000
193@@ -23,7 +23,6 @@
194
195 from CVS import Repository, tree as CVSTree
196
197-from canonical.cachedproperty import cachedproperty
198 from canonical.config import config
199 from canonical.launchpad.scripts.logger import QuietFakeLogger
200 from canonical.testing import BaseLayer
201@@ -37,22 +36,46 @@
202 CVSServer, GitServer, MercurialServer, SubversionServer)
203 from lp.codehosting.tests.helpers import (
204 create_branch_with_one_revision)
205-from lp.testing.factory import LaunchpadObjectFactory
206+from lp.testing import TestCase
207
208 import pysvn
209
210
211+class ForeignBranchPluginLayer(BaseLayer):
212+ """Ensure only specific tests are run with foreign branch plugins loaded.
213+ """
214+
215+ @classmethod
216+ def setUp(cls):
217+ pass
218+
219+ @classmethod
220+ def tearDown(cls):
221+ # Raise NotImplementedError to signal that this layer cannot be torn
222+ # down. This means that the test runner will run subsequent tests in
223+ # a different process.
224+ raise NotImplementedError
225+
226+ @classmethod
227+ def testSetUp(cls):
228+ pass
229+
230+ @classmethod
231+ def testTearDown(cls):
232+ pass
233+
234+
235 default_format = BzrDirFormat.get_default_format()
236
237
238-class WorkerTest(TestCaseWithTransport):
239+class WorkerTest(TestCaseWithTransport, TestCase):
240 """Base test case for things that test the code import worker.
241
242 Provides Bazaar testing features, access to Launchpad objects and
243 factories for some code import objects.
244 """
245
246- layer = BaseLayer
247+ layer = ForeignBranchPluginLayer
248
249 def setUp(self):
250 TestCaseWithTransport.setUp(self)
251@@ -70,10 +93,6 @@
252 self.assertEqual(
253 sorted(list_files(directory1)), sorted(list_files(directory2)))
254
255- @cachedproperty
256- def factory(self):
257- return LaunchpadObjectFactory()
258-
259 def makeTemporaryDirectory(self):
260 directory = tempfile.mkdtemp()
261 self.addCleanup(shutil.rmtree, directory)
262@@ -906,6 +925,21 @@
263 return self.factory.makeCodeImportSourceDetails(
264 rcstype='git', url=repository_path)
265
266+ def test_partial(self):
267+ # Only config.codeimport.revisions_import_limit will be imported in a
268+ # given run. When bzr-svn and bzr-hg support revision import limits,
269+ # this test case can be moved up to PullingImportWorkerTests.
270+ worker = self.makeImportWorker(self.makeSourceDetails(
271+ 'trunk', [('README', 'Original contents')]))
272+ self.makeForeignCommit(worker.source_details)
273+ self.assertTrue(self.foreign_commit_count > 1)
274+ self.pushConfig(
275+ 'codeimport', revisions_import_limit=self.foreign_commit_count-1)
276+ self.assertEqual(
277+ CodeImportWorkerExitCode.SUCCESS_PARTIAL, worker.run())
278+ self.assertEqual(
279+ CodeImportWorkerExitCode.SUCCESS, worker.run())
280+
281
282 class TestMercurialImport(WorkerTest, TestActualImportMixin,
283 PullingImportWorkerTests):
284
285=== modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py'
286--- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-02-03 19:29:27 +0000
287+++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-02-22 05:40:42 +0000
288@@ -334,6 +334,21 @@
289 # callFinishJob did not swallow the error, this will fail the test.
290 return ret
291
292+ def test_callFinishJobCallsFinishJobPartial(self):
293+ # If the argument to callFinishJob indicates that the subprocess
294+ # exited with a code of CodeImportWorkerExitCode.SUCCESS_PARTIAL, it
295+ # calls finishJob with a status of SUCCESS_PARTIAL.
296+ calls = self.patchOutFinishJob()
297+ ret = self.worker_monitor.callFinishJob(
298+ makeFailure(
299+ error.ProcessTerminated,
300+ exitCode=CodeImportWorkerExitCode.SUCCESS_PARTIAL))
301+ self.assertEqual(calls, [CodeImportResultStatus.SUCCESS_PARTIAL])
302+ self.assertOopsesLogged([])
303+ # We return the deferred that callFinishJob returns -- if
304+ # callFinishJob did not swallow the error, this will fail the test.
305+ return ret
306+
307 def test_callFinishJobLogsTracebackOnFailure(self):
308 # When callFinishJob is called with a failure, it dumps the traceback
309 # of the failure into the log file.
310
311=== modified file 'lib/lp/codehosting/codeimport/worker.py'
312--- lib/lp/codehosting/codeimport/worker.py 2010-02-01 04:26:12 +0000
313+++ lib/lp/codehosting/codeimport/worker.py 2010-02-22 05:40:42 +0000
314@@ -21,7 +21,7 @@
315 import os
316 import shutil
317
318-from bzrlib.branch import Branch
319+from bzrlib.branch import Branch, InterBranch
320 from bzrlib.bzrdir import BzrDir, BzrDirFormat
321 from bzrlib.transport import get_transport
322 from bzrlib.errors import NoSuchFile, NotBranchError
323@@ -50,6 +50,7 @@
324 SUCCESS = 0
325 FAILURE = 1
326 SUCCESS_NOCHANGE = 2
327+ SUCCESS_PARTIAL = 3
328
329
330 class BazaarBranchStore:
331@@ -407,11 +408,7 @@
332 saved_pwd = os.getcwd()
333 os.chdir(working_directory)
334 try:
335- non_trivial = self._doImport()
336- if non_trivial:
337- return CodeImportWorkerExitCode.SUCCESS
338- else:
339- return CodeImportWorkerExitCode.SUCCESS_NOCHANGE
340+ return self._doImport()
341 finally:
342 shutil.rmtree(working_directory)
343 os.chdir(saved_pwd)
344@@ -497,7 +494,10 @@
345 self.importToBazaar(foreign_tree, bazaar_tree)
346 non_trivial = self.pushBazaarWorkingTree(bazaar_tree)
347 self.foreign_tree_store.archive(foreign_tree)
348- return non_trivial
349+ if non_trivial:
350+ return CodeImportWorkerExitCode.SUCCESS
351+ else:
352+ return CodeImportWorkerExitCode.SUCCESS_NOCHANGE
353
354
355 class PullingImportWorker(ImportWorker):
356@@ -511,6 +511,15 @@
357 """The format classes that should be tried for this import."""
358 raise NotImplementedError
359
360+ def getExtraPullArgs(self):
361+ """Return extra arguments to `InterBranch.pull`.
362+
363+ This method only really exists because only bzr-git supports the
364+ 'limit' argument to this method. When bzr-svn and bzr-hg plugin do
365+ too, this method can go away.
366+ """
367+ return {}
368+
369 def _doImport(self):
370 bazaar_tree = self.getBazaarWorkingTree()
371 self.bazaar_branch_store.push(
372@@ -529,10 +538,20 @@
373 else:
374 raise NotBranchError(self.source_details.url)
375 foreign_branch = format.open(transport).open_branch()
376- bazaar_tree.branch.pull(foreign_branch, overwrite=True)
377+ inter_branch = InterBranch.get(foreign_branch, bazaar_tree.branch)
378+ pull_result = inter_branch.pull(
379+ overwrite=True, **self.getExtraPullArgs())
380+ self.pushBazaarWorkingTree(bazaar_tree)
381+ last_imported_revison = bazaar_tree.branch.last_revision()
382+ if last_imported_revison == foreign_branch.last_revision():
383+ if pull_result.old_revid != pull_result.new_revid:
384+ return CodeImportWorkerExitCode.SUCCESS
385+ else:
386+ return CodeImportWorkerExitCode.SUCCESS_NOCHANGE
387+ else:
388+ return CodeImportWorkerExitCode.SUCCESS_PARTIAL
389 finally:
390 bzrlib.ui.ui_factory = saved_factory
391- return self.pushBazaarWorkingTree(bazaar_tree)
392
393
394 class GitImportWorker(PullingImportWorker):
395@@ -549,6 +568,10 @@
396 LocalGitBzrDirFormat, RemoteGitBzrDirFormat)
397 return [LocalGitBzrDirFormat, RemoteGitBzrDirFormat]
398
399+ def getExtraPullArgs(self):
400+ """See `PullingImportWorker.getExtraPullArgs`."""
401+ return {'limit': config.codeimport.revisions_import_limit}
402+
403 def getBazaarWorkingTree(self):
404 """See `ImportWorker.getBazaarWorkingTree`.
405
406
407=== modified file 'lib/lp/codehosting/codeimport/workermonitor.py'
408--- lib/lp/codehosting/codeimport/workermonitor.py 2010-02-01 03:49:23 +0000
409+++ lib/lp/codehosting/codeimport/workermonitor.py 2010-02-22 05:40:42 +0000
410@@ -302,6 +302,9 @@
411 if reason.value.exitCode == \
412 CodeImportWorkerExitCode.SUCCESS_NOCHANGE:
413 return CodeImportResultStatus.SUCCESS_NOCHANGE
414+ elif reason.value.exitCode == \
415+ CodeImportWorkerExitCode.SUCCESS_PARTIAL:
416+ return CodeImportResultStatus.SUCCESS_PARTIAL
417 return CodeImportResultStatus.FAILURE
418 else:
419 return CodeImportResultStatus.SUCCESS
420
421=== modified file 'lib/lp/testing/factory.py'
422--- lib/lp/testing/factory.py 2010-02-20 13:16:38 +0000
423+++ lib/lp/testing/factory.py 2010-02-22 05:40:42 +0000
424@@ -255,6 +255,31 @@
425 host = "%s.domain.com" % self.getUniqueString('domain')
426 return '%s://%s/%s' % (scheme, host, self.getUniqueString('path'))
427
428+ def makeCodeImportSourceDetails(self, branch_id=None, rcstype=None,
429+ url=None, cvs_root=None, cvs_module=None):
430+ if branch_id is None:
431+ branch_id = self.getUniqueInteger()
432+ if rcstype is None:
433+ rcstype = 'svn'
434+ if rcstype in ['svn', 'bzr-svn', 'hg']:
435+ assert cvs_root is cvs_module is None
436+ if url is None:
437+ url = self.getUniqueURL()
438+ elif rcstype == 'cvs':
439+ assert url is None
440+ if cvs_root is None:
441+ cvs_root = self.getUniqueString()
442+ if cvs_module is None:
443+ cvs_module = self.getUniqueString()
444+ elif rcstype == 'git':
445+ assert cvs_root is cvs_module is None
446+ if url is None:
447+ url = self.getUniqueURL(scheme='git')
448+ else:
449+ raise AssertionError("Unknown rcstype %r." % rcstype)
450+ return CodeImportSourceDetails(
451+ branch_id, rcstype, url, cvs_root, cvs_module)
452+
453
454 class LaunchpadObjectFactory(ObjectFactory):
455 """Factory methods for creating Launchpad objects.
456@@ -1409,31 +1434,6 @@
457 code_import, machine, requesting_user, log_excerpt, log_alias,
458 result_status, date_started, date_finished)
459
460- def makeCodeImportSourceDetails(self, branch_id=None, rcstype=None,
461- url=None, cvs_root=None, cvs_module=None):
462- if branch_id is None:
463- branch_id = self.getUniqueInteger()
464- if rcstype is None:
465- rcstype = 'svn'
466- if rcstype in ['svn', 'bzr-svn', 'hg']:
467- assert cvs_root is cvs_module is None
468- if url is None:
469- url = self.getUniqueURL()
470- elif rcstype == 'cvs':
471- assert url is None
472- if cvs_root is None:
473- cvs_root = self.getUniqueString()
474- if cvs_module is None:
475- cvs_module = self.getUniqueString()
476- elif rcstype == 'git':
477- assert cvs_root is cvs_module is None
478- if url is None:
479- url = self.getUniqueURL(scheme='git')
480- else:
481- raise AssertionError("Unknown rcstype %r." % rcstype)
482- return CodeImportSourceDetails(
483- branch_id, rcstype, url, cvs_root, cvs_module)
484-
485 def makeCodeReviewComment(self, sender=None, subject=None, body=None,
486 vote=None, vote_tag=None, parent=None,
487 merge_proposal=None):
488
489=== modified file 'utilities/sourcedeps.conf'
490--- utilities/sourcedeps.conf 2010-02-18 13:53:20 +0000
491+++ utilities/sourcedeps.conf 2010-02-22 05:40:42 +0000
492@@ -1,10 +1,10 @@
493 bzr-builder lp:~launchpad-pqm/bzr-builder/trunk;revno=63
494-bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=249
495+bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=250
496 bzr-hg lp:~launchpad-pqm/bzr-hg/devel;revno=281
497 bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=47
498 bzr-svn lp:~launchpad-pqm/bzr-svn/devel;revno=2707
499 cscvs lp:~launchpad-pqm/launchpad-cscvs/devel;revno=430
500-dulwich lp:~launchpad-pqm/dulwich/devel;revno=415
501+dulwich lp:~launchpad-pqm/dulwich/devel;revno=416
502 launchpad-loggerhead lp:~launchpad-pqm/launchpad-loggerhead/devel;revno=54
503 loggerhead lp:~launchpad-pqm/loggerhead/devel;revno=174
504 lpreview lp:~launchpad-pqm/bzr-lpreview/devel;revno=23

Subscribers

People subscribed via source and target branches

to status/vote changes: