Merge lp:~thumper/launchpad/hg-imports-using-url into lp:launchpad/db-devel

Proposed by Tim Penhey
Status: Merged
Merged at revision: 8859
Proposed branch: lp:~thumper/launchpad/hg-imports-using-url
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~thumper/launchpad/imports-urls
Diff against target: 1027 lines (+372/-71)
22 files modified
lib/canonical/config/schema-lazr.conf (+5/-0)
lib/lp/code/browser/codeimport.py (+26/-5)
lib/lp/code/doc/codeimport-event.txt (+14/-0)
lib/lp/code/doc/codeimport.txt (+46/-1)
lib/lp/code/mail/codeimport.py (+2/-1)
lib/lp/code/model/codeimport.py (+9/-4)
lib/lp/code/model/codeimportevent.py (+2/-1)
lib/lp/code/model/tests/test_codeimport.py (+29/-33)
lib/lp/code/stories/codeimport/xx-admin-codeimport.txt (+22/-1)
lib/lp/code/stories/codeimport/xx-codeimport-list.txt (+7/-2)
lib/lp/code/stories/codeimport/xx-create-codeimport.txt (+28/-10)
lib/lp/code/templates/branch-import-details.pt (+7/-1)
lib/lp/code/templates/codeimport-new.pt (+16/-0)
lib/lp/codehosting/codeimport/tests/servers.py (+21/-0)
lib/lp/codehosting/codeimport/tests/test_worker.py (+55/-5)
lib/lp/codehosting/codeimport/tests/test_workermonitor.py (+22/-1)
lib/lp/codehosting/codeimport/worker.py (+44/-2)
lib/lp/testing/factory.py (+10/-3)
scripts/code-import-worker.py (+4/-1)
setup.py (+1/-0)
utilities/sourcedeps.conf (+1/-0)
versions.cfg (+1/-0)
To merge this branch: bzr merge lp:~thumper/launchpad/hg-imports-using-url
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle Approve
Review via email: mp+17187@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

This branch merges jelmer's hg import work into the imports-urls branch.

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

We are now ready to go.

Local import succeeded \o/

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (14.0 KiB)

Hi Tim,

Cool to see this arrive! Sadly, I'm not sure it can land yet, because
of the new sourcecode branch. Let's talk to a LOSA about that.

> === modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'

I think it would be good to create a Hg import in this test.

> --- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-11 22:40:42 +0000
> +++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-12 22:39:29 +0000
> @@ -32,6 +32,7 @@
>
> >>> print_radio_button_field(browser.contents, "rcs_type")
> ( ) Git
> + ( ) Mercurial
> (*) Subversion
> ( ) CVS
>
> @@ -66,21 +67,14 @@
> Requesting a Git import
> =======================
>
> -The default foreign VCS type is Subversion.
> -
> - >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
> - >>> print_radio_button_field(browser.contents, "rcs_type")
> - ( ) Git
> - (*) Subversion
> - ( ) CVS
> -
> The user is required to enter a project that the import is for,
> a name for the import branch, and a subversion branch location.
>
> + >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
> >>> browser.getControl('Project').value = "firefox"
> >>> browser.getControl('Branch Name').value = "git-import"
> >>> browser.getControl('Git').click()
> - >>> browser.getControl('Repo URL').value = (
> + >>> browser.getControl('Repo URL', index=0).value = (
> ... "git://example.com/firefox.git")
> >>> browser.getControl('Request Import').click()
>

> === modified file 'lib/lp/code/templates/branch-import-details.pt'
> --- lib/lp/code/templates/branch-import-details.pt 2010-01-11 21:21:00 +0000
> +++ lib/lp/code/templates/branch-import-details.pt 2010-01-12 22:39:29 +0000
> @@ -45,6 +45,12 @@
> </p>
> </tal:git-import>
>
> + <tal:hg-import condition="code_import/rcs_type/enumvalue:HG">
> + <p>This branch is an import of the Mercurial repo at

I think I overheard some conversation about this, but it's not really
an import of the repo, but of the tip branch or whatever it's called
for Mercurial. Should we try to explain this?

> + <span tal:replace="code_import/url" />.
> + </p>
> + </tal:hg-import>
> +
> <tal:svn-import condition="view/is_svn_import">
> <p id="svn-import-details">
> This branch is an import of the

> === modified file 'lib/lp/codehosting/codeimport/tests/servers.py'
> --- lib/lp/codehosting/codeimport/tests/servers.py 2009-12-04 04:08:12 +0000
> +++ lib/lp/codehosting/codeimport/tests/servers.py 2010-01-12 22:39:29 +0000
> @@ -8,6 +8,7 @@
> __all__ = [
> 'CVSServer',
> 'GitServer',
> + 'MercurialServer',
> 'SubversionServer',
> ]
>
> @@ -210,3 +211,23 @@
> builder.finish()
> finally:
> os.chdir(wd)
> +
> +
> +class MercurialServer(Server):
> +
> + def __init__(self, repo_url):
> + super(MercurialServer, self).__init__()
> + self.repo_url = repo_url
> +
> + def makeRepo(self, tree_contents):
> + from mercurial.ui import ui
> + from mercurial.localrepo import localreposit...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/config/schema-lazr.conf'
--- lib/canonical/config/schema-lazr.conf 2009-11-17 11:27:51 +0000
+++ lib/canonical/config/schema-lazr.conf 2010-01-13 02:53:16 +0000
@@ -402,6 +402,11 @@
402default_interval_git: 21600402default_interval_git: 21600
403403
404# The default value of the update interval of a code import from404# The default value of the update interval of a code import from
405# Mercurial, in seconds.
406# datatype: integer
407default_interval_hg: 21600
408
409# The default value of the update interval of a code import from
405# CVS, in seconds.410# CVS, in seconds.
406# datatype: integer411# datatype: integer
407default_interval_cvs: 43200412default_interval_cvs: 43200
408413
=== modified file 'lib/lp/code/browser/codeimport.py'
--- lib/lp/code/browser/codeimport.py 2010-01-13 02:53:14 +0000
+++ lib/lp/code/browser/codeimport.py 2010-01-13 02:53:16 +0000
@@ -213,7 +213,7 @@
213 git_repo_url = URIField(213 git_repo_url = URIField(
214 title=_("Repo URL"), required=False,214 title=_("Repo URL"), required=False,
215 description=_(215 description=_(
216 "The URL of the git repository. The MASTER branch will be "216 "The URL of the git repository. The HEAD branch will be "
217 "imported."),217 "imported."),
218 allowed_schemes=["git"],218 allowed_schemes=["git"],
219 allow_userinfo=False, # Only anonymous access is supported.219 allow_userinfo=False, # Only anonymous access is supported.
@@ -222,6 +222,18 @@
222 allow_fragment=False,222 allow_fragment=False,
223 trailing_slash=False)223 trailing_slash=False)
224224
225 hg_repo_url = URIField(
226 title=_("Repo URL"), required=False,
227 description=_(
228 "The URL of the Mercurial repository. The tip branch will be "
229 "imported."),
230 allowed_schemes=["http", "https"],
231 allow_userinfo=False, # Only anonymous access is supported.
232 allow_port=True,
233 allow_query=False, # Query makes no sense in Mercurial
234 allow_fragment=False, # Fragment makes no sense in Mercurial
235 trailing_slash=False) # See http://launchpad.net/bugs/56357.
236
225 branch_name = copy_field(237 branch_name = copy_field(
226 IBranch['name'],238 IBranch['name'],
227 __name__='branch_name',239 __name__='branch_name',
@@ -258,16 +270,18 @@
258 # display them separately in the form.270 # display them separately in the form.
259 soup = BeautifulSoup(self.widgets['rcs_type']())271 soup = BeautifulSoup(self.widgets['rcs_type']())
260 fields = soup.findAll('input')272 fields = soup.findAll('input')
261 [cvs_button, svn_button, git_button, empty_marker] = [273 [cvs_button, svn_button, git_button, hg_button, empty_marker] = [
262 field for field in fields274 field for field in fields
263 if field.get('value') in ['CVS', 'BZR_SVN', 'GIT', '1']]275 if field.get('value') in ['CVS', 'BZR_SVN', 'GIT', 'HG', '1']]
264 cvs_button['onclick'] = 'updateWidgets()'276 cvs_button['onclick'] = 'updateWidgets()'
265 svn_button['onclick'] = 'updateWidgets()'277 svn_button['onclick'] = 'updateWidgets()'
266 git_button['onclick'] = 'updateWidgets()'278 git_button['onclick'] = 'updateWidgets()'
279 hg_button['onclick'] = 'updateWidgets()'
267 # The following attributes are used only in the page template.280 # The following attributes are used only in the page template.
268 self.rcs_type_cvs = str(cvs_button)281 self.rcs_type_cvs = str(cvs_button)
269 self.rcs_type_svn = str(svn_button)282 self.rcs_type_svn = str(svn_button)
270 self.rcs_type_git = str(git_button)283 self.rcs_type_git = str(git_button)
284 self.rcs_type_hg = str(hg_button)
271 self.rcs_type_emptymarker = str(empty_marker)285 self.rcs_type_emptymarker = str(empty_marker)
272286
273 def _getImportLocation(self, data):287 def _getImportLocation(self, data):
@@ -279,6 +293,8 @@
279 return None, None, data.get('svn_branch_url')293 return None, None, data.get('svn_branch_url')
280 elif rcs_type == RevisionControlSystems.GIT:294 elif rcs_type == RevisionControlSystems.GIT:
281 return None, None, data.get('git_repo_url')295 return None, None, data.get('git_repo_url')
296 elif rcs_type == RevisionControlSystems.HG:
297 return None, None, data.get('hg_repo_url')
282 else:298 else:
283 raise AssertionError(299 raise AssertionError(
284 'Unexpected revision control type %r.' % rcs_type)300 'Unexpected revision control type %r.' % rcs_type)
@@ -379,6 +395,9 @@
379 elif rcs_type == RevisionControlSystems.GIT:395 elif rcs_type == RevisionControlSystems.GIT:
380 self._validateURL(396 self._validateURL(
381 data.get('git_repo_url'), field_name='git_repo_url')397 data.get('git_repo_url'), field_name='git_repo_url')
398 elif rcs_type == RevisionControlSystems.HG:
399 self._validateURL(
400 data.get('hg_repo_url'), field_name='hg_repo_url')
382 else:401 else:
383 raise AssertionError(402 raise AssertionError(
384 'Unexpected revision control type %r.' % rcs_type)403 'Unexpected revision control type %r.' % rcs_type)
@@ -469,7 +488,8 @@
469 self.form_fields = self.form_fields.omit('url')488 self.form_fields = self.form_fields.omit('url')
470 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,489 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
471 RevisionControlSystems.BZR_SVN,490 RevisionControlSystems.BZR_SVN,
472 RevisionControlSystems.GIT):491 RevisionControlSystems.GIT,
492 RevisionControlSystems.HG):
473 self.form_fields = self.form_fields.omit(493 self.form_fields = self.form_fields.omit(
474 'cvs_root', 'cvs_module')494 'cvs_root', 'cvs_module')
475 else:495 else:
@@ -503,7 +523,8 @@
503 self.code_import)523 self.code_import)
504 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,524 elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
505 RevisionControlSystems.BZR_SVN,525 RevisionControlSystems.BZR_SVN,
506 RevisionControlSystems.GIT):526 RevisionControlSystems.GIT,
527 RevisionControlSystems.HG):
507 self._validateURL(data.get('url'), self.code_import)528 self._validateURL(data.get('url'), self.code_import)
508 else:529 else:
509 raise AssertionError('Unknown rcs_type for code import.')530 raise AssertionError('Unknown rcs_type for code import.')
510531
=== modified file 'lib/lp/code/doc/codeimport-event.txt'
--- lib/lp/code/doc/codeimport-event.txt 2010-01-13 02:53:14 +0000
+++ lib/lp/code/doc/codeimport-event.txt 2010-01-13 02:53:16 +0000
@@ -165,6 +165,20 @@
165 URL u'git://git.example.org/main.git'165 URL u'git://git.example.org/main.git'
166166
167167
168And for a Mercurial import, the hg details are recorded.
169
170 >>> hg_import = factory.makeCodeImport(
171 ... hg_repo_url='http://hg.example.org/main')
172 >>> hg_create_event = event_set.newCreate(hg_import, nopriv)
173 >>> print_items(hg_create_event)
174 CODE_IMPORT <muted>
175 OWNER ...
176 REVIEW_STATUS u'REVIEWED'
177 ASSIGNEE None
178 UPDATE_INTERVAL None
179 URL u'http://hg.example.org/main'
180
181
168== MODIFY ==182== MODIFY ==
169183
170When a code import is modified, the ICodeImportEventSet utility should184When a code import is modified, the ICodeImportEventSet utility should
171185
=== modified file 'lib/lp/code/doc/codeimport.txt'
--- lib/lp/code/doc/codeimport.txt 2010-01-13 02:53:14 +0000
+++ lib/lp/code/doc/codeimport.txt 2010-01-13 02:53:16 +0000
@@ -197,6 +197,33 @@
197 >>> git_import == existing_import197 >>> git_import == existing_import
198 True198 True
199199
200Import from Mercurial
201+++++++++++++++++++++
202
203Code imports from Mercurial specify the URL used with "hg clone" to
204retrieve the branch to import.
205
206 >>> hg = RevisionControlSystems.HG
207 >>> hg_url = 'http://hg.example.com/metallic'
208 >>> hg_import = code_import_set.new(
209 ... registrant=nopriv, product=product, branch_name='trunk-hg',
210 ... rcs_type=hg, url=hg_url)
211 >>> verifyObject(ICodeImport, removeSecurityProxy(hg_import))
212 True
213
214Creating a CodeImport object creates a corresponding CodeImportEvent.
215
216 >>> hg_events = event_set.getEventsForCodeImport(hg_import)
217 >>> [event.event_type.name for event in hg_events]
218 ['CREATE']
219
220The CodeImportSet is also able to retrieve the code imports with the
221specified hg repo url.
222
223 >>> existing_import = code_import_set.getByURL(url=hg_url)
224 >>> hg_import == existing_import
225 True
226
200227
201Updating code import details228Updating code import details
202----------------------------229----------------------------
@@ -316,6 +343,8 @@
316 ... seconds=config.codeimport.default_interval_subversion)343 ... seconds=config.codeimport.default_interval_subversion)
317 >>> default_interval_git = timedelta(344 >>> default_interval_git = timedelta(
318 ... seconds=config.codeimport.default_interval_git)345 ... seconds=config.codeimport.default_interval_git)
346 >>> default_interval_hg = timedelta(
347 ... seconds=config.codeimport.default_interval_hg)
319348
320By default, code imports are created with an unspecified update interval.349By default, code imports are created with an unspecified update interval.
321350
@@ -345,6 +374,12 @@
345 >>> git_import.effective_update_interval374 >>> git_import.effective_update_interval
346 datetime.timedelta(0, 21600)375 datetime.timedelta(0, 21600)
347376
377 >>> default_interval_hg
378 datetime.timedelta(0, 21600)
379 >>> hg_import.effective_update_interval
380 datetime.timedelta(0, 21600)
381
382
348If the update interval is set, then it overrides the default value.383If the update interval is set, then it overrides the default value.
349384
350As explained in the "Modify CodeImports" section, the interface does not allow385As explained in the "Modify CodeImports" section, the interface does not allow
@@ -477,7 +512,7 @@
477 instead of:512 instead of:
478 hello from :pserver:anonymous@cvs.example.com:/cvsroot513 hello from :pserver:anonymous@cvs.example.com:/cvsroot
479514
480And for Git.515For Git.
481516
482 >>> data = {'url': 'git://git.example.com/goodbye.git'}517 >>> data = {'url': 'git://git.example.com/goodbye.git'}
483 >>> modify_event = git_import.updateFromData(data, nopriv)518 >>> modify_event = git_import.updateFromData(data, nopriv)
@@ -499,6 +534,16 @@
499 instead of:534 instead of:
500 svn://svn.example.com/for-bzr-svn/trunk535 svn://svn.example.com/for-bzr-svn/trunk
501536
537And for Mercurial.
538
539 >>> data = {'url': 'http://metal.example.com/byebye.hg'}
540 >>> modify_event = hg_import.updateFromData(data, nopriv)
541 >>> print make_email_body_for_code_import_update(
542 ... hg_import, modify_event, None)
543 ~no-priv/firefox/trunk-hg is now being imported from:
544 http://metal.example.com/byebye.hg
545 instead of:
546 http://hg.example.com/metallic
502547
503In addition, updateFromData can be used to set the branch whiteboard,548In addition, updateFromData can be used to set the branch whiteboard,
504which is also described in the email that is sent.549which is also described in the email that is sent.
505550
=== modified file 'lib/lp/code/mail/codeimport.py'
--- lib/lp/code/mail/codeimport.py 2010-01-13 02:53:14 +0000
+++ lib/lp/code/mail/codeimport.py 2010-01-13 02:53:16 +0000
@@ -97,7 +97,8 @@
97 "\ninstead of:\n" + old_details)97 "\ninstead of:\n" + old_details)
98 elif code_import.rcs_type in (RevisionControlSystems.SVN,98 elif code_import.rcs_type in (RevisionControlSystems.SVN,
99 RevisionControlSystems.BZR_SVN,99 RevisionControlSystems.BZR_SVN,
100 RevisionControlSystems.GIT):100 RevisionControlSystems.GIT,
101 RevisionControlSystems.HG):
101 if CodeImportEventDataType.OLD_URL in event_data:102 if CodeImportEventDataType.OLD_URL in event_data:
102 old_url = event_data[CodeImportEventDataType.OLD_URL]103 old_url = event_data[CodeImportEventDataType.OLD_URL]
103 body.append(104 body.append(
104105
=== modified file 'lib/lp/code/model/codeimport.py'
--- lib/lp/code/model/codeimport.py 2010-01-13 02:53:13 +0000
+++ lib/lp/code/model/codeimport.py 2010-01-13 02:53:16 +0000
@@ -106,6 +106,8 @@
106 config.codeimport.default_interval_subversion,106 config.codeimport.default_interval_subversion,
107 RevisionControlSystems.GIT:107 RevisionControlSystems.GIT:
108 config.codeimport.default_interval_git,108 config.codeimport.default_interval_git,
109 RevisionControlSystems.HG:
110 config.codeimport.default_interval_hg,
109 }111 }
110 seconds = default_interval_dict[self.rcs_type]112 seconds = default_interval_dict[self.rcs_type]
111 return timedelta(seconds=seconds)113 return timedelta(seconds=seconds)
@@ -122,7 +124,8 @@
122 elif self.rcs_type in (124 elif self.rcs_type in (
123 RevisionControlSystems.SVN,125 RevisionControlSystems.SVN,
124 RevisionControlSystems.GIT,126 RevisionControlSystems.GIT,
125 RevisionControlSystems.BZR_SVN):127 RevisionControlSystems.BZR_SVN,
128 RevisionControlSystems.HG):
126 return self.url129 return self.url
127 else:130 else:
128 raise AssertionError(131 raise AssertionError(
@@ -218,7 +221,8 @@
218 assert url is None221 assert url is None
219 elif rcs_type in (RevisionControlSystems.SVN,222 elif rcs_type in (RevisionControlSystems.SVN,
220 RevisionControlSystems.BZR_SVN,223 RevisionControlSystems.BZR_SVN,
221 RevisionControlSystems.GIT):224 RevisionControlSystems.GIT,
225 RevisionControlSystems.HG):
222 assert cvs_root is None and cvs_module is None226 assert cvs_root is None and cvs_module is None
223 assert url is not None227 assert url is not None
224 else:228 else:
@@ -226,8 +230,9 @@
226 "Don't know how to sanity check source details for unknown "230 "Don't know how to sanity check source details for unknown "
227 "rcs_type %s"%rcs_type)231 "rcs_type %s"%rcs_type)
228 if review_status is None:232 if review_status is None:
229 # Auto approve git imports.233 # Auto approve git and hg imports.
230 if rcs_type == RevisionControlSystems.GIT:234 if rcs_type in (
235 RevisionControlSystems.GIT, RevisionControlSystems.HG):
231 review_status = CodeImportReviewStatus.REVIEWED236 review_status = CodeImportReviewStatus.REVIEWED
232 else:237 else:
233 review_status = CodeImportReviewStatus.NEW238 review_status = CodeImportReviewStatus.NEW
234239
=== modified file 'lib/lp/code/model/codeimportevent.py'
--- lib/lp/code/model/codeimportevent.py 2010-01-13 02:53:14 +0000
+++ lib/lp/code/model/codeimportevent.py 2010-01-13 02:53:16 +0000
@@ -258,7 +258,8 @@
258 """Yield key-value tuples describing the source of the import."""258 """Yield key-value tuples describing the source of the import."""
259 if code_import.rcs_type in (RevisionControlSystems.SVN,259 if code_import.rcs_type in (RevisionControlSystems.SVN,
260 RevisionControlSystems.BZR_SVN,260 RevisionControlSystems.BZR_SVN,
261 RevisionControlSystems.GIT):261 RevisionControlSystems.GIT,
262 RevisionControlSystems.HG):
262 yield 'URL', code_import.url263 yield 'URL', code_import.url
263 elif code_import.rcs_type == RevisionControlSystems.CVS:264 elif code_import.rcs_type == RevisionControlSystems.CVS:
264 yield 'CVS_ROOT', code_import.cvs_root265 yield 'CVS_ROOT', code_import.cvs_root
265266
=== modified file 'lib/lp/code/model/tests/test_codeimport.py'
--- lib/lp/code/model/tests/test_codeimport.py 2010-01-13 02:53:14 +0000
+++ lib/lp/code/model/tests/test_codeimport.py 2010-01-13 02:53:16 +0000
@@ -24,26 +24,17 @@
24from lp.code.interfaces.codeimportjob import ICodeImportJobWorkflow24from lp.code.interfaces.codeimportjob import ICodeImportJobWorkflow
25from lp.testing import (25from lp.testing import (
26 login, login_person, logout, TestCaseWithFactory, time_counter)26 login, login_person, logout, TestCaseWithFactory, time_counter)
27from lp.testing.factory import LaunchpadObjectFactory
28from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities27from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
29from canonical.testing import (28from canonical.testing import (
30 DatabaseFunctionalLayer, LaunchpadFunctionalLayer,29 DatabaseFunctionalLayer, LaunchpadFunctionalLayer,
31 LaunchpadZopelessLayer)30 LaunchpadZopelessLayer)
3231
3332
34class TestCodeImportCreation(unittest.TestCase):33class TestCodeImportCreation(TestCaseWithFactory):
35 """Test the creation of CodeImports."""34 """Test the creation of CodeImports."""
3635
37 layer = DatabaseFunctionalLayer36 layer = DatabaseFunctionalLayer
3837
39 def setUp(self):
40 unittest.TestCase.setUp(self)
41 self.factory = LaunchpadObjectFactory()
42 login('no-priv@canonical.com')
43
44 def tearDown(self):
45 logout()
46
47 def test_new_svn_import(self):38 def test_new_svn_import(self):
48 """A new subversion code import should have NEW status."""39 """A new subversion code import should have NEW status."""
49 code_import = CodeImportSet().new(40 code_import = CodeImportSet().new(
@@ -56,7 +47,7 @@
56 CodeImportReviewStatus.NEW,47 CodeImportReviewStatus.NEW,
57 code_import.review_status)48 code_import.review_status)
58 # No job is created for the import.49 # No job is created for the import.
59 self.assertTrue(code_import.import_job is None)50 self.assertIs(None, code_import.import_job)
6051
61 def test_reviewed_svn_import(self):52 def test_reviewed_svn_import(self):
62 """A specific review status can be set for a new import."""53 """A specific review status can be set for a new import."""
@@ -71,7 +62,7 @@
71 CodeImportReviewStatus.REVIEWED,62 CodeImportReviewStatus.REVIEWED,
72 code_import.review_status)63 code_import.review_status)
73 # A job is created for the import.64 # A job is created for the import.
74 self.assertTrue(code_import.import_job is not None)65 self.assertIsNot(None, code_import.import_job)
7566
76 def test_new_cvs_import(self):67 def test_new_cvs_import(self):
77 """A new CVS code import should have NEW status."""68 """A new CVS code import should have NEW status."""
@@ -86,7 +77,7 @@
86 CodeImportReviewStatus.NEW,77 CodeImportReviewStatus.NEW,
87 code_import.review_status)78 code_import.review_status)
88 # No job is created for the import.79 # No job is created for the import.
89 self.assertTrue(code_import.import_job is None)80 self.assertIs(None, code_import.import_job)
9081
91 def test_reviewed_cvs_import(self):82 def test_reviewed_cvs_import(self):
92 """A specific review status can be set for a new import."""83 """A specific review status can be set for a new import."""
@@ -102,7 +93,7 @@
102 CodeImportReviewStatus.REVIEWED,93 CodeImportReviewStatus.REVIEWED,
103 code_import.review_status)94 code_import.review_status)
104 # A job is created for the import.95 # A job is created for the import.
105 self.assertTrue(code_import.import_job is not None)96 self.assertIsNot(None, code_import.import_job)
10697
107 def test_git_import_reviewed(self):98 def test_git_import_reviewed(self):
108 """A new git import is always reviewed by default."""99 """A new git import is always reviewed by default."""
@@ -117,23 +108,29 @@
117 CodeImportReviewStatus.REVIEWED,108 CodeImportReviewStatus.REVIEWED,
118 code_import.review_status)109 code_import.review_status)
119 # A job is created for the import.110 # A job is created for the import.
120 self.assertTrue(code_import.import_job is not None)111 self.assertIsNot(None, code_import.import_job)
121112
122113 def test_hg_import_reviewed(self):
123class TestCodeImportDeletion(unittest.TestCase):114 """A new hg import is always reviewed by default."""
115 code_import = CodeImportSet().new(
116 registrant=self.factory.makePerson(),
117 product=self.factory.makeProduct(),
118 branch_name='imported',
119 rcs_type=RevisionControlSystems.HG,
120 url=self.factory.getUniqueURL(),
121 review_status=None)
122 self.assertEqual(
123 CodeImportReviewStatus.REVIEWED,
124 code_import.review_status)
125 # No job is created for the import.
126 self.assertIsNot(None, code_import.import_job)
127
128
129class TestCodeImportDeletion(TestCaseWithFactory):
124 """Test the deletion of CodeImports."""130 """Test the deletion of CodeImports."""
125131
126 layer = LaunchpadFunctionalLayer132 layer = LaunchpadFunctionalLayer
127133
128 def setUp(self):
129 unittest.TestCase.setUp(self)
130 self.factory = LaunchpadObjectFactory()
131 # Log in a vcs import member.
132 login('david.allouche@canonical.com')
133
134 def tearDown(self):
135 logout()
136
137 def test_delete(self):134 def test_delete(self):
138 """Ensure CodeImport objects can be deleted via CodeImportSet."""135 """Ensure CodeImport objects can be deleted via CodeImportSet."""
139 code_import = self.factory.makeCodeImport()136 code_import = self.factory.makeCodeImport()
@@ -302,15 +299,13 @@
302 CodeImportReviewStatus.FAILING, code_import.review_status)299 CodeImportReviewStatus.FAILING, code_import.review_status)
303300
304301
305class TestCodeImportResultsAttribute(unittest.TestCase):302class TestCodeImportResultsAttribute(TestCaseWithFactory):
306 """Test the results attribute of a CodeImport."""303 """Test the results attribute of a CodeImport."""
307304
308 layer = LaunchpadFunctionalLayer305 layer = LaunchpadFunctionalLayer
309306
310 def setUp(self):307 def setUp(self):
311 unittest.TestCase.setUp(self)308 TestCaseWithFactory.setUp(self)
312 login('no-priv@canonical.com')
313 self.factory = LaunchpadObjectFactory()
314 self.code_import = self.factory.makeCodeImport()309 self.code_import = self.factory.makeCodeImport()
315310
316 def tearDown(self):311 def tearDown(self):
@@ -534,7 +529,7 @@
534def make_active_import(factory, project_name=None, product_name=None,529def make_active_import(factory, project_name=None, product_name=None,
535 branch_name=None, svn_branch_url=None,530 branch_name=None, svn_branch_url=None,
536 cvs_root=None, cvs_module=None, git_repo_url=None,531 cvs_root=None, cvs_module=None, git_repo_url=None,
537 last_update=None, rcs_type=None):532 hg_repo_url=None, last_update=None, rcs_type=None):
538 """Make a new CodeImport for a new Product, maybe in a new Project.533 """Make a new CodeImport for a new Product, maybe in a new Project.
539534
540 The import will be 'active' in the sense used by535 The import will be 'active' in the sense used by
@@ -549,7 +544,8 @@
549 code_import = factory.makeCodeImport(544 code_import = factory.makeCodeImport(
550 product=product, branch_name=branch_name,545 product=product, branch_name=branch_name,
551 svn_branch_url=svn_branch_url, cvs_root=cvs_root,546 svn_branch_url=svn_branch_url, cvs_root=cvs_root,
552 cvs_module=cvs_module, git_repo_url=git_repo_url, rcs_type=rcs_type)547 cvs_module=cvs_module, git_repo_url=git_repo_url,
548 hg_repo_url=hg_repo_url, rcs_type=None)
553 make_import_active(factory, code_import, last_update)549 make_import_active(factory, code_import, last_update)
554 return code_import550 return code_import
555551
556552
=== modified file 'lib/lp/code/stories/codeimport/xx-admin-codeimport.txt'
--- lib/lp/code/stories/codeimport/xx-admin-codeimport.txt 2010-01-13 02:53:14 +0000
+++ lib/lp/code/stories/codeimport/xx-admin-codeimport.txt 2010-01-13 02:53:16 +0000
@@ -32,6 +32,12 @@
32 ... git_repo_url="git://git.example.org/fooix")32 ... git_repo_url="git://git.example.org/fooix")
33 >>> git_import_location = str(canonical_url(git_import.branch))33 >>> git_import_location = str(canonical_url(git_import.branch))
34 >>> git_import_branch_unique_name = git_import.branch.unique_name34 >>> git_import_branch_unique_name = git_import.branch.unique_name
35
36 >>> hg_import = factory.makeCodeImport(
37 ... hg_repo_url="http://hg.example.org/bar")
38 >>> hg_import_location = str(canonical_url(hg_import.branch))
39 >>> hg_import_branch_unique_name = hg_import.branch.unique_name
40
35 >>> logout()41 >>> logout()
3642
37 >>> import_browser = setupBrowser(43 >>> import_browser = setupBrowser(
@@ -97,6 +103,12 @@
97 >>> print_form_fields(import_browser)103 >>> print_form_fields(import_browser)
98 field.url: git://git.example.org/fooix104 field.url: git://git.example.org/fooix
99105
106 >>> import_browser.open(hg_import_location)
107 >>> import_browser.getLink('Edit import source or review import').click()
108 >>> print_form_fields(import_browser)
109 field.url: http://hg.example.org/bar
110
111
100Editing the import location112Editing the import location
101+++++++++++++++++++++++++++113+++++++++++++++++++++++++++
102114
@@ -135,7 +147,7 @@
135 ... print extract_text(message)147 ... print extract_text(message)
136 The code import has been updated.148 The code import has been updated.
137149
138and Git imports.150Git imports,
139151
140 >>> import_browser.open(git_import_location + '/+edit-import')152 >>> import_browser.open(git_import_location + '/+edit-import')
141 >>> import_browser.getControl('URL').value = \153 >>> import_browser.getControl('URL').value = \
@@ -145,6 +157,15 @@
145 ... print extract_text(message)157 ... print extract_text(message)
146 The code import has been updated.158 The code import has been updated.
147159
160and Mercurial imports.
161
162 >>> import_browser.open(hg_import_location + '/+edit-import')
163 >>> import_browser.getControl('URL').value = \
164 ... 'http://metal.example.org/bar'
165 >>> import_browser.getControl('Update').click()
166 >>> for message in get_feedback_messages(import_browser.contents):
167 ... print extract_text(message)
168 The code import has been updated.
148169
149Approving an import170Approving an import
150+++++++++++++++++++171+++++++++++++++++++
151172
=== modified file 'lib/lp/code/stories/codeimport/xx-codeimport-list.txt'
--- lib/lp/code/stories/codeimport/xx-codeimport-list.txt 2009-12-08 04:31:34 +0000
+++ lib/lp/code/stories/codeimport/xx-codeimport-list.txt 2010-01-13 02:53:16 +0000
@@ -34,14 +34,18 @@
34 ... factory, product_name="herproj", branch_name="master",34 ... factory, product_name="herproj", branch_name="master",
35 ... git_repo_url="git://git.example.org/herproj",35 ... git_repo_url="git://git.example.org/herproj",
36 ... last_update=datetime.datetime(2007, 1, 4, tzinfo=pytz.UTC))36 ... last_update=datetime.datetime(2007, 1, 4, tzinfo=pytz.UTC))
37 >>> active_hg_import = make_active_import(
38 ... factory, product_name="hg-proj", branch_name="tip",
39 ... hg_repo_url="http://hg.example.org/proj",
40 ... last_update=datetime.datetime(2007, 1, 5, tzinfo=pytz.UTC))
37 >>> len(list(code_import_set.getActiveImports()))41 >>> len(list(code_import_set.getActiveImports()))
38 442 5
39 >>> logout()43 >>> logout()
4044
41The page is linked to from the "$N imported branches" text.45The page is linked to from the "$N imported branches" text.
4246
43 >>> browser.open('http://code.launchpad.dev')47 >>> browser.open('http://code.launchpad.dev')
44 >>> browser.getLink('4 imported branches').click()48 >>> browser.getLink('5 imported branches').click()
45 >>> print browser.title49 >>> print browser.title
46 Available code imports50 Available code imports
4751
@@ -54,6 +58,7 @@
54 >>> print_import_table()58 >>> print_import_table()
55 Project Branch Name Source Details Last Updated59 Project Branch Name Source Details Last Updated
56 herproj master git://git.example.org/herproj 2007-01-0460 herproj master git://git.example.org/herproj 2007-01-04
61 hg-proj tip http://hg.example.org/proj 2007-01-05
57 hisproj main :pserver:anon@example.com:/cvs hisproj 2007-01-0362 hisproj main :pserver:anon@example.com:/cvs hisproj 2007-01-03
58 myproject trunk http://example.com/svn/myproject/trunk 2007-01-0163 myproject trunk http://example.com/svn/myproject/trunk 2007-01-01
59 ourproject trunk http://example.com/bzr-svn/myproject/trunk 2007-01-0264 ourproject trunk http://example.com/bzr-svn/myproject/trunk 2007-01-02
6065
=== modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
--- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-13 02:53:14 +0000
+++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-13 02:53:16 +0000
@@ -32,6 +32,7 @@
3232
33 >>> print_radio_button_field(browser.contents, "rcs_type")33 >>> print_radio_button_field(browser.contents, "rcs_type")
34 ( ) Git34 ( ) Git
35 ( ) Mercurial
35 (*) Subversion36 (*) Subversion
36 ( ) CVS37 ( ) CVS
3738
@@ -63,24 +64,18 @@
63 >>> print svn_span['title']64 >>> print svn_span['title']
64 Subversion via bzr-svn65 Subversion via bzr-svn
6566
67
66Requesting a Git import68Requesting a Git import
67=======================69=======================
6870
69The default foreign VCS type is Subversion.
70
71 >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
72 >>> print_radio_button_field(browser.contents, "rcs_type")
73 ( ) Git
74 (*) Subversion
75 ( ) CVS
76
77The user is required to enter a project that the import is for,71The user is required to enter a project that the import is for,
78a name for the import branch, and a subversion branch location.72a name for the import branch, and a subversion branch location.
7973
74 >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
80 >>> browser.getControl('Project').value = "firefox"75 >>> browser.getControl('Project').value = "firefox"
81 >>> browser.getControl('Branch Name').value = "git-import"76 >>> browser.getControl('Branch Name').value = "git-import"
82 >>> browser.getControl('Git').click()77 >>> browser.getControl('Git').click()
83 >>> browser.getControl('Repo URL').value = (78 >>> browser.getControl('Repo URL', index=0).value = (
84 ... "git://example.com/firefox.git")79 ... "git://example.com/firefox.git")
85 >>> browser.getControl('Request Import').click()80 >>> browser.getControl('Request Import').click()
8681
@@ -88,11 +83,34 @@
8883
89 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))84 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))
90 Import Status: Reviewed85 Import Status: Reviewed
91 This branch is an import of the MASTER branch from the Git repo at86 This branch is an import of the HEAD branch of the Git repository at
92 git://example.com/firefox.git.87 git://example.com/firefox.git.
93 The next import is scheduled to run as soon as possible.88 The next import is scheduled to run as soon as possible.
9489
9590
91Requesting a Mercurial import
92=======================
93
94The user is required to enter a project that the import is for,
95a name for the import branch, and a mercurial branch location.
96
97 >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
98 >>> browser.getControl('Project').value = "firefox"
99 >>> browser.getControl('Branch Name').value = "hg-import"
100 >>> browser.getControl('Mercurial').click()
101 >>> browser.getControl('Repo URL', index=1).value = (
102 ... "http://example.com/firefox.hg")
103 >>> browser.getControl('Request Import').click()
104
105When the user clicks continue, the approved import branch is created
106
107 >>> print extract_text(find_tag_by_id(browser.contents, "import-details"))
108 Import Status: Reviewed
109 This branch is an import of the tip branch of the Mercurial repository at
110 http://example.com/firefox.hg.
111 The next import is scheduled to run as soon as possible.
112
113
96Requesting a CVS import114Requesting a CVS import
97=======================115=======================
98116
99117
=== modified file 'lib/lp/code/templates/branch-import-details.pt'
--- lib/lp/code/templates/branch-import-details.pt 2010-01-13 02:53:14 +0000
+++ lib/lp/code/templates/branch-import-details.pt 2010-01-13 02:53:16 +0000
@@ -40,11 +40,17 @@
40 </div>40 </div>
4141
42 <tal:git-import condition="code_import/rcs_type/enumvalue:GIT">42 <tal:git-import condition="code_import/rcs_type/enumvalue:GIT">
43 <p>This branch is an import of the MASTER branch from the Git repo at43 <p>This branch is an import of the HEAD branch of the Git repository at
44 <span tal:replace="code_import/url" />.44 <span tal:replace="code_import/url" />.
45 </p>45 </p>
46 </tal:git-import>46 </tal:git-import>
4747
48 <tal:hg-import condition="code_import/rcs_type/enumvalue:HG">
49 <p>This branch is an import of the tip branch of the Mercurial repository at
50 <span tal:replace="code_import/url" />.
51 </p>
52 </tal:hg-import>
53
48 <tal:svn-import condition="view/is_svn_import">54 <tal:svn-import condition="view/is_svn_import">
49 <p id="svn-import-details">55 <p id="svn-import-details">
50 This branch is an import of the56 This branch is an import of the
5157
=== modified file 'lib/lp/code/templates/codeimport-new.pt'
--- lib/lp/code/templates/codeimport-new.pt 2009-12-08 02:32:03 +0000
+++ lib/lp/code/templates/codeimport-new.pt 2010-01-13 02:53:16 +0000
@@ -62,6 +62,20 @@
62 <tr>62 <tr>
63 <td>63 <td>
64 <label>64 <label>
65 <input tal:replace="structure view/rcs_type_hg" />
66 Mercurial
67 </label>
68 <table class="importdetails">
69 <tal:widget define="widget nocall:view/widgets/hg_repo_url">
70 <metal:block use-macro="context/@@launchpad_form/widget_row" />
71 </tal:widget>
72 </table>
73 </td>
74 </tr>
75
76 <tr>
77 <td>
78 <label>
65 <input tal:replace="structure view/rcs_type_svn" />79 <input tal:replace="structure view/rcs_type_svn" />
66 Subversion80 Subversion
67 </label>81 </label>
@@ -108,6 +122,8 @@
108 }122 }
109 // GIT123 // GIT
110 updateField(form['field.git_repo_url'], rcs_type == 'GIT');124 updateField(form['field.git_repo_url'], rcs_type == 'GIT');
125 // HG
126 updateField(form['field.hg_repo_url'], rcs_type == 'HG');
111 // CVS127 // CVS
112 updateField(form['field.cvs_root'], rcs_type == 'CVS');128 updateField(form['field.cvs_root'], rcs_type == 'CVS');
113 updateField(form['field.cvs_module'], rcs_type == 'CVS');129 updateField(form['field.cvs_module'], rcs_type == 'CVS');
114130
=== modified file 'lib/lp/codehosting/codeimport/tests/servers.py'
--- lib/lp/codehosting/codeimport/tests/servers.py 2009-12-04 04:08:12 +0000
+++ lib/lp/codehosting/codeimport/tests/servers.py 2010-01-13 02:53:16 +0000
@@ -8,6 +8,7 @@
8__all__ = [8__all__ = [
9 'CVSServer',9 'CVSServer',
10 'GitServer',10 'GitServer',
11 'MercurialServer',
11 'SubversionServer',12 'SubversionServer',
12 ]13 ]
1314
@@ -210,3 +211,23 @@
210 builder.finish()211 builder.finish()
211 finally:212 finally:
212 os.chdir(wd)213 os.chdir(wd)
214
215
216class MercurialServer(Server):
217
218 def __init__(self, repo_url):
219 super(MercurialServer, self).__init__()
220 self.repo_url = repo_url
221
222 def makeRepo(self, tree_contents):
223 from mercurial.ui import ui
224 from mercurial.localrepo import localrepository
225 repo = localrepository(ui(), self.repo_url, create=1)
226 for filename, contents in tree_contents:
227 f = open(os.path.join(self.repo_url, filename), 'w')
228 try:
229 f.write(contents)
230 finally:
231 f.close()
232 repo.add([filename])
233 repo.commit(text='<The commit message>', user='jane Foo <joe@foo.com>')
213234
=== modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py'
--- lib/lp/codehosting/codeimport/tests/test_worker.py 2010-01-13 02:53:14 +0000
+++ lib/lp/codehosting/codeimport/tests/test_worker.py 2010-01-13 02:53:16 +0000
@@ -9,7 +9,6 @@
9import os9import os
10import shutil10import shutil
11import subprocess11import subprocess
12import sys
13import tempfile12import tempfile
14import time13import time
15import unittest14import unittest
@@ -32,10 +31,10 @@
32from lp.codehosting import load_optional_plugin31from lp.codehosting import load_optional_plugin
33from lp.codehosting.codeimport.worker import (32from lp.codehosting.codeimport.worker import (
34 BazaarBranchStore, BzrSvnImportWorker, CSCVSImportWorker,33 BazaarBranchStore, BzrSvnImportWorker, CSCVSImportWorker,
35 ForeignTreeStore, GitImportWorker, ImportDataStore, ImportWorker,34 ForeignTreeStore, GitImportWorker, HgImportWorker, ImportDataStore,
36 get_default_bazaar_branch_store)35 ImportWorker, get_default_bazaar_branch_store)
37from lp.codehosting.codeimport.tests.servers import (36from lp.codehosting.codeimport.tests.servers import (
38 CVSServer, GitServer, SubversionServer)37 CVSServer, GitServer, MercurialServer, SubversionServer)
39from lp.codehosting.tests.helpers import (38from lp.codehosting.tests.helpers import (
40 create_branch_with_one_revision)39 create_branch_with_one_revision)
41from lp.testing.factory import LaunchpadObjectFactory40from lp.testing.factory import LaunchpadObjectFactory
@@ -822,7 +821,7 @@
822 # import should be rejected.821 # import should be rejected.
823 args = {'rcstype': self.rcstype}822 args = {'rcstype': self.rcstype}
824 reference_url = self.createBranchReference()823 reference_url = self.createBranchReference()
825 if self.rcstype in ('git', 'bzr-svn'):824 if self.rcstype in ('git', 'bzr-svn', 'hg'):
826 args['url'] = reference_url825 args['url'] = reference_url
827 else:826 else:
828 raise AssertionError("unexpected rcs_type %r" % self.rcs_type)827 raise AssertionError("unexpected rcs_type %r" % self.rcs_type)
@@ -886,6 +885,57 @@
886 rcstype='git', url=repository_path)885 rcstype='git', url=repository_path)
887886
888887
888class TestMercurialImport(WorkerTest, TestActualImportMixin,
889 PullingImportWorkerTests):
890
891 rcstype = 'hg'
892
893 def setUp(self):
894 super(TestMercurialImport, self).setUp()
895 load_optional_plugin('hg')
896 self.setUpImport()
897
898 def tearDown(self):
899 """Clear bzr-hg's cache of sqlite connections.
900
901 This is rather obscure: different test runs tend to re-use the same
902 paths on disk, which confuses bzr-hg as it keeps a cache that maps
903 paths to database connections, which happily returns the connection
904 that corresponds to a path that no longer exists.
905 """
906 from bzrlib.plugins.hg.idmap import mapdbs
907 mapdbs().clear()
908 WorkerTest.tearDown(self)
909
910 def makeImportWorker(self, source_details):
911 """Make a new `ImportWorker`."""
912 return HgImportWorker(
913 source_details, self.get_transport('import_data'),
914 self.bazaar_store, logging.getLogger())
915
916 def makeForeignCommit(self, source_details):
917 """Change the foreign tree, generating exactly one commit."""
918 from mercurial.ui import ui
919 from mercurial.localrepo import localrepository
920 repo = localrepository(ui(), source_details.url)
921 repo.commit(text="hello world!", user="Jane Random Hacker", force=1)
922 self.foreign_commit_count += 1
923
924 def makeSourceDetails(self, branch_name, files):
925 """Make a Mercurial `CodeImportSourceDetails` pointing at a real repo.
926 """
927 repository_path = self.makeTemporaryDirectory()
928 hg_server = MercurialServer(repository_path)
929 hg_server.setUp()
930 self.addCleanup(hg_server.tearDown)
931
932 hg_server.makeRepo(files)
933 self.foreign_commit_count = 1
934
935 return self.factory.makeCodeImportSourceDetails(
936 rcstype='hg', url=repository_path)
937
938
889class TestBzrSvnImport(WorkerTest, SubversionImportHelpers,939class TestBzrSvnImport(WorkerTest, SubversionImportHelpers,
890 TestActualImportMixin, PullingImportWorkerTests):940 TestActualImportMixin, PullingImportWorkerTests):
891941
892942
=== modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py'
--- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-01-13 02:53:14 +0000
+++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-01-13 02:53:16 +0000
@@ -43,7 +43,7 @@
43 CodeImportWorkerMonitor, CodeImportWorkerMonitorProtocol, ExitQuietly,43 CodeImportWorkerMonitor, CodeImportWorkerMonitorProtocol, ExitQuietly,
44 read_only_transaction)44 read_only_transaction)
45from lp.codehosting.codeimport.tests.servers import (45from lp.codehosting.codeimport.tests.servers import (
46 CVSServer, GitServer, SubversionServer)46 CVSServer, GitServer, MercurialServer, SubversionServer)
47from lp.codehosting.codeimport.tests.test_worker import (47from lp.codehosting.codeimport.tests.test_worker import (
48 clean_up_default_stores_for_import)48 clean_up_default_stores_for_import)
49from lp.testing import login, logout49from lp.testing import login, logout
@@ -501,6 +501,18 @@
501501
502 return self.factory.makeCodeImport(git_repo_url=self.repo_path)502 return self.factory.makeCodeImport(git_repo_url=self.repo_path)
503503
504 def makeHgCodeImport(self):
505 """Make a `CodeImport` that points to a real Mercurial repository."""
506 load_optional_plugin('hg')
507 self.hg_server = MercurialServer(self.repo_path)
508 self.hg_server.setUp()
509 self.addCleanup(self.hg_server.tearDown)
510
511 self.hg_server.makeRepo([('README', 'contents')])
512 self.foreign_commit_count = 1
513
514 return self.factory.makeCodeImport(hg_repo_url=self.repo_path)
515
504 def getStartedJobForImport(self, code_import):516 def getStartedJobForImport(self, code_import):
505 """Get a started `CodeImportJob` for `code_import`.517 """Get a started `CodeImportJob` for `code_import`.
506518
@@ -592,6 +604,15 @@
592 result = self.performImport(job_id)604 result = self.performImport(job_id)
593 return result.addCallback(self.assertImported, code_import_id)605 return result.addCallback(self.assertImported, code_import_id)
594606
607 def test_import_hg(self):
608 # Create a Mercurial CodeImport and import it.
609 job = self.getStartedJobForImport(self.makeHgCodeImport())
610 code_import_id = job.code_import.id
611 job_id = job.id
612 self.layer.txn.commit()
613 result = self.performImport(job_id)
614 return result.addCallback(self.assertImported, code_import_id)
615
595 def test_import_bzrsvn(self):616 def test_import_bzrsvn(self):
596 # Create a Subversion-via-bzr-svn CodeImport and import it.617 # Create a Subversion-via-bzr-svn CodeImport and import it.
597 job = self.getStartedJobForImport(self.makeBzrSvnCodeImport())618 job = self.getStartedJobForImport(self.makeBzrSvnCodeImport())
598619
=== modified file 'lib/lp/codehosting/codeimport/worker.py'
--- lib/lp/codehosting/codeimport/worker.py 2010-01-13 02:53:14 +0000
+++ lib/lp/codehosting/codeimport/worker.py 2010-01-13 02:53:16 +0000
@@ -11,6 +11,7 @@
11 'CodeImportSourceDetails',11 'CodeImportSourceDetails',
12 'ForeignTreeStore',12 'ForeignTreeStore',
13 'GitImportWorker',13 'GitImportWorker',
14 'HgImportWorker',
14 'ImportWorker',15 'ImportWorker',
15 'get_default_bazaar_branch_store',16 'get_default_bazaar_branch_store',
16 ]17 ]
@@ -126,7 +127,7 @@
126 """Convert command line-style arguments to an instance."""127 """Convert command line-style arguments to an instance."""
127 branch_id = int(arguments.pop(0))128 branch_id = int(arguments.pop(0))
128 rcstype = arguments.pop(0)129 rcstype = arguments.pop(0)
129 if rcstype in ['svn', 'bzr-svn', 'git']:130 if rcstype in ['svn', 'bzr-svn', 'git', 'hg']:
130 [url] = arguments131 [url] = arguments
131 cvs_root = cvs_module = None132 cvs_root = cvs_module = None
132 elif rcstype == 'cvs':133 elif rcstype == 'cvs':
@@ -151,6 +152,8 @@
151 cvs_module=str(code_import.cvs_module))152 cvs_module=str(code_import.cvs_module))
152 elif code_import.rcs_type == RevisionControlSystems.GIT:153 elif code_import.rcs_type == RevisionControlSystems.GIT:
153 return cls(branch_id, 'git', str(code_import.url))154 return cls(branch_id, 'git', str(code_import.url))
155 elif code_import.rcs_type == RevisionControlSystems.HG:
156 return cls(branch_id, 'hg', str(code_import.url))
154 else:157 else:
155 raise AssertionError("Unknown rcstype %r." % code_import.rcs_type)158 raise AssertionError("Unknown rcstype %r." % code_import.rcs_type)
156159
@@ -158,7 +161,7 @@
158 """Return a list of arguments suitable for passing to a child process.161 """Return a list of arguments suitable for passing to a child process.
159 """162 """
160 result = [str(self.branch_id), self.rcstype]163 result = [str(self.branch_id), self.rcstype]
161 if self.rcstype in ['svn', 'bzr-svn', 'git']:164 if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg']:
162 result.append(self.url)165 result.append(self.url)
163 elif self.rcstype == 'cvs':166 elif self.rcstype == 'cvs':
164 result.append(self.cvs_root)167 result.append(self.cvs_root)
@@ -544,6 +547,45 @@
544 'git.db', bazaar_tree.branch.repository._transport)547 'git.db', bazaar_tree.branch.repository._transport)
545548
546549
550class HgImportWorker(PullingImportWorker):
551 """An import worker for Mercurial imports.
552
553 The only behaviour we add is preserving the id-sha map between runs.
554 """
555
556 db_file = 'hg-v2.db'
557
558 @property
559 def format_classes(self):
560 """See `PullingImportWorker.opening_format`."""
561 # We only return HgLocalRepository for tests.
562 from bzrlib.plugins.hg import HgBzrDirFormat
563 return [HgBzrDirFormat]
564
565 def getBazaarWorkingTree(self):
566 """See `ImportWorker.getBazaarWorkingTree`.
567
568 In addition to the superclass' behaviour, we retrieve the 'hg-v2.db'
569 map from the import data store and put it where bzr-hg will find
570 it in the Bazaar tree, that is at '.bzr/repository/hg-v2.db'.
571 """
572 tree = PullingImportWorker.getBazaarWorkingTree(self)
573 self.import_data_store.fetch(
574 self.db_file, tree.branch.repository._transport)
575 return tree
576
577 def pushBazaarWorkingTree(self, bazaar_tree):
578 """See `ImportWorker.pushBazaarWorkingTree`.
579
580 In addition to the superclass' behaviour, we store the 'hg-v2.db' shamap
581 that bzr-hg will have created at .bzr/repository/hg-v2.db into the
582 import data store.
583 """
584 PullingImportWorker.pushBazaarWorkingTree(self, bazaar_tree)
585 self.import_data_store.put(
586 self.db_file, bazaar_tree.branch.repository._transport)
587
588
547class BzrSvnImportWorker(PullingImportWorker):589class BzrSvnImportWorker(PullingImportWorker):
548 """An import worker for importing Subversion via bzr-svn."""590 """An import worker for importing Subversion via bzr-svn."""
549591
550592
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-01-13 02:53:14 +0000
+++ lib/lp/testing/factory.py 2010-01-13 02:53:16 +0000
@@ -1216,14 +1216,16 @@
12161216
1217 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,1217 def makeCodeImport(self, svn_branch_url=None, cvs_root=None,
1218 cvs_module=None, product=None, branch_name=None,1218 cvs_module=None, product=None, branch_name=None,
1219 git_repo_url=None, registrant=None, rcs_type=None):1219 git_repo_url=None, hg_repo_url=None, registrant=None,
1220 rcs_type=None):
1220 """Create and return a new, arbitrary code import.1221 """Create and return a new, arbitrary code import.
12211222
1222 The type of code import will be inferred from the source details1223 The type of code import will be inferred from the source details
1223 passed in, but defaults to a Subversion import from an arbitrary1224 passed in, but defaults to a Subversion import from an arbitrary
1224 unique URL.1225 unique URL.
1225 """1226 """
1226 if svn_branch_url is cvs_root is cvs_module is git_repo_url is None:1227 if (svn_branch_url is cvs_root is cvs_module is git_repo_url is hg_repo_url
1228 is None):
1227 svn_branch_url = self.getUniqueURL()1229 svn_branch_url = self.getUniqueURL()
12281230
1229 if product is None:1231 if product is None:
@@ -1249,6 +1251,11 @@
1249 registrant, product, branch_name,1251 registrant, product, branch_name,
1250 rcs_type=RevisionControlSystems.GIT,1252 rcs_type=RevisionControlSystems.GIT,
1251 url=git_repo_url)1253 url=git_repo_url)
1254 elif hg_repo_url is not None:
1255 return code_import_set.new(
1256 registrant, product, branch_name,
1257 rcs_type=RevisionControlSystems.HG,
1258 url=hg_repo_url)
1252 else:1259 else:
1253 assert rcs_type in (None, RevisionControlSystems.CVS)1260 assert rcs_type in (None, RevisionControlSystems.CVS)
1254 return code_import_set.new(1261 return code_import_set.new(
@@ -1322,7 +1329,7 @@
1322 branch_id = self.getUniqueInteger()1329 branch_id = self.getUniqueInteger()
1323 if rcstype is None:1330 if rcstype is None:
1324 rcstype = 'svn'1331 rcstype = 'svn'
1325 if rcstype in ['svn', 'bzr-svn']:1332 if rcstype in ['svn', 'bzr-svn', 'hg']:
1326 assert cvs_root is cvs_module is None1333 assert cvs_root is cvs_module is None
1327 if url is None:1334 if url is None:
1328 url = self.getUniqueURL()1335 url = self.getUniqueURL()
13291336
=== added symlink 'lib/mercurial'
=== target is u'../sourcecode/mercurial/mercurial'
=== added symlink 'optionalbzrplugins/hg'
=== target is u'../sourcecode/bzr-hg'
=== modified file 'scripts/code-import-worker.py'
--- scripts/code-import-worker.py 2009-11-17 02:49:41 +0000
+++ scripts/code-import-worker.py 2010-01-13 02:53:16 +0000
@@ -26,7 +26,7 @@
26from lp.codehosting import load_optional_plugin26from lp.codehosting import load_optional_plugin
27from lp.codehosting.codeimport.worker import (27from lp.codehosting.codeimport.worker import (
28 BzrSvnImportWorker, CSCVSImportWorker, CodeImportSourceDetails,28 BzrSvnImportWorker, CSCVSImportWorker, CodeImportSourceDetails,
29 GitImportWorker, get_default_bazaar_branch_store)29 GitImportWorker, HgImportWorker, get_default_bazaar_branch_store)
30from canonical.launchpad import scripts30from canonical.launchpad import scripts
3131
3232
@@ -46,6 +46,9 @@
46 elif source_details.rcstype == 'bzr-svn':46 elif source_details.rcstype == 'bzr-svn':
47 load_optional_plugin('svn')47 load_optional_plugin('svn')
48 import_worker_cls = BzrSvnImportWorker48 import_worker_cls = BzrSvnImportWorker
49 elif source_details.rcstype == 'hg':
50 load_optional_plugin('hg')
51 import_worker_cls = HgImportWorker
49 else:52 else:
50 if source_details.rcstype not in ['cvs', 'svn']:53 if source_details.rcstype not in ['cvs', 'svn']:
51 raise AssertionError(54 raise AssertionError(
5255
=== modified file 'setup.py'
--- setup.py 2010-01-12 01:45:22 +0000
+++ setup.py 2010-01-13 02:53:16 +0000
@@ -43,6 +43,7 @@
43 'lazr.uri',43 'lazr.uri',
44 'lazr-js',44 'lazr-js',
45 'mechanize',45 'mechanize',
46 'mercurial',
46 'mocker',47 'mocker',
47 'oauth',48 'oauth',
48 'paramiko',49 'paramiko',
4950
=== modified file 'utilities/sourcedeps.conf'
--- utilities/sourcedeps.conf 2009-12-02 02:46:05 +0000
+++ utilities/sourcedeps.conf 2010-01-13 02:53:16 +0000
@@ -1,4 +1,5 @@
1bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=2481bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=248
2bzr-hg lp:~launchpad-pqm/bzr-hg/devel;revno=281
2bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=473bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=47
3bzr-svn lp:~launchpad-pqm/bzr-svn/devel;revno=27064bzr-svn lp:~launchpad-pqm/bzr-svn/devel;revno=2706
4cscvs lp:~launchpad-pqm/launchpad-cscvs/devel;revno=4305cscvs lp:~launchpad-pqm/launchpad-cscvs/devel;revno=430
56
=== modified file 'versions.cfg'
--- versions.cfg 2010-01-12 01:45:22 +0000
+++ versions.cfg 2010-01-13 02:53:16 +0000
@@ -40,6 +40,7 @@
40lazr-js = 0.9.2DEV40lazr-js = 0.9.2DEV
41martian = 0.1141martian = 0.11
42mechanize = 0.1.1142mechanize = 0.1.11
43mercurial = 1.3.1
43mocker = 0.10.144mocker = 0.10.1
44mozrunner = 1.3.445mozrunner = 1.3.4
45oauth = 1.046oauth = 1.0

Subscribers

People subscribed via source and target branches

to status/vote changes: