Merge lp:~jelmer/bzr-hg/sprout into lp:~launchpad-pqm/bzr-hg/devel
- sprout
- Merge into devel
Proposed by
Jelmer Vernooij
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~jelmer/bzr-hg/sprout | ||||
Merge into: | lp:~launchpad-pqm/bzr-hg/devel | ||||
Diff against target: |
2260 lines (+1096/-407) 27 files modified
.bzrignore (+1/-0) .testr.conf (+4/-0) HACKING (+1/-3) Makefile (+1/-1) NEWS (+23/-2) __init__.py (+144/-272) branch.py (+131/-11) dir.py (+395/-0) fetch.py (+133/-74) info.py (+4/-3) mapping.py (+11/-4) overlay.py (+1/-1) parsers.py (+33/-0) repository.py (+47/-5) setup.py (+1/-1) tests/__init__.py (+1/-0) tests/test_branch.py (+4/-4) tests/test_dir.py (+13/-6) tests/test_fetch.py (+53/-0) tests/test_mapping.py (+33/-1) tests/test_parsers.py (+12/-1) tests/test_pull.py (+5/-5) tests/test_repository.py (+3/-3) ui.py (+4/-4) util.py (+1/-1) versionedfiles.py (+2/-0) workingtree.py (+35/-5) |
||||
To merge this branch: | bzr merge lp:~jelmer/bzr-hg/sprout | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Launchpad PQM Bot | Pending | ||
Review via email: mp+54614@code.launchpad.net |
This proposal has been superseded by a proposal from 2011-03-23.
Commit message
Description of the change
Provide a custom sprout implementation. Fixes compatibility with newer versions of bzr.
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2009-05-21 15:53:00 +0000 | |||
3 | +++ .bzrignore 2011-03-23 21:41:04 +0000 | |||
4 | @@ -1,3 +1,4 @@ | |||
5 | 1 | *.pyc | 1 | *.pyc |
6 | 2 | build | 2 | build |
7 | 3 | .plugins | 3 | .plugins |
8 | 4 | .testrepository | ||
9 | 4 | 5 | ||
10 | === added file '.testr.conf' | |||
11 | --- .testr.conf 1970-01-01 00:00:00 +0000 | |||
12 | +++ .testr.conf 2011-03-23 21:41:04 +0000 | |||
13 | @@ -0,0 +1,4 @@ | |||
14 | 1 | [DEFAULT] | ||
15 | 2 | test_command=BZR_PLUGINS_AT=hg@`pwd` bzr selftest ^bzrlib.plugins.hg. Hg Mercurial --subunit $IDOPTION $LISTOPT | ||
16 | 3 | test_id_option=--load-list $IDFILE | ||
17 | 4 | test_list_option=--list | ||
18 | 0 | 5 | ||
19 | === modified file 'HACKING' | |||
20 | --- HACKING 2009-10-10 01:17:00 +0000 | |||
21 | +++ HACKING 2011-03-23 21:41:04 +0000 | |||
22 | @@ -3,8 +3,7 @@ | |||
23 | 3 | 3 | ||
24 | 4 | Unit testing | 4 | Unit testing |
25 | 5 | ~~~~~~~~~~~~ | 5 | ~~~~~~~~~~~~ |
28 | 6 | To run the bzr-hg testsuite, simply run | 6 | To run the bzr-hg testsuite, simply run 'bzr selftest -s bp.hg' or |
27 | 7 | 'bzr selftest --starting-with=bazaar.plugins.hg' or | ||
29 | 8 | run 'make check' in the top-level bzr-hg directory. | 7 | run 'make check' in the top-level bzr-hg directory. |
30 | 9 | 8 | ||
31 | 10 | Unavailable bzrlib API's | 9 | Unavailable bzrlib API's |
32 | @@ -24,4 +23,3 @@ | |||
33 | 24 | Coding Style, etc | 23 | Coding Style, etc |
34 | 25 | ~~~~~~~~~~~~~~~~~ | 24 | ~~~~~~~~~~~~~~~~~ |
35 | 26 | Please refer to HACKING in the Bazaar source distribution. | 25 | Please refer to HACKING in the Bazaar source distribution. |
36 | 27 | |||
37 | 28 | 26 | ||
38 | === modified file 'Makefile' | |||
39 | --- Makefile 2010-07-28 16:15:17 +0000 | |||
40 | +++ Makefile 2011-03-23 21:41:04 +0000 | |||
41 | @@ -6,7 +6,7 @@ | |||
42 | 6 | CTAGS ?= ctags | 6 | CTAGS ?= ctags |
43 | 7 | PYLINT ?= pylint | 7 | PYLINT ?= pylint |
44 | 8 | RST2HTML ?= rst2html | 8 | RST2HTML ?= rst2html |
46 | 9 | TESTS ?= bzrlib.tests.per_foreign_vcs.*Hg bzrlib.plugins.hg | 9 | TESTS ?= -s bt.per_foreign_vcs.*Hg -s bp.hg |
47 | 10 | 10 | ||
48 | 11 | all:: build | 11 | all:: build |
49 | 12 | 12 | ||
50 | 13 | 13 | ||
51 | === modified file 'NEWS' | |||
52 | --- NEWS 2010-08-14 00:41:20 +0000 | |||
53 | +++ NEWS 2011-03-23 21:41:04 +0000 | |||
54 | @@ -27,9 +27,14 @@ | |||
55 | 27 | * Put idmap information in a bzr-hg-specific directory. (Jelmer Vernooij, | 27 | * Put idmap information in a bzr-hg-specific directory. (Jelmer Vernooij, |
56 | 28 | #599397) | 28 | #599397) |
57 | 29 | 29 | ||
58 | 30 | * Add basic support for tags. (Jelmer Vernooij) | ||
59 | 31 | |||
60 | 32 | * Check for Mercurial repository before checking for Bazaar repository. | ||
61 | 33 | (Jelmer Vernooij, #674581, #688455) | ||
62 | 34 | |||
63 | 30 | BUG FIXES | 35 | BUG FIXES |
64 | 31 | 36 | ||
66 | 32 | * Mark as compatible with Bazaar 2.1. (Jelmer Vernooij) | 37 | * Mark as compatible with Bazaar 2.1, 2.2, 2.3 and 2.4. (Jelmer Vernooij) |
67 | 33 | 38 | ||
68 | 34 | * Check for Mercurial version before attempting to import it. | 39 | * Check for Mercurial version before attempting to import it. |
69 | 35 | (#432100, Jelmer Vernooij) | 40 | (#432100, Jelmer Vernooij) |
70 | @@ -38,7 +43,7 @@ | |||
71 | 38 | 43 | ||
72 | 39 | * Remove empty directories during fetch. (Jelmer Vernooij) | 44 | * Remove empty directories during fetch. (Jelmer Vernooij) |
73 | 40 | 45 | ||
75 | 41 | * Mark Mercurial 1.6 as supported, drop support for earlier versions. | 46 | * Mark Mercurial 1.6, 1.7 and 1.8 as supported, drop support for earlier versions. |
76 | 42 | (#486899, Jelmer Vernooij) | 47 | (#486899, Jelmer Vernooij) |
77 | 43 | 48 | ||
78 | 44 | * Don't warn about development versions of Mercurial. (#492292, Jelmer | 49 | * Don't warn about development versions of Mercurial. (#492292, Jelmer |
79 | @@ -59,6 +64,22 @@ | |||
80 | 59 | * Don't allow probing for Mercurial repositories over HTTP to break | 64 | * Don't allow probing for Mercurial repositories over HTTP to break |
81 | 60 | bzr. (Jelmer Vernooij) | 65 | bzr. (Jelmer Vernooij) |
82 | 61 | 66 | ||
83 | 67 | * Fix recursive removing of parent empty directories. (#691994, | ||
84 | 68 | Leonid Borisenko) | ||
85 | 69 | |||
86 | 70 | * WorkingTree.commit()'s message argument is now optional, consistent | ||
87 | 71 | with bzr.dev. (#692902, Jelmer Vernooij) | ||
88 | 72 | |||
89 | 73 | * Fix handling of unknown strings in converted_from extra. | ||
90 | 74 | (#670870, Jelmer Vernooij) | ||
91 | 75 | |||
92 | 76 | * Fix handling of first revision without changes. (Jelmer Vernooij, #688459) | ||
93 | 77 | |||
94 | 78 | * Support stop_revision in Branch.pull(). (Jelmer Vernooij, #544701) | ||
95 | 79 | |||
96 | 80 | * Provide custom ControlDir.sprout() implementation. Required for | ||
97 | 81 | compatibility with bzr 2.4. (Jelmer Vernooij, #717937) | ||
98 | 82 | |||
99 | 62 | DOCUMENTATION | 83 | DOCUMENTATION |
100 | 63 | 84 | ||
101 | 64 | * Add some basic instructions in INSTALL. (Martin Pool) | 85 | * Add some basic instructions in INSTALL. (Martin Pool) |
102 | 65 | 86 | ||
103 | === modified file '__init__.py' | |||
104 | --- __init__.py 2010-08-06 10:51:08 +0000 | |||
105 | +++ __init__.py 2011-03-23 21:41:04 +0000 | |||
106 | @@ -25,13 +25,12 @@ | |||
107 | 25 | 25 | ||
108 | 26 | from info import ( | 26 | from info import ( |
109 | 27 | bzr_compatible_versions, | 27 | bzr_compatible_versions, |
111 | 28 | hg_compatible_versions, | 28 | hg_compatible_version_strings, |
112 | 29 | bzr_plugin_version as version_info, | 29 | bzr_plugin_version as version_info, |
113 | 30 | ) | 30 | ) |
114 | 31 | 31 | ||
115 | 32 | bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions) | 32 | bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions) |
116 | 33 | 33 | ||
117 | 34 | import bzrlib.bzrdir | ||
118 | 35 | from bzrlib import ( | 34 | from bzrlib import ( |
119 | 36 | errors, | 35 | errors, |
120 | 37 | trace, | 36 | trace, |
121 | @@ -39,13 +38,17 @@ | |||
122 | 39 | from bzrlib.foreign import ( | 38 | from bzrlib.foreign import ( |
123 | 40 | foreign_vcs_registry, | 39 | foreign_vcs_registry, |
124 | 41 | ) | 40 | ) |
125 | 42 | import bzrlib.lockable_files | ||
126 | 43 | from bzrlib.send import ( | 41 | from bzrlib.send import ( |
127 | 44 | format_registry as send_format_registry, | 42 | format_registry as send_format_registry, |
128 | 45 | ) | 43 | ) |
129 | 46 | 44 | ||
130 | 45 | from bzrlib.controldir import ( | ||
131 | 46 | network_format_registry as controldir_network_format_registry, | ||
132 | 47 | ControlDirFormat, | ||
133 | 48 | Prober, | ||
134 | 49 | ) | ||
135 | 50 | |||
136 | 47 | _mercurial_loaded = False | 51 | _mercurial_loaded = False |
137 | 48 | LockWarner = bzrlib.lockable_files._LockWarner | ||
138 | 49 | 52 | ||
139 | 50 | def lazy_load_mercurial(): | 53 | def lazy_load_mercurial(): |
140 | 51 | global _mercurial_loaded | 54 | global _mercurial_loaded |
141 | @@ -61,7 +64,7 @@ | |||
142 | 61 | from mercurial.__version__ import version as hg_version | 64 | from mercurial.__version__ import version as hg_version |
143 | 62 | if hg_version != "unknown": | 65 | if hg_version != "unknown": |
144 | 63 | hg_major_version = ".".join(hg_version.split(".")[:2]) | 66 | hg_major_version = ".".join(hg_version.split(".")[:2]) |
146 | 64 | if hg_major_version not in hg_compatible_versions and not "+" in hg_version: | 67 | if hg_major_version not in hg_compatible_version_strings and not "+" in hg_version: |
147 | 65 | raise errors.DependencyNotPresent("mercurial", | 68 | raise errors.DependencyNotPresent("mercurial", |
148 | 66 | 'bzr-hg: Mercurial version %s not supported.' % hg_version) | 69 | 'bzr-hg: Mercurial version %s not supported.' % hg_version) |
149 | 67 | trace.mutter("bzr-hg: using Mercurial %s" % hg_version) | 70 | trace.mutter("bzr-hg: using Mercurial %s" % hg_version) |
150 | @@ -70,266 +73,79 @@ | |||
151 | 70 | foreign_vcs_registry.register_lazy("hg", | 73 | foreign_vcs_registry.register_lazy("hg", |
152 | 71 | "bzrlib.plugins.hg.mapping", "foreign_hg", "Mercurial") | 74 | "bzrlib.plugins.hg.mapping", "foreign_hg", "Mercurial") |
153 | 72 | 75 | ||
410 | 73 | class HgDummyLock(object): | 76 | def has_hg_http_smart_server(transport, external_url): |
411 | 74 | """Lock that doesn't actually lock.""" | 77 | if not external_url.startswith("http:") and not external_url.startswith("https:"): |
412 | 75 | 78 | return False | |
413 | 76 | def __init__(self, hgrepo): | 79 | url = external_url + "?pairs=%s-%s&cmd=between" % ("0" * 40, "0" * 40) |
414 | 77 | self._hgrepo = hgrepo | 80 | from bzrlib.transport.http._urllib import HttpTransport_urllib, Request |
415 | 78 | 81 | if isinstance(transport, HttpTransport_urllib): | |
416 | 79 | def lock_write(self, token=None): | 82 | req = Request('GET', url, accepted_errors=[200, 403, 404, 405]) |
417 | 80 | if token is not None: | 83 | req.follow_redirections = True |
418 | 81 | raise errors.TokenLockingNotSupported(self) | 84 | resp = transport._perform(req) |
419 | 82 | self._lock = self._hgrepo.wlock() | 85 | if resp.code == 404: |
420 | 83 | 86 | return False | |
421 | 84 | def lock_read(self): | 87 | headers = resp.headers |
422 | 85 | self._lock = None | 88 | else: |
423 | 86 | 89 | try: | |
424 | 87 | def peek(self): | 90 | from bzrlib.transport.http._pycurl import PyCurlTransport |
425 | 88 | raise NotImplementedError(self.peek) | 91 | except errors.DependencyNotPresent: |
426 | 89 | 92 | return False | |
427 | 90 | def unlock(self): | 93 | else: |
428 | 91 | if self._lock is not None: | 94 | import pycurl |
429 | 92 | self._lock.release() | 95 | from cStringIO import StringIO |
430 | 93 | 96 | if isinstance(transport, PyCurlTransport): | |
431 | 94 | def validate_token(self, token): | 97 | conn = transport._get_curl() |
432 | 95 | if token is not None: | 98 | conn.setopt(pycurl.URL, url) |
433 | 96 | raise errors.TokenLockingNotSupported(self) | 99 | transport._set_curl_options(conn) |
434 | 97 | 100 | conn.setopt(pycurl.HTTPGET, 1) | |
435 | 98 | 101 | conn.setopt(pycurl.NOBODY, 1) | |
436 | 99 | class HgLock(object): | 102 | header = StringIO() |
437 | 100 | """A lock that thunks through to Hg.""" | 103 | data = StringIO() |
438 | 101 | 104 | conn.setopt(pycurl.HEADERFUNCTION, header.write) | |
439 | 102 | def __init__(self, hgrepo): | 105 | conn.setopt(pycurl.WRITEFUNCTION, data.write) |
440 | 103 | self._hgrepo = hgrepo | 106 | transport._curl_perform(conn, header) |
441 | 104 | 107 | code = conn.getinfo(pycurl.HTTP_CODE) | |
442 | 105 | def lock_write(self, token=None): | 108 | if code == 404: |
443 | 106 | if token is not None: | 109 | raise errors.NoSuchFile(transport._path) |
444 | 107 | raise errors.TokenLockingNotSupported(self) | 110 | headers = transport._parse_headers(header) |
445 | 108 | self._lock = self._hgrepo.wlock() | 111 | else: |
446 | 109 | 112 | return False | |
447 | 110 | def lock_read(self): | 113 | ct = headers.getheader("Content-Type") |
448 | 111 | self._lock = self._hgrepo.lock() | 114 | return ct.startswith("application/mercurial") |
449 | 112 | 115 | ||
450 | 113 | def peek(self): | 116 | |
451 | 114 | raise NotImplementedError(self.peek) | 117 | def has_hg_dumb_repository(transport): |
452 | 115 | 118 | try: | |
453 | 116 | def unlock(self): | 119 | return transport.has(".hg/requires") |
454 | 117 | self._lock.release() | 120 | except (errors.NoSuchFile, errors.PermissionDenied): |
455 | 118 | 121 | return False | |
456 | 119 | def validate_token(self, token): | 122 | |
457 | 120 | if token is not None: | 123 | |
458 | 121 | raise errors.TokenLockingNotSupported(self) | 124 | class HgProber(Prober): |
459 | 122 | 125 | ||
460 | 123 | def break_lock(self): | 126 | # Perhaps retrieve list from mercurial.hg.schemes ? |
461 | 124 | pass | 127 | _supported_schemes = ["http", "https", "file", "ssh"] |
462 | 125 | 128 | ||
463 | 126 | 129 | def probe_transport(self, transport): | |
464 | 127 | class HgLockableFiles(bzrlib.lockable_files.LockableFiles): | 130 | try: |
465 | 128 | """Hg specific lockable files abstraction.""" | 131 | external_url = transport.external_url() |
466 | 129 | 132 | except errors.InProcessTransport: | |
467 | 130 | def __init__(self, lock, transport): | 133 | raise errors.NotBranchError(path=transport.base) |
468 | 131 | self._lock = lock | 134 | scheme = external_url.split(":")[0] |
469 | 132 | self._transaction = None | 135 | if scheme not in self._supported_schemes: |
470 | 133 | self._lock_mode = None | 136 | raise errors.NotBranchError(path=transport.base) |
471 | 134 | self._transport = transport | 137 | if (not has_hg_dumb_repository(transport) and |
472 | 135 | self._lock_warner = LockWarner(repr(self)) | 138 | not has_hg_http_smart_server(transport, external_url)): |
217 | 136 | |||
218 | 137 | |||
219 | 138 | class HgDir(bzrlib.bzrdir.BzrDir): | ||
220 | 139 | """An adapter to the '.hg' dir used by mercurial.""" | ||
221 | 140 | |||
222 | 141 | def __init__(self, hgrepo, transport, lockfiles, format): | ||
223 | 142 | self._format = format | ||
224 | 143 | self.root_transport = transport | ||
225 | 144 | self.transport = transport.clone('.hg') | ||
226 | 145 | self._hgrepo = hgrepo | ||
227 | 146 | self._lockfiles = lockfiles | ||
228 | 147 | |||
229 | 148 | def backup_bzrdir(self): | ||
230 | 149 | self.root_transport.copy_tree(".hg", ".hg.backup") | ||
231 | 150 | return (self.root_transport.abspath(".hg"), | ||
232 | 151 | self.root_transport.abspath(".hg.backup")) | ||
233 | 152 | |||
234 | 153 | def break_lock(self): | ||
235 | 154 | """Mercurial locks never break.""" | ||
236 | 155 | raise NotImplementedError(self.break_lock) | ||
237 | 156 | |||
238 | 157 | def clone(self, url, revision_id=None, basis=None, force_new_repo=False): | ||
239 | 158 | """Clone this hg dir to url.""" | ||
240 | 159 | self._make_tail(url) | ||
241 | 160 | if url.startswith('file://'): | ||
242 | 161 | url = url[len('file://'):] | ||
243 | 162 | url = url.encode('utf8') | ||
244 | 163 | result = self._format.initialize(url) | ||
245 | 164 | result._hgrepo.pull(self._hgrepo) | ||
246 | 165 | return result | ||
247 | 166 | |||
248 | 167 | def create_branch(self, name=None): | ||
249 | 168 | """'create' a branch for this dir.""" | ||
250 | 169 | return self.open_branch(name=name) | ||
251 | 170 | |||
252 | 171 | def create_repository(self, shared=False): | ||
253 | 172 | """'create' a repository for this dir.""" | ||
254 | 173 | if shared: | ||
255 | 174 | # dont know enough about hg yet to do 'shared repositories' in it. | ||
256 | 175 | raise errors.IncompatibleFormat(self._format, self._format) | ||
257 | 176 | return self.open_repository() | ||
258 | 177 | |||
259 | 178 | def create_workingtree(self, revision_id=None, from_branch=None, | ||
260 | 179 | accelerator_tree=None, hardlink=False): | ||
261 | 180 | """'create' a workingtree for this dir.""" | ||
262 | 181 | if revision_id is not None: | ||
263 | 182 | raise NotImplementedError("revision_id argument not yet supported") | ||
264 | 183 | if from_branch is not None: | ||
265 | 184 | raise NotImplementedError("from_branch argument not yet supported") | ||
266 | 185 | return self.open_workingtree() | ||
267 | 186 | |||
268 | 187 | def destroy_branch(self, name=None): | ||
269 | 188 | if name is not None: | ||
270 | 189 | raise errors.NoColocatedBranchSupport(self) | ||
271 | 190 | raise errors.UnsupportedOperation(self.destroy_branch, self) | ||
272 | 191 | |||
273 | 192 | def destroy_workingtree(self): | ||
274 | 193 | raise errors.UnsupportedOperation(self.destroy_workingtree, self) | ||
275 | 194 | |||
276 | 195 | def destroy_repository(self): | ||
277 | 196 | raise errors.UnsupportedOperation(self.destroy_repository, self) | ||
278 | 197 | |||
279 | 198 | def get_branch_transport(self, branch_format, name=None): | ||
280 | 199 | if name is not None: | ||
281 | 200 | raise errors.NoColocatedBranchSupport(self) | ||
282 | 201 | if branch_format is None: | ||
283 | 202 | return self.transport | ||
284 | 203 | if isinstance(branch_format, HgBzrDirFormat): | ||
285 | 204 | return self.transport | ||
286 | 205 | raise errors.IncompatibleFormat(branch_format, self._format) | ||
287 | 206 | |||
288 | 207 | get_repository_transport = get_branch_transport | ||
289 | 208 | get_workingtree_transport = get_branch_transport | ||
290 | 209 | |||
291 | 210 | def is_supported(self): | ||
292 | 211 | return True | ||
293 | 212 | |||
294 | 213 | def needs_format_conversion(self, format=None): | ||
295 | 214 | return (format is not HgBzrDirFormat) | ||
296 | 215 | |||
297 | 216 | def open_branch(self, name=None, unsupported=False, | ||
298 | 217 | ignore_fallbacks=False): | ||
299 | 218 | """'create' a branch for this dir.""" | ||
300 | 219 | if name is not None: | ||
301 | 220 | raise errors.NoColocatedBranchSupport(self) | ||
302 | 221 | from bzrlib.plugins.hg.branch import HgLocalBranch, HgRemoteBranch | ||
303 | 222 | if self._hgrepo.local(): | ||
304 | 223 | branch_klass = HgLocalBranch | ||
305 | 224 | else: | ||
306 | 225 | branch_klass = HgRemoteBranch | ||
307 | 226 | return branch_klass(self._hgrepo, self, self._lockfiles) | ||
308 | 227 | |||
309 | 228 | def open_repository(self, shared=False): | ||
310 | 229 | """'open' a repository for this dir.""" | ||
311 | 230 | from bzrlib.plugins.hg.repository import ( | ||
312 | 231 | HgLocalRepository, | ||
313 | 232 | HgRemoteRepository, | ||
314 | 233 | ) | ||
315 | 234 | if self._hgrepo.local(): | ||
316 | 235 | repo_klass = HgLocalRepository | ||
317 | 236 | else: | ||
318 | 237 | repo_klass = HgRemoteRepository | ||
319 | 238 | return repo_klass(self._hgrepo, self, self._lockfiles) | ||
320 | 239 | |||
321 | 240 | def open_workingtree(self, shared=False, recommend_upgrade=False): | ||
322 | 241 | """'open' a workingtree for this dir.""" | ||
323 | 242 | from bzrlib.plugins.hg.workingtree import HgWorkingTree | ||
324 | 243 | return HgWorkingTree(self._hgrepo, self.open_branch(), self, | ||
325 | 244 | self._lockfiles) | ||
326 | 245 | |||
327 | 246 | def cloning_metadir(self, stacked=False): | ||
328 | 247 | return bzrlib.bzrdir.format_registry.make_bzrdir("default-rich-root") | ||
329 | 248 | |||
330 | 249 | |||
331 | 250 | class HgToSomethingConverter(bzrlib.bzrdir.Converter): | ||
332 | 251 | """A class to upgrade an hg dir to something else.""" | ||
333 | 252 | |||
334 | 253 | def __init__(self, format): | ||
335 | 254 | self.format = format | ||
336 | 255 | if self.format is None: | ||
337 | 256 | self.format = bzrlib.bzrdir.BzrDirFormat.get_default_format() | ||
338 | 257 | |||
339 | 258 | def convert(self, bzrdir, pb): | ||
340 | 259 | source_repo = bzrdir.open_repository() | ||
341 | 260 | source_branch = bzrdir.open_branch() | ||
342 | 261 | target = self.format.initialize_on_transport(bzrdir.root_transport) | ||
343 | 262 | target_repo = target.create_repository() | ||
344 | 263 | target_repo.fetch(source_repo, pb=pb) | ||
345 | 264 | target_branch = target.create_branch() | ||
346 | 265 | target_branch.generate_revision_history(source_branch.last_revision()) | ||
347 | 266 | target_wt = target.create_workingtree() | ||
348 | 267 | bzrdir.root_transport.delete_tree(".hg") | ||
349 | 268 | return target | ||
350 | 269 | |||
351 | 270 | |||
352 | 271 | class HgBzrDirFormat(bzrlib.bzrdir.BzrDirFormat): | ||
353 | 272 | """The .hg directory control format.""" | ||
354 | 273 | |||
355 | 274 | def __init__(self): | ||
356 | 275 | super(HgBzrDirFormat, self).__init__() | ||
357 | 276 | self.workingtree_format = None | ||
358 | 277 | |||
359 | 278 | def get_converter(self, format): | ||
360 | 279 | """We should write a converter.""" | ||
361 | 280 | return HgToSomethingConverter(format) | ||
362 | 281 | |||
363 | 282 | def network_name(self): | ||
364 | 283 | return "hg" | ||
365 | 284 | |||
366 | 285 | def get_format_description(self): | ||
367 | 286 | return "Mercurial Branch" | ||
368 | 287 | |||
369 | 288 | def initialize_on_transport(self, transport): | ||
370 | 289 | """Initialize a new .not dir in the base directory of a Transport.""" | ||
371 | 290 | return self.open(transport, _create=True) | ||
372 | 291 | |||
373 | 292 | def __eq__(self, other): | ||
374 | 293 | return type(self) == type(other) | ||
375 | 294 | |||
376 | 295 | @classmethod | ||
377 | 296 | def _known_formats(self): | ||
378 | 297 | return set([HgBzrDirFormat()]) | ||
379 | 298 | |||
380 | 299 | def open(self, transport, _create=False, _found=None): | ||
381 | 300 | """Open this directory. | ||
382 | 301 | |||
383 | 302 | :param _create: create the hg dir on the fly. private to HgBzrDirFormat. | ||
384 | 303 | """ | ||
385 | 304 | # we dont grok readonly - hg isn't integrated with transport. | ||
386 | 305 | if transport.base.startswith('readonly+'): | ||
387 | 306 | transport = transport._decorated | ||
388 | 307 | if transport.base.startswith('file://'): | ||
389 | 308 | path = transport.local_abspath('.').encode('utf-8') | ||
390 | 309 | lock_class = HgLock | ||
391 | 310 | else: | ||
392 | 311 | path = transport.base | ||
393 | 312 | lock_class = HgDummyLock | ||
394 | 313 | lazy_load_mercurial() | ||
395 | 314 | import mercurial.hg | ||
396 | 315 | from bzrlib.plugins.hg.ui import ui | ||
397 | 316 | repository = mercurial.hg.repository(ui(), path, create=_create) | ||
398 | 317 | lockfiles = HgLockableFiles(lock_class(repository), transport) | ||
399 | 318 | return HgDir(repository, transport, lockfiles, self) | ||
400 | 319 | |||
401 | 320 | @classmethod | ||
402 | 321 | def probe_transport(klass, transport): | ||
403 | 322 | """Our format is present if the transport ends in '.not/'.""" | ||
404 | 323 | # little ugly, but works | ||
405 | 324 | format = klass() | ||
406 | 325 | from bzrlib.transport.local import LocalTransport | ||
407 | 326 | lazy_load_mercurial() | ||
408 | 327 | from mercurial import error as hg_errors | ||
409 | 328 | if isinstance(transport, LocalTransport) and not transport.has(".hg"): | ||
473 | 329 | # Explicitly check for .hg directories here, so we avoid | 139 | # Explicitly check for .hg directories here, so we avoid |
474 | 330 | # loading foreign branches through Mercurial. | 140 | # loading foreign branches through Mercurial. |
475 | 331 | raise errors.NotBranchError(path=transport.base) | 141 | raise errors.NotBranchError(path=transport.base) |
476 | 142 | |||
477 | 143 | lazy_load_mercurial() | ||
478 | 144 | from mercurial import error as hg_errors | ||
479 | 145 | |||
480 | 332 | import urllib2 | 146 | import urllib2 |
481 | 147 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
482 | 148 | format = HgControlDirFormat() | ||
483 | 333 | try: | 149 | try: |
484 | 334 | format.open(transport) | 150 | format.open(transport) |
485 | 335 | except hg_errors.RepoError, e: | 151 | except hg_errors.RepoError, e: |
486 | @@ -342,11 +158,68 @@ | |||
487 | 342 | raise errors.NotBranchError(path=transport.base) | 158 | raise errors.NotBranchError(path=transport.base) |
488 | 343 | return format | 159 | return format |
489 | 344 | 160 | ||
495 | 345 | 161 | @classmethod | |
496 | 346 | bzrlib.bzrdir.BzrDirFormat.register_control_format(HgBzrDirFormat) | 162 | def known_formats(cls): |
497 | 347 | 163 | from bzrlib.plugins.hg.dir import HgControlDirFormat | |
498 | 348 | bzrlib.bzrdir.format_registry.register("hg", | 164 | return set([HgControlDirFormat()]) |
499 | 349 | HgBzrDirFormat, "Mercurial repository. ", native=False, hidden=False) | 165 | |
500 | 166 | |||
501 | 167 | ControlDirFormat.register_prober(HgProber) | ||
502 | 168 | ControlDirFormat._server_probers.insert(0, HgProber) | ||
503 | 169 | if not getattr(Prober, "known_formats", False): # bzr < 2.4 | ||
504 | 170 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
505 | 171 | ControlDirFormat.register_format(HgControlDirFormat()) | ||
506 | 172 | |||
507 | 173 | controldir_network_format_registry.register_lazy("hg", | ||
508 | 174 | "bzrlib.plugins.hg.dir", "HgControlDirFormat") | ||
509 | 175 | |||
510 | 176 | bzrlib.bzrdir.format_registry.register_lazy("hg", | ||
511 | 177 | "bzrlib.plugins.hg.dir", "HgControlDirFormat", | ||
512 | 178 | "Mercurial repository. ", native=False, hidden=False) | ||
513 | 179 | |||
514 | 180 | from bzrlib.repository import ( | ||
515 | 181 | format_registry as repository_format_registry, | ||
516 | 182 | network_format_registry as repository_network_format_registry, | ||
517 | 183 | ) | ||
518 | 184 | repository_network_format_registry.register_lazy('hg', | ||
519 | 185 | 'bzrlib.plugins.hg.repository', 'HgRepositoryFormat') | ||
520 | 186 | |||
521 | 187 | |||
522 | 188 | from bzrlib.branch import ( | ||
523 | 189 | network_format_registry as branch_network_format_registry, | ||
524 | 190 | ) | ||
525 | 191 | branch_network_format_registry.register_lazy( | ||
526 | 192 | "hg", "bzrlib.plugins.hg.branch", "HgBranchFormat") | ||
527 | 193 | |||
528 | 194 | try: | ||
529 | 195 | from bzrlib.branch import ( | ||
530 | 196 | format_registry as branch_format_registry, | ||
531 | 197 | ) | ||
532 | 198 | except ImportError: # bzr < 2.4 | ||
533 | 199 | pass | ||
534 | 200 | else: | ||
535 | 201 | branch_format_registry.register_extra_lazy( | ||
536 | 202 | "bzrlib.plugins.hg.branch", "HgBranchFormat") | ||
537 | 203 | |||
538 | 204 | try: | ||
539 | 205 | from bzrlib.workingtree import ( | ||
540 | 206 | format_registry as workingtree_format_registry, | ||
541 | 207 | ) | ||
542 | 208 | except ImportError: # bzr < 2.4 | ||
543 | 209 | pass | ||
544 | 210 | else: | ||
545 | 211 | workingtree_format_registry.register_extra_lazy( | ||
546 | 212 | "bzrlib.plugins.hg.workingtree", "HgWorkingTreeFormat") | ||
547 | 213 | |||
548 | 214 | |||
549 | 215 | try: | ||
550 | 216 | register_extra_lazy_repository_format = getattr(repository_format_registry, | ||
551 | 217 | "register_extra_lazy") | ||
552 | 218 | except AttributeError: # bzr < 2.4 | ||
553 | 219 | pass | ||
554 | 220 | else: | ||
555 | 221 | register_extra_lazy_repository_format('bzrlib.plugins.hg.repository', | ||
556 | 222 | 'HgRepositoryFormat') | ||
557 | 350 | 223 | ||
558 | 351 | send_format_registry.register_lazy('hg', 'bzrlib.plugins.hg.send', | 224 | send_format_registry.register_lazy('hg', 'bzrlib.plugins.hg.send', |
559 | 352 | 'send_hg', 'Mecurial bundle format') | 225 | 'send_hg', 'Mecurial bundle format') |
560 | @@ -360,15 +233,14 @@ | |||
561 | 360 | ) | 233 | ) |
562 | 361 | plugin_cmds.register_lazy('cmd_hg_import', [], 'bzrlib.plugins.hg.commands') | 234 | plugin_cmds.register_lazy('cmd_hg_import', [], 'bzrlib.plugins.hg.commands') |
563 | 362 | 235 | ||
569 | 363 | try: | 236 | from bzrlib.revisionspec import dwim_revspecs, RevisionSpec_dwim |
570 | 364 | from bzrlib.revisionspec import dwim_revspecs | 237 | if getattr(RevisionSpec_dwim, "append_possible_lazy_revspec", None): |
571 | 365 | except ImportError: | 238 | RevisionSpec_dwim.append_possible_lazy_revspec( |
572 | 366 | pass | 239 | "bzrlib.plugins.hg.revspec", "RevisionSpec_hg") |
573 | 367 | else: | 240 | else: # bzr < 2.4 |
574 | 368 | from bzrlib.plugins.hg.revspec import RevisionSpec_hg | 241 | from bzrlib.plugins.hg.revspec import RevisionSpec_hg |
575 | 369 | dwim_revspecs.append(RevisionSpec_hg) | 242 | dwim_revspecs.append(RevisionSpec_hg) |
576 | 370 | 243 | ||
577 | 371 | |||
578 | 372 | def test_suite(): | 244 | def test_suite(): |
579 | 373 | from unittest import TestSuite, TestLoader | 245 | from unittest import TestSuite, TestLoader |
580 | 374 | from bzrlib.plugins.hg import tests | 246 | from bzrlib.plugins.hg import tests |
581 | 375 | 247 | ||
582 | === modified file 'branch.py' | |||
583 | --- branch.py 2010-08-14 00:38:48 +0000 | |||
584 | +++ branch.py 2011-03-23 21:41:04 +0000 | |||
585 | @@ -1,5 +1,5 @@ | |||
588 | 1 | # Copyright (C) 2005, 2006 Canonical Ltd | 1 | # Copyright (C) 2005, 2006, 2011 Canonical Ltd |
589 | 2 | # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org> | 2 | # Copyright (C) 2008-2011 Jelmer Vernooij <jelmer@samba.org> |
590 | 3 | # | 3 | # |
591 | 4 | # This program is free software; you can redistribute it and/or modify | 4 | # This program is free software; you can redistribute it and/or modify |
592 | 5 | # it under the terms of the GNU General Public License as published by | 5 | # it under the terms of the GNU General Public License as published by |
593 | @@ -39,6 +39,10 @@ | |||
594 | 39 | from bzrlib.repository import ( | 39 | from bzrlib.repository import ( |
595 | 40 | InterRepository, | 40 | InterRepository, |
596 | 41 | ) | 41 | ) |
597 | 42 | from bzrlib.tag import ( | ||
598 | 43 | BasicTags, | ||
599 | 44 | DisabledTags, | ||
600 | 45 | ) | ||
601 | 42 | 46 | ||
602 | 43 | from bzrlib.plugins.hg.changegroup import ( | 47 | from bzrlib.plugins.hg.changegroup import ( |
603 | 44 | dchangegroup, | 48 | dchangegroup, |
604 | @@ -48,6 +52,49 @@ | |||
605 | 48 | _fmt = "Push is not yet supported for bzr-hg. Try dpush instead." | 52 | _fmt = "Push is not yet supported for bzr-hg. Try dpush instead." |
606 | 49 | 53 | ||
607 | 50 | 54 | ||
608 | 55 | class HgTags(BasicTags): | ||
609 | 56 | |||
610 | 57 | def __init__(self, branch): | ||
611 | 58 | self.branch = branch | ||
612 | 59 | |||
613 | 60 | def _get_hg_tags(self): | ||
614 | 61 | raise NotImplementedError(self._get_hg_tags) | ||
615 | 62 | |||
616 | 63 | def get_tag_dict(self): | ||
617 | 64 | ret = {} | ||
618 | 65 | hgtags = self._get_hg_tags() | ||
619 | 66 | for name, value in hgtags.iteritems(): | ||
620 | 67 | ret[name] = self.branch.repository.lookup_foreign_revision_id(value) | ||
621 | 68 | return ret | ||
622 | 69 | |||
623 | 70 | def set_tag(self, name, value): | ||
624 | 71 | self.branch.repository._hgrepo.tag([name], | ||
625 | 72 | self.branch.repository.lookup_bzr_revision_id(value)[0], | ||
626 | 73 | "Create tag %s" % name, | ||
627 | 74 | True, | ||
628 | 75 | self.branch.get_config().username(), None) | ||
629 | 76 | |||
630 | 77 | |||
631 | 78 | class LocalHgTags(HgTags): | ||
632 | 79 | |||
633 | 80 | def _get_hg_tags(self): | ||
634 | 81 | return self.branch.repository._hgrepo.tags() | ||
635 | 82 | |||
636 | 83 | |||
637 | 84 | class FileHgTags(HgTags): | ||
638 | 85 | |||
639 | 86 | def __init__(self, branch, revid): | ||
640 | 87 | self.branch = branch | ||
641 | 88 | self.revid = revid | ||
642 | 89 | |||
643 | 90 | def _get_hg_tags(self): | ||
644 | 91 | revtree = self.branch.repository.revision_tree(self.revid) | ||
645 | 92 | f = revtree.get_file_text(revtree.path2id(".hgtags"), ".hgtags") | ||
646 | 93 | for l in f.readlines(): | ||
647 | 94 | (name, hgtag) = l.strip().split(" ") | ||
648 | 95 | yield name, hgtag | ||
649 | 96 | |||
650 | 97 | |||
651 | 51 | class HgBranchFormat(BranchFormat): | 98 | class HgBranchFormat(BranchFormat): |
652 | 52 | """Mercurial Branch Format. | 99 | """Mercurial Branch Format. |
653 | 53 | 100 | ||
654 | @@ -56,6 +103,11 @@ | |||
655 | 56 | support the branch format. | 103 | support the branch format. |
656 | 57 | """ | 104 | """ |
657 | 58 | 105 | ||
658 | 106 | @property | ||
659 | 107 | def _matchingbzrdir(self): | ||
660 | 108 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
661 | 109 | return HgControlDirFormat() | ||
662 | 110 | |||
663 | 59 | def get_format_description(self): | 111 | def get_format_description(self): |
664 | 60 | """See BranchFormat.get_format_description().""" | 112 | """See BranchFormat.get_format_description().""" |
665 | 61 | return "Mercurial Branch" | 113 | return "Mercurial Branch" |
666 | @@ -67,6 +119,25 @@ | |||
667 | 67 | from bzrlib.plugins.hg.tests.test_branch import ForeignTestsBranchFactory | 119 | from bzrlib.plugins.hg.tests.test_branch import ForeignTestsBranchFactory |
668 | 68 | return ForeignTestsBranchFactory() | 120 | return ForeignTestsBranchFactory() |
669 | 69 | 121 | ||
670 | 122 | def initialize(self, a_bzrdir, name=None, repository=None): | ||
671 | 123 | from bzrlib.plugins.hg.dir import HgDir | ||
672 | 124 | if name is None: | ||
673 | 125 | name = 'default' | ||
674 | 126 | if not isinstance(a_bzrdir, HgDir): | ||
675 | 127 | raise errors.IncompatibleFormat(self, a_bzrdir._format) | ||
676 | 128 | bm = a_bzrdir._hgrepo.branchmap() | ||
677 | 129 | if name in bm: | ||
678 | 130 | raise errors.AlreadyBranchError(a_bzrdir.user_url) | ||
679 | 131 | return a_bzrdir.open_branch(name=name) | ||
680 | 132 | |||
681 | 133 | def make_tags(self, branch): | ||
682 | 134 | """See bzrlib.branch.BranchFormat.make_tags().""" | ||
683 | 135 | if getattr(branch.repository._hgrepo, "tags", None) is not None: | ||
684 | 136 | return LocalHgTags(branch) | ||
685 | 137 | else: | ||
686 | 138 | return DisabledTags(branch) | ||
687 | 139 | |||
688 | 140 | |||
689 | 70 | 141 | ||
690 | 71 | class HgBranchConfig(object): | 142 | class HgBranchConfig(object): |
691 | 72 | """Access Branch Configuration data for an HgBranch. | 143 | """Access Branch Configuration data for an HgBranch. |
692 | @@ -78,6 +149,9 @@ | |||
693 | 78 | self._branch = branch | 149 | self._branch = branch |
694 | 79 | self._ui = branch.repository._hgrepo.ui | 150 | self._ui = branch.repository._hgrepo.ui |
695 | 80 | 151 | ||
696 | 152 | def username(self): | ||
697 | 153 | return self._ui.config("username", "default") | ||
698 | 154 | |||
699 | 81 | def get_nickname(self): | 155 | def get_nickname(self): |
700 | 82 | # remove the trailing / and take the basename. | 156 | # remove the trailing / and take the basename. |
701 | 83 | return os.path.basename(self._branch.base[:-1]) | 157 | return os.path.basename(self._branch.base[:-1]) |
702 | @@ -92,13 +166,13 @@ | |||
703 | 92 | return False | 166 | return False |
704 | 93 | 167 | ||
705 | 94 | def get_user_option(self, name): | 168 | def get_user_option(self, name): |
707 | 95 | return None | 169 | return self._ui.config(name, "bazaar") |
708 | 96 | 170 | ||
709 | 97 | def get_user_option_as_bool(self, name): | 171 | def get_user_option_as_bool(self, name): |
710 | 98 | return False | 172 | return False |
711 | 99 | 173 | ||
712 | 100 | def set_user_option(self, name, value, warn_masked=False): | 174 | def set_user_option(self, name, value, warn_masked=False): |
714 | 101 | pass # FIXME: Uhm? | 175 | self._ui.setconfig(name, "bazaar", value) |
715 | 102 | 176 | ||
716 | 103 | def log_format(self): | 177 | def log_format(self): |
717 | 104 | """What log format should be used""" | 178 | """What log format should be used""" |
718 | @@ -120,14 +194,22 @@ | |||
719 | 120 | class HgBranch(ForeignBranch): | 194 | class HgBranch(ForeignBranch): |
720 | 121 | """An adapter to mercurial repositories for bzr Branch objects.""" | 195 | """An adapter to mercurial repositories for bzr Branch objects.""" |
721 | 122 | 196 | ||
724 | 123 | def __init__(self, hgrepo, hgdir, lockfiles): | 197 | @property |
725 | 124 | self._format = HgBranchFormat() | 198 | def control_url(self): |
726 | 199 | return self.bzrdir.control_url | ||
727 | 200 | |||
728 | 201 | @property | ||
729 | 202 | def control_transport(self): | ||
730 | 203 | return self.bzrdir.control_transport | ||
731 | 204 | |||
732 | 205 | def __init__(self, hgrepo, name, hgdir, lockfiles): | ||
733 | 125 | self.repository = hgdir.open_repository() | 206 | self.repository = hgdir.open_repository() |
734 | 126 | ForeignBranch.__init__(self, self.repository.get_mapping()) | 207 | ForeignBranch.__init__(self, self.repository.get_mapping()) |
735 | 127 | self._hgrepo = hgrepo | 208 | self._hgrepo = hgrepo |
736 | 128 | self.bzrdir = hgdir | 209 | self.bzrdir = hgdir |
737 | 129 | self.control_files = lockfiles | 210 | self.control_files = lockfiles |
738 | 130 | self.base = hgdir.root_transport.base | 211 | self.base = hgdir.root_transport.base |
739 | 212 | self.name = name | ||
740 | 131 | 213 | ||
741 | 132 | def _check(self): | 214 | def _check(self): |
742 | 133 | # TODO: Call out to mercurial for consistency checking? | 215 | # TODO: Call out to mercurial for consistency checking? |
743 | @@ -195,21 +277,42 @@ | |||
744 | 195 | revision_id = source_revision_id | 277 | revision_id = source_revision_id |
745 | 196 | destination.generate_revision_history(revision_id) | 278 | destination.generate_revision_history(revision_id) |
746 | 197 | 279 | ||
747 | 280 | def _tip(self): | ||
748 | 281 | try: | ||
749 | 282 | return self._hgrepo.branchmap()[self.name][0] | ||
750 | 283 | except KeyError: | ||
751 | 284 | import mercurial.node | ||
752 | 285 | return mercurial.node.nullid | ||
753 | 286 | |||
754 | 198 | 287 | ||
755 | 199 | class HgLocalBranch(HgBranch): | 288 | class HgLocalBranch(HgBranch): |
756 | 200 | 289 | ||
757 | 290 | def __init__(self, hgrepo, name, hgdir, lockfiles): | ||
758 | 291 | self._format = HgBranchFormat() | ||
759 | 292 | super(HgLocalBranch, self).__init__(hgrepo, name, hgdir, lockfiles) | ||
760 | 293 | |||
761 | 294 | def supports_tags(self): | ||
762 | 295 | return True | ||
763 | 296 | |||
764 | 201 | @needs_read_lock | 297 | @needs_read_lock |
765 | 202 | def last_revision(self): | 298 | def last_revision(self): |
767 | 203 | tip = self._hgrepo.lookup("tip") | 299 | tip = self._tip() |
768 | 204 | return self.repository.lookup_foreign_revision_id(tip, | 300 | return self.repository.lookup_foreign_revision_id(tip, |
769 | 205 | mapping=self.mapping) | 301 | mapping=self.mapping) |
770 | 206 | 302 | ||
771 | 207 | 303 | ||
772 | 208 | class HgRemoteBranch(HgBranch): | 304 | class HgRemoteBranch(HgBranch): |
773 | 209 | 305 | ||
774 | 306 | def __init__(self, hgrepo, name, hgdir, lockfiles): | ||
775 | 307 | self._format = HgBranchFormat() | ||
776 | 308 | super(HgRemoteBranch, self).__init__(hgrepo, name, hgdir, lockfiles) | ||
777 | 309 | |||
778 | 310 | def supports_tags(self): | ||
779 | 311 | return getattr(self.repository._hgrepo, "tags", None) is not None | ||
780 | 312 | |||
781 | 210 | @needs_read_lock | 313 | @needs_read_lock |
782 | 211 | def last_revision(self): | 314 | def last_revision(self): |
784 | 212 | tip = self._hgrepo.lookup("tip") | 315 | tip = self._tip() |
785 | 213 | return self.mapping.revision_id_foreign_to_bzr(tip) | 316 | return self.mapping.revision_id_foreign_to_bzr(tip) |
786 | 214 | 317 | ||
787 | 215 | 318 | ||
788 | @@ -234,7 +337,15 @@ | |||
789 | 234 | result.old_revno, result.old_revid = self.target.last_revision_info() | 337 | result.old_revno, result.old_revid = self.target.last_revision_info() |
790 | 235 | inter = InterRepository.get(self.source.repository, | 338 | inter = InterRepository.get(self.source.repository, |
791 | 236 | self.target.repository) | 339 | self.target.repository) |
792 | 340 | if stop_revision is None: | ||
793 | 341 | stop_revision = self.source.last_revision() | ||
794 | 237 | inter.fetch(revision_id=stop_revision) | 342 | inter.fetch(revision_id=stop_revision) |
795 | 343 | if overwrite: | ||
796 | 344 | req_base = None | ||
797 | 345 | else: | ||
798 | 346 | req_base = self.target.last_revision() | ||
799 | 347 | self.target.generate_revision_history(stop_revision, | ||
800 | 348 | req_base, self.source) | ||
801 | 238 | result.new_revno, result.new_revid = self.target.last_revision_info() | 349 | result.new_revno, result.new_revid = self.target.last_revision_info() |
802 | 239 | return result | 350 | return result |
803 | 240 | 351 | ||
804 | @@ -284,6 +395,8 @@ | |||
805 | 284 | self.target.generate_revision_history(self.source.last_revision(), | 395 | self.target.generate_revision_history(self.source.last_revision(), |
806 | 285 | req_base, self.source) | 396 | req_base, self.source) |
807 | 286 | result.new_revno, result.new_revid = self.target.last_revision_info() | 397 | result.new_revno, result.new_revid = self.target.last_revision_info() |
808 | 398 | tags = FileHgTags(self.target, result.new_revid) | ||
809 | 399 | result.tag_conflicts = tags.merge_to(self.target.tags, overwrite) | ||
810 | 287 | return result | 400 | return result |
811 | 288 | 401 | ||
812 | 289 | def push(self, overwrite=False, stop_revision=None): | 402 | def push(self, overwrite=False, stop_revision=None): |
813 | @@ -294,11 +407,18 @@ | |||
814 | 294 | result.old_revid = self.target.last_revision() | 407 | result.old_revid = self.target.last_revision() |
815 | 295 | inter = InterRepository.get(self.source.repository, | 408 | inter = InterRepository.get(self.source.repository, |
816 | 296 | self.target.repository) | 409 | self.target.repository) |
817 | 410 | if stop_revision is not None: | ||
818 | 411 | stop_revision = self.source.last_revision() | ||
819 | 297 | inter.fetch(revision_id=stop_revision) | 412 | inter.fetch(revision_id=stop_revision) |
823 | 298 | self.target.generate_revision_history(self.source.last_revision(), | 413 | if overwrite: |
824 | 299 | self.target.last_revision(), | 414 | req_base = None |
825 | 300 | self.source) | 415 | else: |
826 | 416 | req_base = self.target.last_revision() | ||
827 | 417 | self.target.generate_revision_history(stop_revision, req_base, | ||
828 | 418 | self.source) | ||
829 | 301 | result.new_revid = self.target.last_revision() | 419 | result.new_revid = self.target.last_revision() |
830 | 420 | tags = FileHgTags(self.target, result.new_revid) | ||
831 | 421 | result.tag_conflicts = tags.merge_to(self.target.tags, overwrite) | ||
832 | 302 | return result | 422 | return result |
833 | 303 | 423 | ||
834 | 304 | 424 | ||
835 | 305 | 425 | ||
836 | === added file 'dir.py' | |||
837 | --- dir.py 1970-01-01 00:00:00 +0000 | |||
838 | +++ dir.py 2011-03-23 21:41:04 +0000 | |||
839 | @@ -0,0 +1,395 @@ | |||
840 | 1 | # Copyright (C) 2005-2011 Canonical Ltd | ||
841 | 2 | # Copyright (C) 2008-2010 Jelmer Vernooij <jelmer@samba.org> | ||
842 | 3 | # | ||
843 | 4 | # This program is free software; you can redistribute it and/or modify | ||
844 | 5 | # it under the terms of the GNU General Public License as published by | ||
845 | 6 | # the Free Software Foundation; either version 2 of the License, or | ||
846 | 7 | # (at your option) any later version. | ||
847 | 8 | # | ||
848 | 9 | # This program is distributed in the hope that it will be useful, | ||
849 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
850 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
851 | 12 | # GNU General Public License for more details. | ||
852 | 13 | # | ||
853 | 14 | # You should have received a copy of the GNU General Public License | ||
854 | 15 | # along with this program; if not, write to the Free Software | ||
855 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
856 | 17 | |||
857 | 18 | """Mercurial control directory support. | ||
858 | 19 | |||
859 | 20 | """ | ||
860 | 21 | |||
861 | 22 | import bzrlib.bzrdir | ||
862 | 23 | import bzrlib.lockable_files | ||
863 | 24 | from bzrlib import ( | ||
864 | 25 | errors, | ||
865 | 26 | urlutils, | ||
866 | 27 | ) | ||
867 | 28 | |||
868 | 29 | from bzrlib.controldir import ( | ||
869 | 30 | ControlDir, | ||
870 | 31 | ControlDirFormat, | ||
871 | 32 | ) | ||
872 | 33 | try: | ||
873 | 34 | from bzrlib.controldir import Converter | ||
874 | 35 | except ImportError: # bzr < 2.4 | ||
875 | 36 | from bzrlib.bzrdir import Converter | ||
876 | 37 | from bzrlib.plugins.hg import ( | ||
877 | 38 | lazy_load_mercurial, | ||
878 | 39 | ) | ||
879 | 40 | |||
880 | 41 | |||
881 | 42 | class HgControlDirConfig(object): | ||
882 | 43 | |||
883 | 44 | def get_default_stack_on(self): | ||
884 | 45 | return None | ||
885 | 46 | |||
886 | 47 | def set_default_stack_on(self, value): | ||
887 | 48 | raise errors.BzrError("Cannot set configuration") | ||
888 | 49 | |||
889 | 50 | |||
890 | 51 | class HgDir(ControlDir): | ||
891 | 52 | """An adapter to the '.hg' dir used by mercurial.""" | ||
892 | 53 | |||
893 | 54 | @property | ||
894 | 55 | def user_transport(self): | ||
895 | 56 | return self.root_transport | ||
896 | 57 | |||
897 | 58 | @property | ||
898 | 59 | def control_transport(self): | ||
899 | 60 | return self.transport | ||
900 | 61 | |||
901 | 62 | def is_control_filename(self, filename): | ||
902 | 63 | return (filename == ".hg" or filename.startswith(".hg/")) | ||
903 | 64 | |||
904 | 65 | def __init__(self, hgrepo, transport, lockfiles, format): | ||
905 | 66 | self._format = format | ||
906 | 67 | self.root_transport = transport | ||
907 | 68 | self.transport = transport.clone('.hg') | ||
908 | 69 | self._hgrepo = hgrepo | ||
909 | 70 | self._lockfiles = lockfiles | ||
910 | 71 | |||
911 | 72 | def backup_bzrdir(self): | ||
912 | 73 | self.root_transport.copy_tree(".hg", ".hg.backup") | ||
913 | 74 | return (self.root_transport.abspath(".hg"), | ||
914 | 75 | self.root_transport.abspath(".hg.backup")) | ||
915 | 76 | |||
916 | 77 | def break_lock(self): | ||
917 | 78 | """Mercurial locks never break.""" | ||
918 | 79 | raise NotImplementedError(self.break_lock) | ||
919 | 80 | |||
920 | 81 | def clone_on_transport(self, transport, revision_id=None, | ||
921 | 82 | force_new_repo=False, preserve_stacking=False, stacked_on=None, | ||
922 | 83 | create_prefix=False, use_existing_dir=True, no_tree=False): | ||
923 | 84 | """See ControlDir.clone_on_transport.""" | ||
924 | 85 | path = transport.local_abspath(".") | ||
925 | 86 | result = self._format.initialize(path) | ||
926 | 87 | result._hgrepo.pull(self._hgrepo) | ||
927 | 88 | return result | ||
928 | 89 | |||
929 | 90 | def create_branch(self, name=None, repository=None): | ||
930 | 91 | """'create' a branch for this dir.""" | ||
931 | 92 | return self.open_branch(name=name) | ||
932 | 93 | |||
933 | 94 | def create_repository(self, shared=False): | ||
934 | 95 | """'create' a repository for this dir.""" | ||
935 | 96 | if shared: | ||
936 | 97 | raise errors.IncompatibleFormat(self._format, self._format) | ||
937 | 98 | return self.open_repository() | ||
938 | 99 | |||
939 | 100 | def create_workingtree(self, revision_id=None, from_branch=None, | ||
940 | 101 | accelerator_tree=None, hardlink=False): | ||
941 | 102 | """'create' a workingtree for this dir.""" | ||
942 | 103 | if revision_id is not None: | ||
943 | 104 | raise NotImplementedError("revision_id argument not yet supported") | ||
944 | 105 | if from_branch is not None: | ||
945 | 106 | raise NotImplementedError("from_branch argument not yet supported") | ||
946 | 107 | return self.open_workingtree() | ||
947 | 108 | |||
948 | 109 | def destroy_branch(self, name=None): | ||
949 | 110 | raise errors.UnsupportedOperation(self.destroy_branch, self) | ||
950 | 111 | |||
951 | 112 | def destroy_workingtree(self): | ||
952 | 113 | raise errors.UnsupportedOperation(self.destroy_workingtree, self) | ||
953 | 114 | |||
954 | 115 | def destroy_repository(self): | ||
955 | 116 | raise errors.UnsupportedOperation(self.destroy_repository, self) | ||
956 | 117 | |||
957 | 118 | def get_branch_transport(self, branch_format, name=None): | ||
958 | 119 | if branch_format is None: | ||
959 | 120 | return self.transport | ||
960 | 121 | if isinstance(branch_format, HgControlDirFormat): | ||
961 | 122 | return self.transport | ||
962 | 123 | raise errors.IncompatibleFormat(branch_format, self._format) | ||
963 | 124 | |||
964 | 125 | get_repository_transport = get_branch_transport | ||
965 | 126 | get_workingtree_transport = get_branch_transport | ||
966 | 127 | |||
967 | 128 | def is_supported(self): | ||
968 | 129 | return True | ||
969 | 130 | |||
970 | 131 | def needs_format_conversion(self, format=None): | ||
971 | 132 | return (format is not HgControlDirFormat) | ||
972 | 133 | |||
973 | 134 | def open_branch(self, name=None, unsupported=False, | ||
974 | 135 | ignore_fallbacks=False): | ||
975 | 136 | """'create' a branch for this dir.""" | ||
976 | 137 | if name is None: | ||
977 | 138 | name = 'default' | ||
978 | 139 | from bzrlib.plugins.hg.branch import HgLocalBranch, HgRemoteBranch | ||
979 | 140 | if self._hgrepo.local(): | ||
980 | 141 | branch_klass = HgLocalBranch | ||
981 | 142 | else: | ||
982 | 143 | branch_klass = HgRemoteBranch | ||
983 | 144 | return branch_klass(self._hgrepo, name, self, self._lockfiles) | ||
984 | 145 | |||
985 | 146 | def find_repository(self, shared=False): | ||
986 | 147 | return self.open_repository(shared) | ||
987 | 148 | |||
988 | 149 | def open_repository(self, shared=False): | ||
989 | 150 | """'open' a repository for this dir.""" | ||
990 | 151 | from bzrlib.plugins.hg.repository import ( | ||
991 | 152 | HgLocalRepository, | ||
992 | 153 | HgRemoteRepository, | ||
993 | 154 | ) | ||
994 | 155 | if self._hgrepo.local(): | ||
995 | 156 | repo_klass = HgLocalRepository | ||
996 | 157 | else: | ||
997 | 158 | repo_klass = HgRemoteRepository | ||
998 | 159 | return repo_klass(self._hgrepo, self, self._lockfiles) | ||
999 | 160 | |||
1000 | 161 | def open_workingtree(self, shared=False, recommend_upgrade=False): | ||
1001 | 162 | """'open' a workingtree for this dir.""" | ||
1002 | 163 | from bzrlib.plugins.hg.workingtree import HgWorkingTree | ||
1003 | 164 | return HgWorkingTree(self._hgrepo, self.open_branch(), self, | ||
1004 | 165 | self._lockfiles) | ||
1005 | 166 | |||
1006 | 167 | def cloning_metadir(self, stacked=False): | ||
1007 | 168 | return bzrlib.bzrdir.format_registry.make_bzrdir("default-rich-root") | ||
1008 | 169 | |||
1009 | 170 | def get_config(self): | ||
1010 | 171 | return HgControlDirConfig() | ||
1011 | 172 | |||
1012 | 173 | def sprout(self, url, revision_id=None, force_new_repo=False, | ||
1013 | 174 | recurse='down', possible_transports=None, | ||
1014 | 175 | accelerator_tree=None, hardlink=False, stacked=False, | ||
1015 | 176 | source_branch=None, create_tree_if_local=True): | ||
1016 | 177 | from bzrlib.repository import InterRepository | ||
1017 | 178 | from bzrlib.transport.local import LocalTransport | ||
1018 | 179 | from bzrlib.transport import get_transport | ||
1019 | 180 | target_transport = get_transport(url, possible_transports) | ||
1020 | 181 | target_transport.ensure_base() | ||
1021 | 182 | cloning_format = self.cloning_metadir() | ||
1022 | 183 | # Create/update the result branch | ||
1023 | 184 | result = cloning_format.initialize_on_transport(target_transport) | ||
1024 | 185 | source_branch = self.open_branch() | ||
1025 | 186 | source_repository = self.find_repository() | ||
1026 | 187 | try: | ||
1027 | 188 | result_repo = result.find_repository() | ||
1028 | 189 | except errors.NoRepositoryPresent: | ||
1029 | 190 | result_repo = result.create_repository() | ||
1030 | 191 | target_is_empty = True | ||
1031 | 192 | else: | ||
1032 | 193 | target_is_empty = None # Unknown | ||
1033 | 194 | if stacked: | ||
1034 | 195 | raise errors.IncompatibleRepositories(source_repository, result_repo) | ||
1035 | 196 | interrepo = InterRepository.get(source_repository, result_repo) | ||
1036 | 197 | interrepo.fetch(revision_id=revision_id) | ||
1037 | 198 | result_branch = source_branch.sprout(result, | ||
1038 | 199 | revision_id=revision_id, repository=result_repo) | ||
1039 | 200 | if (create_tree_if_local and isinstance(target_transport, LocalTransport) | ||
1040 | 201 | and (result_repo is None or result_repo.make_working_trees())): | ||
1041 | 202 | wt = result.create_workingtree(accelerator_tree=accelerator_tree, | ||
1042 | 203 | hardlink=hardlink, from_branch=result_branch) | ||
1043 | 204 | wt.lock_write() | ||
1044 | 205 | try: | ||
1045 | 206 | if wt.path2id('') is None: | ||
1046 | 207 | try: | ||
1047 | 208 | wt.set_root_id(self.open_workingtree.get_root_id()) | ||
1048 | 209 | except errors.NoWorkingTree: | ||
1049 | 210 | pass | ||
1050 | 211 | finally: | ||
1051 | 212 | wt.unlock() | ||
1052 | 213 | return result | ||
1053 | 214 | |||
1054 | 215 | |||
1055 | 216 | class HgToSomethingConverter(Converter): | ||
1056 | 217 | """A class to upgrade an hg dir to something else.""" | ||
1057 | 218 | |||
1058 | 219 | def __init__(self, format): | ||
1059 | 220 | self.format = format | ||
1060 | 221 | if self.format is None: | ||
1061 | 222 | self.format = ControlDirFormat.get_default_format() | ||
1062 | 223 | |||
1063 | 224 | def convert(self, bzrdir, pb): | ||
1064 | 225 | source_repo = bzrdir.open_repository() | ||
1065 | 226 | source_branch = bzrdir.open_branch() | ||
1066 | 227 | target = self.format.initialize_on_transport(bzrdir.root_transport) | ||
1067 | 228 | target_repo = target.create_repository() | ||
1068 | 229 | target_repo.fetch(source_repo, pb=pb) | ||
1069 | 230 | target_branch = target.create_branch() | ||
1070 | 231 | target_branch.generate_revision_history(source_branch.last_revision()) | ||
1071 | 232 | target_wt = target.create_workingtree() | ||
1072 | 233 | bzrdir.root_transport.delete_tree(".hg") | ||
1073 | 234 | return target | ||
1074 | 235 | |||
1075 | 236 | |||
1076 | 237 | LockWarner = bzrlib.lockable_files._LockWarner | ||
1077 | 238 | |||
1078 | 239 | |||
1079 | 240 | class HgLockableFiles(bzrlib.lockable_files.LockableFiles): | ||
1080 | 241 | """Hg specific lockable files abstraction.""" | ||
1081 | 242 | |||
1082 | 243 | def __init__(self, lock, transport): | ||
1083 | 244 | self.lock_name = "hg lock" | ||
1084 | 245 | self._lock = lock | ||
1085 | 246 | self._transaction = None | ||
1086 | 247 | self._lock_mode = None | ||
1087 | 248 | self._transport = transport | ||
1088 | 249 | self._lock_warner = LockWarner(repr(self)) | ||
1089 | 250 | |||
1090 | 251 | |||
1091 | 252 | class HgDummyLock(object): | ||
1092 | 253 | """Lock that doesn't actually lock.""" | ||
1093 | 254 | |||
1094 | 255 | def __init__(self, hgrepo): | ||
1095 | 256 | self._hgrepo = hgrepo | ||
1096 | 257 | |||
1097 | 258 | def lock_write(self, token=None): | ||
1098 | 259 | if token is not None: | ||
1099 | 260 | raise errors.TokenLockingNotSupported(self) | ||
1100 | 261 | self._lock = self._hgrepo.wlock() | ||
1101 | 262 | |||
1102 | 263 | def lock_read(self): | ||
1103 | 264 | self._lock = None | ||
1104 | 265 | |||
1105 | 266 | def peek(self): | ||
1106 | 267 | raise NotImplementedError(self.peek) | ||
1107 | 268 | |||
1108 | 269 | def unlock(self): | ||
1109 | 270 | if self._lock is not None: | ||
1110 | 271 | self._lock.release() | ||
1111 | 272 | |||
1112 | 273 | def validate_token(self, token): | ||
1113 | 274 | if token is not None: | ||
1114 | 275 | raise errors.TokenLockingNotSupported(self) | ||
1115 | 276 | |||
1116 | 277 | |||
1117 | 278 | class HgLock(object): | ||
1118 | 279 | """A lock that thunks through to Hg.""" | ||
1119 | 280 | |||
1120 | 281 | def __init__(self, hgrepo): | ||
1121 | 282 | self._hgrepo = hgrepo | ||
1122 | 283 | |||
1123 | 284 | def lock_write(self, token=None): | ||
1124 | 285 | if token is not None: | ||
1125 | 286 | raise errors.TokenLockingNotSupported(self) | ||
1126 | 287 | self._lock = self._hgrepo.wlock() | ||
1127 | 288 | |||
1128 | 289 | def lock_read(self): | ||
1129 | 290 | self._lock = self._hgrepo.lock() | ||
1130 | 291 | |||
1131 | 292 | def peek(self): | ||
1132 | 293 | raise NotImplementedError(self.peek) | ||
1133 | 294 | |||
1134 | 295 | def unlock(self): | ||
1135 | 296 | self._lock.release() | ||
1136 | 297 | |||
1137 | 298 | def validate_token(self, token): | ||
1138 | 299 | if token is not None: | ||
1139 | 300 | raise errors.TokenLockingNotSupported(self) | ||
1140 | 301 | |||
1141 | 302 | def break_lock(self): | ||
1142 | 303 | pass | ||
1143 | 304 | |||
1144 | 305 | |||
1145 | 306 | class HgControlDirFormat(ControlDirFormat): | ||
1146 | 307 | """The .hg directory control format.""" | ||
1147 | 308 | |||
1148 | 309 | colocated_branches = True | ||
1149 | 310 | fixed_components = True | ||
1150 | 311 | |||
1151 | 312 | def __init__(self): | ||
1152 | 313 | super(HgControlDirFormat, self).__init__() | ||
1153 | 314 | self.workingtree_format = None | ||
1154 | 315 | |||
1155 | 316 | def get_converter(self, format): | ||
1156 | 317 | """We should write a converter.""" | ||
1157 | 318 | return HgToSomethingConverter(format) | ||
1158 | 319 | |||
1159 | 320 | def network_name(self): | ||
1160 | 321 | return "hg" | ||
1161 | 322 | |||
1162 | 323 | def get_format_description(self): | ||
1163 | 324 | return "Mercurial Branch" | ||
1164 | 325 | |||
1165 | 326 | def initialize_on_transport_ex(self, transport, use_existing_dir=False, | ||
1166 | 327 | create_prefix=False, force_new_repo=False, stacked_on=None, | ||
1167 | 328 | stack_on_pwd=None, repo_format_name=None, make_working_trees=None, | ||
1168 | 329 | shared_repo=False, vfs_only=False): | ||
1169 | 330 | from bzrlib import trace | ||
1170 | 331 | from bzrlib.bzrdir import CreateRepository | ||
1171 | 332 | from bzrlib.transport import do_catching_redirections | ||
1172 | 333 | def make_directory(transport): | ||
1173 | 334 | transport.mkdir('.') | ||
1174 | 335 | return transport | ||
1175 | 336 | def redirected(transport, e, redirection_notice): | ||
1176 | 337 | trace.note(redirection_notice) | ||
1177 | 338 | return transport._redirected_to(e.source, e.target) | ||
1178 | 339 | try: | ||
1179 | 340 | transport = do_catching_redirections(make_directory, transport, | ||
1180 | 341 | redirected) | ||
1181 | 342 | except errors.FileExists: | ||
1182 | 343 | if not use_existing_dir: | ||
1183 | 344 | raise | ||
1184 | 345 | except errors.NoSuchFile: | ||
1185 | 346 | if not create_prefix: | ||
1186 | 347 | raise | ||
1187 | 348 | transport.create_prefix() | ||
1188 | 349 | controldir = self.initialize_on_transport(transport) | ||
1189 | 350 | repository = controldir.open_repository() | ||
1190 | 351 | repository.lock_write() | ||
1191 | 352 | return (repository, controldir, False, CreateRepository(controldir)) | ||
1192 | 353 | |||
1193 | 354 | def get_branch_format(self): | ||
1194 | 355 | from bzrlib.plugins.hg.branch import HgBranchFormat | ||
1195 | 356 | return HgBranchFormat() | ||
1196 | 357 | |||
1197 | 358 | @property | ||
1198 | 359 | def repository_format(self): | ||
1199 | 360 | from bzrlib.plugins.hg.repository import HgRepositoryFormat | ||
1200 | 361 | return HgRepositoryFormat() | ||
1201 | 362 | |||
1202 | 363 | def initialize_on_transport(self, transport): | ||
1203 | 364 | """Initialize a new .not dir in the base directory of a Transport.""" | ||
1204 | 365 | return self.open(transport, _create=True) | ||
1205 | 366 | |||
1206 | 367 | def __eq__(self, other): | ||
1207 | 368 | return type(self) == type(other) | ||
1208 | 369 | |||
1209 | 370 | def __hash__(self): | ||
1210 | 371 | return hash((self.__class__,)) | ||
1211 | 372 | |||
1212 | 373 | def open(self, transport, _create=False, _found=None): | ||
1213 | 374 | """Open this directory. | ||
1214 | 375 | |||
1215 | 376 | :param _create: create the hg dir on the fly. private to | ||
1216 | 377 | HgControlDirFormat. | ||
1217 | 378 | """ | ||
1218 | 379 | # we dont grok readonly - hg isn't integrated with transport. | ||
1219 | 380 | try: | ||
1220 | 381 | url = transport.external_url() | ||
1221 | 382 | except errors.InProcessTransport: | ||
1222 | 383 | raise errors.NotBranchError(transport.base) | ||
1223 | 384 | if url.startswith('file://'): | ||
1224 | 385 | path = transport.local_abspath('.').encode('utf-8') | ||
1225 | 386 | lock_class = HgLock | ||
1226 | 387 | else: | ||
1227 | 388 | path = url | ||
1228 | 389 | lock_class = HgDummyLock | ||
1229 | 390 | lazy_load_mercurial() | ||
1230 | 391 | import mercurial.hg | ||
1231 | 392 | from bzrlib.plugins.hg.ui import ui | ||
1232 | 393 | repository = mercurial.hg.repository(ui(), path, create=_create) | ||
1233 | 394 | lockfiles = HgLockableFiles(lock_class(repository), transport) | ||
1234 | 395 | return HgDir(repository, transport, lockfiles, self) | ||
1235 | 0 | 396 | ||
1236 | === modified file 'fetch.py' | |||
1237 | --- fetch.py 2010-08-14 00:41:20 +0000 | |||
1238 | +++ fetch.py 2011-03-23 21:41:04 +0000 | |||
1239 | @@ -70,6 +70,8 @@ | |||
1240 | 70 | get_overlay, | 70 | get_overlay, |
1241 | 71 | ) | 71 | ) |
1242 | 72 | from bzrlib.plugins.hg.parsers import ( | 72 | from bzrlib.plugins.hg.parsers import ( |
1243 | 73 | chunkiter, | ||
1244 | 74 | deserialize_file_text, | ||
1245 | 73 | parse_changeset, | 75 | parse_changeset, |
1246 | 74 | parse_manifest, | 76 | parse_manifest, |
1247 | 75 | unpack_chunk_iter, | 77 | unpack_chunk_iter, |
1248 | @@ -145,6 +147,12 @@ | |||
1249 | 145 | # Set of directories that have been created in this delta, their file id | 147 | # Set of directories that have been created in this delta, their file id |
1250 | 146 | # as value. | 148 | # as value. |
1251 | 147 | directories = {} | 149 | directories = {} |
1252 | 150 | if basis_inv is None: | ||
1253 | 151 | # Root is mandatory | ||
1254 | 152 | extra, root_id = inventory_create_directory(directories, | ||
1255 | 153 | basis_inv, other_inv, "", lookup_file_id, revid) | ||
1256 | 154 | for e in extra: | ||
1257 | 155 | yield e | ||
1258 | 148 | # Dictionary of directories that could have been made empty in this delta, | 156 | # Dictionary of directories that could have been made empty in this delta, |
1259 | 149 | # with the set of removed children as value. | 157 | # with the set of removed children as value. |
1260 | 150 | maybe_empty_dirs = defaultdict(set) | 158 | maybe_empty_dirs = defaultdict(set) |
1261 | @@ -209,17 +217,20 @@ | |||
1262 | 209 | ie.symlink_target = lookup_symlink((fileid, ie.revision)) | 217 | ie.symlink_target = lookup_symlink((fileid, ie.revision)) |
1263 | 210 | yield (old_path, path, fileid, ie) | 218 | yield (old_path, path, fileid, ie) |
1264 | 211 | # Remove empty directories | 219 | # Remove empty directories |
1276 | 212 | for path in sorted(maybe_empty_dirs.keys(), reverse=True): | 220 | while maybe_empty_dirs: |
1277 | 213 | if maybe_empty_dirs[path] is None: | 221 | for path in sorted(maybe_empty_dirs.keys(), reverse=True): |
1278 | 214 | # Stuff was added to this directory in this revision, don't bother | 222 | removed_children = maybe_empty_dirs.pop(path) |
1279 | 215 | continue | 223 | if removed_children is None: |
1280 | 216 | file_id = basis_inv.path2id(path) | 224 | # Stuff was added to this directory in this revision, |
1281 | 217 | # Is this directory really empty ? | 225 | # don't bother |
1282 | 218 | if set(basis_inv[file_id].children.keys()) == maybe_empty_dirs[path]: | 226 | continue |
1283 | 219 | yield (path, None, file_id, None) | 227 | file_id = basis_inv.path2id(path) |
1284 | 220 | dirname = os.path.dirname(path) | 228 | # Is this directory really empty ? |
1285 | 221 | if maybe_empty_dirs[dirname] is not None: | 229 | if set(basis_inv[file_id].children.keys()) == removed_children: |
1286 | 222 | maybe_empty_dirs[dirname].add(basis_inv[file_id].name) | 230 | yield (path, None, file_id, None) |
1287 | 231 | dirname = os.path.dirname(path) | ||
1288 | 232 | if maybe_empty_dirs[dirname] is not None: | ||
1289 | 233 | maybe_empty_dirs[dirname].add(basis_inv[file_id].name) | ||
1290 | 223 | 234 | ||
1291 | 224 | 235 | ||
1292 | 225 | def create_directory_texts(texts, invdelta): | 236 | def create_directory_texts(texts, invdelta): |
1293 | @@ -231,7 +242,8 @@ | |||
1294 | 231 | def generate_stream(): | 242 | def generate_stream(): |
1295 | 232 | for (old_path, new_path, fileid, ie) in invdelta: | 243 | for (old_path, new_path, fileid, ie) in invdelta: |
1296 | 233 | if old_path is None and ie.kind == "directory": | 244 | if old_path is None and ie.kind == "directory": |
1298 | 234 | record = FulltextContentFactory((fileid, ie.revision), (), None, "") | 245 | record = FulltextContentFactory((fileid, ie.revision), (), |
1299 | 246 | None, "") | ||
1300 | 235 | record.parents = () | 247 | record.parents = () |
1301 | 236 | yield record | 248 | yield record |
1302 | 237 | texts.insert_record_stream(generate_stream()) | 249 | texts.insert_record_stream(generate_stream()) |
1303 | @@ -264,7 +276,10 @@ | |||
1304 | 264 | if expected_files != files: | 276 | if expected_files != files: |
1305 | 265 | raise AssertionError | 277 | raise AssertionError |
1306 | 266 | lookup = [m.__getitem__ for m, f in manifest_parents[:2]] | 278 | lookup = [m.__getitem__ for m, f in manifest_parents[:2]] |
1308 | 267 | (manifest, flags) = manifest_and_flags_from_tree(parent_trees, tree, | 279 | for i in range(2): |
1309 | 280 | if len(lookup) <= i: | ||
1310 | 281 | lookup.append({}.__getitem__) | ||
1311 | 282 | (manifest, flags, unusual_fileids) = manifest_and_flags_from_tree(parent_trees, tree, | ||
1312 | 268 | mapping, lookup) | 283 | mapping, lookup) |
1313 | 269 | if set(manifest.keys()) != set(expected_manifest.keys()): | 284 | if set(manifest.keys()) != set(expected_manifest.keys()): |
1314 | 270 | raise AssertionError("Different contents in manifests: %r, %r" % | 285 | raise AssertionError("Different contents in manifests: %r, %r" % |
1315 | @@ -313,6 +328,18 @@ | |||
1316 | 313 | ret.append(self._inventories[revid]) | 328 | ret.append(self._inventories[revid]) |
1317 | 314 | return ret | 329 | return ret |
1318 | 315 | 330 | ||
1319 | 331 | def _lookup_file_target(self, key): | ||
1320 | 332 | return str(self._symlink_targets[key]).decode("utf-8") | ||
1321 | 333 | |||
1322 | 334 | def _lookup_file_metadata(self, key): | ||
1323 | 335 | try: | ||
1324 | 336 | return self._text_metadata[key] | ||
1325 | 337 | except KeyError: | ||
1326 | 338 | (fileid, revision) = key | ||
1327 | 339 | stream = self.target.texts.get_record_stream([key], 'unordered', True) | ||
1328 | 340 | record = stream.next() | ||
1329 | 341 | return (record.sha1, len(record.get_bytes_as("fulltext"))) | ||
1330 | 342 | |||
1331 | 316 | def _import_manifest_delta(self, manifest, flags, files, rev, | 343 | def _import_manifest_delta(self, manifest, flags, files, rev, |
1332 | 317 | mapping): | 344 | mapping): |
1333 | 318 | parent_invs = self._get_inventories(rev.parent_ids) | 345 | parent_invs = self._get_inventories(rev.parent_ids) |
1334 | @@ -333,8 +360,8 @@ | |||
1335 | 333 | invdelta = list(manifest_to_inventory_delta(mapping.generate_file_id, | 360 | invdelta = list(manifest_to_inventory_delta(mapping.generate_file_id, |
1336 | 334 | basis_inv, other_inv, (basis_manifest, basis_flags), | 361 | basis_inv, other_inv, (basis_manifest, basis_flags), |
1337 | 335 | (manifest, flags), rev.revision_id, files, | 362 | (manifest, flags), rev.revision_id, files, |
1340 | 336 | self._text_metadata.__getitem__, | 363 | self._lookup_file_metadata, |
1341 | 337 | self._symlink_targets.__getitem__)) | 364 | self._lookup_file_target)) |
1342 | 338 | return basis_inv, invdelta | 365 | return basis_inv, invdelta |
1343 | 339 | 366 | ||
1344 | 340 | def _get_target_fulltext(self, key): | 367 | def _get_target_fulltext(self, key): |
1345 | @@ -342,31 +369,37 @@ | |||
1346 | 342 | return self._symlink_targets[key] | 369 | return self._symlink_targets[key] |
1347 | 343 | return self._target_overlay.get_file_fulltext(key) | 370 | return self._target_overlay.get_file_fulltext(key) |
1348 | 344 | 371 | ||
1350 | 345 | def _unpack_texts(self, cg, mapping, filetext_map, pb): | 372 | def _create_text_record(self, fileid, revision, parents, kind, fulltext): |
1351 | 373 | key = (fileid, revision) | ||
1352 | 374 | if kind == "symlink": | ||
1353 | 375 | self._symlink_targets[key] = fulltext | ||
1354 | 376 | bzr_fulltext = "" | ||
1355 | 377 | else: | ||
1356 | 378 | (meta, bzr_fulltext) = deserialize_file_text(str(fulltext)) | ||
1357 | 379 | record = FulltextContentFactory(key, [(fileid, p) for p in parents], | ||
1358 | 380 | osutils.sha_string(bzr_fulltext), bzr_fulltext) | ||
1359 | 381 | self._text_metadata[key] = (record.sha1, len(bzr_fulltext)) | ||
1360 | 382 | return record | ||
1361 | 383 | |||
1362 | 384 | def _unpack_texts(self, cg, mapping, kind_map, pb): | ||
1363 | 346 | i = 0 | 385 | i = 0 |
1364 | 347 | # Texts | 386 | # Texts |
1366 | 348 | while 1: | 387 | while True: |
1367 | 349 | f = mercurial.changegroup.getchunk(cg) | 388 | f = mercurial.changegroup.getchunk(cg) |
1368 | 350 | if not f: | 389 | if not f: |
1369 | 351 | break | 390 | break |
1370 | 352 | i += 1 | 391 | i += 1 |
1372 | 353 | pb.update("fetching texts", i, len(filetext_map)) | 392 | pb.update("fetching texts", i, len(kind_map)) |
1373 | 354 | fileid = mapping.generate_file_id(f) | 393 | fileid = mapping.generate_file_id(f) |
1375 | 355 | chunkiter = mercurial.changegroup.chunkiter(cg) | 394 | itertextchunks = chunkiter(cg) |
1376 | 356 | def get_text(node): | 395 | def get_text(node): |
1378 | 357 | key = iter(filetext_map[fileid][node]).next() | 396 | key = iter(kind_map[fileid][node]).next() |
1379 | 358 | return self._get_target_fulltext(key) | 397 | return self._get_target_fulltext(key) |
1391 | 359 | for fulltext, hgkey, hgparents, csid in unpack_chunk_iter(chunkiter, get_text): | 398 | for fulltext, hgkey, hgparents, csid in unpack_chunk_iter(itertextchunks, get_text): |
1392 | 360 | for revision, (kind, parents) in filetext_map[fileid][hgkey].iteritems(): | 399 | for revision, kind in kind_map[fileid][hgkey].iteritems(): |
1393 | 361 | key = (fileid, revision) | 400 | text_parents = () |
1394 | 362 | if kind == "symlink": | 401 | yield self._create_text_record(fileid, revision, |
1395 | 363 | self._symlink_targets[key] = fulltext | 402 | text_parents, kind, fulltext) |
1385 | 364 | bzr_fulltext = "" | ||
1386 | 365 | else: | ||
1387 | 366 | bzr_fulltext = fulltext | ||
1388 | 367 | record = FulltextContentFactory(key, [(fileid, p) for p in parents], osutils.sha_string(bzr_fulltext), bzr_fulltext) | ||
1389 | 368 | self._text_metadata[key] = (record.sha1, len(bzr_fulltext)) | ||
1390 | 369 | yield record | ||
1396 | 370 | 403 | ||
1397 | 371 | def _add_inventories(self, todo, mapping, pb): | 404 | def _add_inventories(self, todo, mapping, pb): |
1398 | 372 | assert isinstance(todo, list) | 405 | assert isinstance(todo, list) |
1399 | @@ -444,7 +477,66 @@ | |||
1400 | 444 | return r | 477 | return r |
1401 | 445 | raise AssertionError | 478 | raise AssertionError |
1402 | 446 | 479 | ||
1404 | 447 | def _unpack_manifests(self, chunkiter, mapping, filetext_map, todo, pb): | 480 | def _determine_text_parents(self, parents, path, fileid, revid, kind_map): |
1405 | 481 | ret = [] | ||
1406 | 482 | for parent in parents: | ||
1407 | 483 | tp = self._determine_text_parent(parent, path, fileid, revid, kind_map) | ||
1408 | 484 | if tp is not None: | ||
1409 | 485 | ret.append(tp) | ||
1410 | 486 | return ret | ||
1411 | 487 | |||
1412 | 488 | def _determine_text_parent(self, parent, path, fileid, revid, kind_map): | ||
1413 | 489 | path2id = getattr(parent, "path2id", None) | ||
1414 | 490 | if path2id is None: # manifest | ||
1415 | 491 | parent_node = parent.get(path) | ||
1416 | 492 | if parent_node is None: | ||
1417 | 493 | # Didn't exist in parent | ||
1418 | 494 | return None | ||
1419 | 495 | revisions = kind_map[fileid][parent_node].keys() | ||
1420 | 496 | tp = self._find_most_recent_ancestor(revisions, revid) | ||
1421 | 497 | return tp | ||
1422 | 498 | elif path2id(path) == fileid: | ||
1423 | 499 | # FIXME: Handle situation where path is not actually in | ||
1424 | 500 | # parent | ||
1425 | 501 | return parent[fileid].revision | ||
1426 | 502 | else: | ||
1427 | 503 | return None | ||
1428 | 504 | |||
1429 | 505 | def _process_manifest(self, manifest, flags, revid, mapping, kind_map): | ||
1430 | 506 | """Process a manifest. | ||
1431 | 507 | |||
1432 | 508 | :param manifest: Mercurial manifest (dict of path -> node) | ||
1433 | 509 | :param flags: Mercurial manifest flags (dict of path -> mode) | ||
1434 | 510 | :param revid: Bazaar revision id | ||
1435 | 511 | :param mapping: Bzr<->Hg mapping to use | ||
1436 | 512 | :param kind_map: Mapping of (fileid, node, revid) -> kind | ||
1437 | 513 | """ | ||
1438 | 514 | self._target_overlay.remember_manifest(revid, | ||
1439 | 515 | self._revisions[revid].parent_ids, (manifest, flags)) | ||
1440 | 516 | if not self._files[revid]: | ||
1441 | 517 | # Avoid fetching inventories and parent manifests | ||
1442 | 518 | # unnecessarily | ||
1443 | 519 | return | ||
1444 | 520 | rev = self._revisions[revid] | ||
1445 | 521 | parents = [] | ||
1446 | 522 | for previd in rev.parent_ids: | ||
1447 | 523 | try: | ||
1448 | 524 | inv = self.target.get_inventory(previd) | ||
1449 | 525 | except errors.NoSuchRevision: | ||
1450 | 526 | parents.append(self._target_overlay.get_manifest_and_flags_by_revid(previd)[0]) | ||
1451 | 527 | else: | ||
1452 | 528 | parents.append(inv) | ||
1453 | 529 | for path in self._files[revid]: | ||
1454 | 530 | assert type(path) is str | ||
1455 | 531 | fileid = mapping.generate_file_id(path) | ||
1456 | 532 | if not path in manifest: | ||
1457 | 533 | # Path no longer exists | ||
1458 | 534 | continue | ||
1459 | 535 | kind = flags_kind(flags, path) | ||
1460 | 536 | node = manifest[path] | ||
1461 | 537 | kind_map[fileid][node][revid] = kind | ||
1462 | 538 | |||
1463 | 539 | def _unpack_manifests(self, chunkiter, mapping, kind_map, todo, pb): | ||
1464 | 448 | """Unpack the manifest deltas. | 540 | """Unpack the manifest deltas. |
1465 | 449 | 541 | ||
1466 | 450 | :param chunkiter: Iterator over delta chunks for the manifest. | 542 | :param chunkiter: Iterator over delta chunks for the manifest. |
1467 | @@ -458,42 +550,8 @@ | |||
1468 | 458 | for revid in self._manifest2rev_map[hgkey]: | 550 | for revid in self._manifest2rev_map[hgkey]: |
1469 | 459 | todo.append(revid) | 551 | todo.append(revid) |
1470 | 460 | yield (revid, self._revisions[revid].parent_ids, fulltext) | 552 | yield (revid, self._revisions[revid].parent_ids, fulltext) |
1507 | 461 | self._target_overlay.remember_manifest(revid, | 553 | self._process_manifest(manifest, flags, revid, mapping, |
1508 | 462 | self._revisions[revid].parent_ids, (manifest, flags)) | 554 | kind_map) |
1473 | 463 | if not self._files[revid]: | ||
1474 | 464 | # Avoid fetching inventories and parent manifests | ||
1475 | 465 | # unnecessarily | ||
1476 | 466 | continue | ||
1477 | 467 | rev = self._revisions[revid] | ||
1478 | 468 | parents = [] | ||
1479 | 469 | for previd in rev.parent_ids: | ||
1480 | 470 | try: | ||
1481 | 471 | inv = self.target.get_inventory(previd) | ||
1482 | 472 | except errors.NoSuchRevision: | ||
1483 | 473 | parents.append(self._target_overlay.get_manifest_and_flags_by_revid(previd)[0]) | ||
1484 | 474 | else: | ||
1485 | 475 | parents.append(inv) | ||
1486 | 476 | for path in self._files[revid]: | ||
1487 | 477 | assert type(path) is str | ||
1488 | 478 | fileid = mapping.generate_file_id(path) | ||
1489 | 479 | if not path in manifest: | ||
1490 | 480 | # Path still has to actually exist.. | ||
1491 | 481 | continue | ||
1492 | 482 | kind = flags_kind(flags, path) | ||
1493 | 483 | text_parents = [] | ||
1494 | 484 | for parent in parents: | ||
1495 | 485 | path2id = getattr(parent, "path2id", None) | ||
1496 | 486 | if path2id is None: # manifest | ||
1497 | 487 | node = parent.get(path) | ||
1498 | 488 | if node is None: | ||
1499 | 489 | continue | ||
1500 | 490 | revisions = filetext_map[fileid][node] | ||
1501 | 491 | tp = self._find_most_recent_ancestor(revisions.keys(), revid) | ||
1502 | 492 | text_parents.append(tp) | ||
1503 | 493 | elif path2id(path) == fileid: | ||
1504 | 494 | # FIXME: Handle situation where path is not actually in parent | ||
1505 | 495 | text_parents.append(parent[fileid].revision) | ||
1506 | 496 | filetext_map[fileid][manifest[path]][revid] = (kind, text_parents) | ||
1509 | 497 | 555 | ||
1510 | 498 | def addchangegroup(self, cg, mapping): | 556 | def addchangegroup(self, cg, mapping): |
1511 | 499 | """Import a Mercurial changegroup into the target repository. | 557 | """Import a Mercurial changegroup into the target repository. |
1512 | @@ -502,26 +560,27 @@ | |||
1513 | 502 | :param mapping: Mercurial mapping | 560 | :param mapping: Mercurial mapping |
1514 | 503 | """ | 561 | """ |
1515 | 504 | # Changesets | 562 | # Changesets |
1517 | 505 | chunkiter = mercurial.changegroup.chunkiter(cg) | 563 | changesetchunks = chunkiter(cg) |
1518 | 506 | pb = ui.ui_factory.nested_progress_bar() | 564 | pb = ui.ui_factory.nested_progress_bar() |
1519 | 507 | try: | 565 | try: |
1521 | 508 | self._unpack_changesets(chunkiter, mapping, pb) | 566 | self._unpack_changesets(changesetchunks, mapping, pb) |
1522 | 509 | finally: | 567 | finally: |
1523 | 510 | pb.finished() | 568 | pb.finished() |
1524 | 511 | # Manifests | 569 | # Manifests |
1527 | 512 | manifestchunks = mercurial.changegroup.chunkiter(cg) | 570 | manifestchunks = chunkiter(cg) |
1528 | 513 | filetext_map = defaultdict(lambda: defaultdict(dict)) | 571 | kind_map = defaultdict(lambda: defaultdict(dict)) |
1529 | 514 | todo = [] | 572 | todo = [] |
1530 | 515 | pb = ui.ui_factory.nested_progress_bar() | 573 | pb = ui.ui_factory.nested_progress_bar() |
1531 | 516 | try: | 574 | try: |
1533 | 517 | self._target_overlay.remember_manifest_texts(self._unpack_manifests(manifestchunks, mapping, filetext_map, todo, pb)) | 575 | self._target_overlay.remember_manifest_texts( |
1534 | 576 | self._unpack_manifests(manifestchunks, mapping, kind_map, todo, pb)) | ||
1535 | 518 | finally: | 577 | finally: |
1536 | 519 | pb.finished() | 578 | pb.finished() |
1537 | 520 | # Texts | 579 | # Texts |
1538 | 521 | pb = ui.ui_factory.nested_progress_bar() | 580 | pb = ui.ui_factory.nested_progress_bar() |
1539 | 522 | try: | 581 | try: |
1540 | 523 | self.target.texts.insert_record_stream( | 582 | self.target.texts.insert_record_stream( |
1542 | 524 | self._unpack_texts(cg, mapping, filetext_map, pb)) | 583 | self._unpack_texts(cg, mapping, kind_map, pb)) |
1543 | 525 | finally: | 584 | finally: |
1544 | 526 | pb.finished() | 585 | pb.finished() |
1545 | 527 | # Adding actual data | 586 | # Adding actual data |
1546 | 528 | 587 | ||
1547 | === modified file 'info.py' | |||
1548 | --- info.py 2010-08-05 23:55:52 +0000 | |||
1549 | +++ info.py 2011-03-23 21:41:04 +0000 | |||
1550 | @@ -1,7 +1,6 @@ | |||
1551 | 1 | bzr_plugin_name = 'hg' | 1 | bzr_plugin_name = 'hg' |
1552 | 2 | 2 | ||
1555 | 3 | bzr_compatible_versions = [(1, x, 0) for x in [13, 14, 15, 16, 17, 18]] + \ | 3 | bzr_compatible_versions = [(2, x, 0) for x in [3, 4]] |
1554 | 4 | [(2, x, 0) for x in [0, 1, 2]] | ||
1556 | 5 | 4 | ||
1557 | 6 | bzr_minimum_version = bzr_compatible_versions[0] | 5 | bzr_minimum_version = bzr_compatible_versions[0] |
1558 | 7 | 6 | ||
1559 | @@ -11,4 +10,6 @@ | |||
1560 | 11 | 10 | ||
1561 | 12 | bzr_control_formats = {"Mercurial": {'.hg/': None}} | 11 | bzr_control_formats = {"Mercurial": {'.hg/': None}} |
1562 | 13 | 12 | ||
1564 | 14 | hg_compatible_versions = ["1.6"] | 13 | hg_compatible_versions = [(1, 6), (1, 7), (1, 8)] |
1565 | 14 | |||
1566 | 15 | hg_compatible_version_strings = ["%d.%d" % x for x in hg_compatible_versions] | ||
1567 | 15 | 16 | ||
1568 | === modified file 'mapping.py' | |||
1569 | --- mapping.py 2010-02-08 13:32:23 +0000 | |||
1570 | +++ mapping.py 2011-03-23 21:41:04 +0000 | |||
1571 | @@ -42,12 +42,15 @@ | |||
1572 | 42 | """Convert a Mercurial 'convert_revision' extra to a Bazaar 'converted-from' revprop. | 42 | """Convert a Mercurial 'convert_revision' extra to a Bazaar 'converted-from' revprop. |
1573 | 43 | 43 | ||
1574 | 44 | """ | 44 | """ |
1576 | 45 | (kind, revid) = rev.split(":", 1) | 45 | try: |
1577 | 46 | (kind, revid) = rev.split(":", 1) | ||
1578 | 47 | except ValueError: | ||
1579 | 48 | return "unspecified %s\n" % rev | ||
1580 | 46 | if kind == "svn": | 49 | if kind == "svn": |
1581 | 47 | url, revnum = revid.rsplit('@', 1) | 50 | url, revnum = revid.rsplit('@', 1) |
1582 | 48 | revnum = int(revnum) | 51 | revnum = int(revnum) |
1583 | 49 | parts = url.split('/', 1) | 52 | parts = url.split('/', 1) |
1585 | 50 | uuid = parts.pop(0)[4:] | 53 | uuid = parts.pop(0) |
1586 | 51 | mod = '' | 54 | mod = '' |
1587 | 52 | if parts: | 55 | if parts: |
1588 | 53 | mod = parts[0] | 56 | mod = parts[0] |
1589 | @@ -65,7 +68,9 @@ | |||
1590 | 65 | (uuid, revnumstr, branchpathstr) = revid.split(":", 2) | 68 | (uuid, revnumstr, branchpathstr) = revid.split(":", 2) |
1591 | 66 | revnum = int(revnumstr) | 69 | revnum = int(revnumstr) |
1592 | 67 | branchpath = urllib.unquote(branchpathstr) | 70 | branchpath = urllib.unquote(branchpathstr) |
1594 | 68 | return "svn:%s%s@%s" % (uuid, branchpath, revnum) | 71 | return "svn:%s/%s@%s" % (uuid, branchpath, revnum) |
1595 | 72 | elif kind == "unspecified": | ||
1596 | 73 | return revid | ||
1597 | 69 | else: | 74 | else: |
1598 | 70 | raise KeyError("Unknown VCS '%s'" % kind) | 75 | raise KeyError("Unknown VCS '%s'" % kind) |
1599 | 71 | 76 | ||
1600 | @@ -175,6 +180,7 @@ | |||
1601 | 175 | :param parent_node_lookup: 2-tuple with functions to look up the nodes | 180 | :param parent_node_lookup: 2-tuple with functions to look up the nodes |
1602 | 176 | of paths in the tree's parents | 181 | of paths in the tree's parents |
1603 | 177 | """ | 182 | """ |
1604 | 183 | assert len(parent_node_lookup) == 2 | ||
1605 | 178 | unusual_fileids = {} | 184 | unusual_fileids = {} |
1606 | 179 | def get_text_parents(path): | 185 | def get_text_parents(path): |
1607 | 180 | assert type(path) == str | 186 | assert type(path) == str |
1608 | @@ -184,6 +190,7 @@ | |||
1609 | 184 | ret.append(lookup(path)) | 190 | ret.append(lookup(path)) |
1610 | 185 | except KeyError: | 191 | except KeyError: |
1611 | 186 | ret.append(mercurial.node.nullid) | 192 | ret.append(mercurial.node.nullid) |
1612 | 193 | assert len(ret) == 2 | ||
1613 | 187 | return tuple(ret) | 194 | return tuple(ret) |
1614 | 188 | manifest = {} | 195 | manifest = {} |
1615 | 189 | flags = {} | 196 | flags = {} |
1616 | @@ -263,7 +270,7 @@ | |||
1617 | 263 | elif len(revision_id) == 40: | 270 | elif len(revision_id) == 40: |
1618 | 264 | hexhgrevid = revision_id | 271 | hexhgrevid = revision_id |
1619 | 265 | else: | 272 | else: |
1621 | 266 | raise AssertionError | 273 | raise AssertionError("Invalid hg id %r" % revision_id) |
1622 | 267 | return "%s:%s" % (cls.revid_prefix, hexhgrevid) | 274 | return "%s:%s" % (cls.revid_prefix, hexhgrevid) |
1623 | 268 | 275 | ||
1624 | 269 | @classmethod | 276 | @classmethod |
1625 | 270 | 277 | ||
1626 | === modified file 'overlay.py' | |||
1627 | --- overlay.py 2009-11-26 21:33:32 +0000 | |||
1628 | +++ overlay.py 2011-03-23 21:41:04 +0000 | |||
1629 | @@ -122,7 +122,7 @@ | |||
1630 | 122 | """ | 122 | """ |
1631 | 123 | if self.manifests_vf is not None: | 123 | if self.manifests_vf is not None: |
1632 | 124 | self.manifests_vf.insert_record_stream( | 124 | self.manifests_vf.insert_record_stream( |
1634 | 125 | ((FulltextContentFactory((revid,), [(p,) for p in parent_revids] , None, text) for (revid, parent_revids, text) in entries))) | 125 | ((FulltextContentFactory((revid,), [(p,) for p in parent_revids], None, str(text)) for (revid, parent_revids, text) in entries))) |
1635 | 126 | 126 | ||
1636 | 127 | def _get_cached_manifest(self, revid): | 127 | def _get_cached_manifest(self, revid): |
1637 | 128 | """Attempt to retrieve a cached manifest. | 128 | """Attempt to retrieve a cached manifest. |
1638 | 129 | 129 | ||
1639 | === modified file 'parsers.py' | |||
1640 | --- parsers.py 2010-02-08 13:32:23 +0000 | |||
1641 | +++ parsers.py 2011-03-23 21:41:04 +0000 | |||
1642 | @@ -25,6 +25,7 @@ | |||
1643 | 25 | """ | 25 | """ |
1644 | 26 | 26 | ||
1645 | 27 | import mercurial.changelog | 27 | import mercurial.changelog |
1646 | 28 | import mercurial.changegroup | ||
1647 | 28 | import mercurial.manifest | 29 | import mercurial.manifest |
1648 | 29 | import mercurial.mdiff | 30 | import mercurial.mdiff |
1649 | 30 | import mercurial.node | 31 | import mercurial.node |
1650 | @@ -81,6 +82,7 @@ | |||
1651 | 81 | :param text: Text to parse | 82 | :param text: Text to parse |
1652 | 82 | :return: Tuple with (manifest, user, (time, timezone), files, desc, extra) | 83 | :return: Tuple with (manifest, user, (time, timezone), files, desc, extra) |
1653 | 83 | """ | 84 | """ |
1654 | 85 | text = str(text) | ||
1655 | 84 | last = text.index("\n\n") | 86 | last = text.index("\n\n") |
1656 | 85 | desc = text[last + 2:].decode("utf-8") | 87 | desc = text[last + 2:].decode("utf-8") |
1657 | 86 | l = text[:last].split('\n') | 88 | l = text[:last].split('\n') |
1658 | @@ -179,3 +181,34 @@ | |||
1659 | 179 | node = mercurial.node.hex(node) | 181 | node = mercurial.node.hex(node) |
1660 | 180 | lines.append("%s\0%s%s\n" % (path, node, flags.get(path, ""))) | 182 | lines.append("%s\0%s%s\n" % (path, node, flags.get(path, ""))) |
1661 | 181 | return "".join(lines) | 183 | return "".join(lines) |
1662 | 184 | |||
1663 | 185 | |||
1664 | 186 | def serialize_file_text(meta, text): | ||
1665 | 187 | if meta or text.startswith('\1\n'): | ||
1666 | 188 | mt = ["%s: %s\n" % (k, v) for k, v in sorted(meta.iteritems())] | ||
1667 | 189 | text = "\1\n%s\1\n%s" % ("".join(mt), text) | ||
1668 | 190 | return text | ||
1669 | 191 | |||
1670 | 192 | |||
1671 | 193 | def deserialize_file_text(text): | ||
1672 | 194 | if not text.startswith("\1\n"): | ||
1673 | 195 | return ({}, text) | ||
1674 | 196 | s = text.index('\1\n', 2) | ||
1675 | 197 | mt = text[2:s] | ||
1676 | 198 | meta = {} | ||
1677 | 199 | for l in mt.splitlines(): | ||
1678 | 200 | k, v = l.split(": ", 1) | ||
1679 | 201 | meta[k] = v | ||
1680 | 202 | return meta, text[s+2:] | ||
1681 | 203 | |||
1682 | 204 | |||
1683 | 205 | def chunkiter(source, progress=None): | ||
1684 | 206 | """iterate through the chunks in source, yielding a sequence of chunks | ||
1685 | 207 | (strings)""" | ||
1686 | 208 | while 1: | ||
1687 | 209 | c = mercurial.changegroup.getchunk(source) | ||
1688 | 210 | if not c: | ||
1689 | 211 | break | ||
1690 | 212 | elif progress is not None: | ||
1691 | 213 | progress() | ||
1692 | 214 | yield c | ||
1693 | 182 | 215 | ||
1694 | === modified file 'repository.py' | |||
1695 | --- repository.py 2010-07-31 18:38:03 +0000 | |||
1696 | +++ repository.py 2011-03-23 21:41:04 +0000 | |||
1697 | @@ -62,8 +62,23 @@ | |||
1698 | 62 | support the repository format. | 62 | support the repository format. |
1699 | 63 | """ | 63 | """ |
1700 | 64 | rich_root_data = True | 64 | rich_root_data = True |
1703 | 65 | 65 | fast_deltas = True | |
1704 | 66 | def initialize(self, url, shared=False, _internal=False): | 66 | supports_leaving_lock = False |
1705 | 67 | supports_funky_characters = True | ||
1706 | 68 | supports_external_lookups = False | ||
1707 | 69 | supports_full_versioned_files = False | ||
1708 | 70 | |||
1709 | 71 | @property | ||
1710 | 72 | def _matchingbzrdir(self): | ||
1711 | 73 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
1712 | 74 | return HgControlDirFormat() | ||
1713 | 75 | |||
1714 | 76 | def initialize(self, controldir, shared=False, _internal=False): | ||
1715 | 77 | from bzrlib.plugins.hg.dir import HgDir | ||
1716 | 78 | if shared: | ||
1717 | 79 | raise errors.IncompatibleFormat(self, controldir._format) | ||
1718 | 80 | if isinstance(controldir, HgDir): | ||
1719 | 81 | return controldir.open_repository() | ||
1720 | 67 | raise errors.UninitializableFormat(self) | 82 | raise errors.UninitializableFormat(self) |
1721 | 68 | 83 | ||
1722 | 69 | def is_supported(self): | 84 | def is_supported(self): |
1723 | @@ -126,6 +141,7 @@ | |||
1724 | 126 | return ancestry_cache[some_revision_id] | 141 | return ancestry_cache[some_revision_id] |
1725 | 127 | except KeyError: | 142 | except KeyError: |
1726 | 128 | pass | 143 | pass |
1727 | 144 | assert some_revision_id in all_relevant_revisions | ||
1728 | 129 | ancestry = set() | 145 | ancestry = set() |
1729 | 130 | # add what can be reached from some_revision_id | 146 | # add what can be reached from some_revision_id |
1730 | 131 | # TODO: must factor this trivial iteration in bzrlib.graph cleanly. | 147 | # TODO: must factor this trivial iteration in bzrlib.graph cleanly. |
1731 | @@ -287,6 +303,8 @@ | |||
1732 | 287 | 303 | ||
1733 | 288 | _serializer = None | 304 | _serializer = None |
1734 | 289 | 305 | ||
1735 | 306 | chk_bytes = None | ||
1736 | 307 | |||
1737 | 290 | def __init__(self, hgrepo, hgdir, lockfiles): | 308 | def __init__(self, hgrepo, hgdir, lockfiles): |
1738 | 291 | ForeignRepository.__init__(self, HgRepositoryFormat(), hgdir, lockfiles) | 309 | ForeignRepository.__init__(self, HgRepositoryFormat(), hgdir, lockfiles) |
1739 | 292 | self._hgrepo = hgrepo | 310 | self._hgrepo = hgrepo |
1740 | @@ -303,6 +321,9 @@ | |||
1741 | 303 | self.inventories = None | 321 | self.inventories = None |
1742 | 304 | self.texts = None | 322 | self.texts = None |
1743 | 305 | 323 | ||
1744 | 324 | def revision_graph_can_have_wrong_parents(self): | ||
1745 | 325 | return False | ||
1746 | 326 | |||
1747 | 306 | def _warn_if_deprecated(self): | 327 | def _warn_if_deprecated(self): |
1748 | 307 | # This class isn't deprecated | 328 | # This class isn't deprecated |
1749 | 308 | pass | 329 | pass |
1750 | @@ -341,20 +362,33 @@ | |||
1751 | 341 | 362 | ||
1752 | 342 | def lookup_bzr_revision_id(self, revision_id): | 363 | def lookup_bzr_revision_id(self, revision_id): |
1753 | 343 | """See ForeignRepository.lookup_bzr_revision_id().""" | 364 | """See ForeignRepository.lookup_bzr_revision_id().""" |
1755 | 344 | assert type(revision_id) is str | 365 | assert type(revision_id) is str, "invalid revid: %r" % revision_id |
1756 | 345 | # TODO: Handle round-tripped revisions | 366 | # TODO: Handle round-tripped revisions |
1757 | 346 | try: | 367 | try: |
1758 | 347 | return mapping_registry.revision_id_bzr_to_foreign(revision_id) | 368 | return mapping_registry.revision_id_bzr_to_foreign(revision_id) |
1759 | 348 | except errors.InvalidRevisionId: | 369 | except errors.InvalidRevisionId: |
1760 | 349 | raise errors.NoSuchRevision(self, revision_id) | 370 | raise errors.NoSuchRevision(self, revision_id) |
1761 | 350 | 371 | ||
1762 | 372 | def get_parent_map(self, revids): | ||
1763 | 373 | ret = {} | ||
1764 | 374 | for revid in revids: | ||
1765 | 375 | hgrevid, mapping = self.lookup_bzr_revision_id(revid) | ||
1766 | 376 | # FIXME: what about extra parents? | ||
1767 | 377 | hgparents = self._hgrepo.changelog.parents(hgrevid) | ||
1768 | 378 | ret[revid] = as_bzr_parents(hgparents, self.lookup_foreign_revision_id) | ||
1769 | 379 | return ret | ||
1770 | 380 | |||
1771 | 351 | def get_revision(self, revision_id): | 381 | def get_revision(self, revision_id): |
1773 | 352 | assert type(revision_id) is str | 382 | if not type(revision_id) is str: |
1774 | 383 | raise errors.InvalidRevisionId(revision_id, self) | ||
1775 | 353 | hgrevid, mapping = self.lookup_bzr_revision_id(revision_id) | 384 | hgrevid, mapping = self.lookup_bzr_revision_id(revision_id) |
1776 | 385 | assert mapping is not None | ||
1777 | 354 | hgchange = self._hgrepo.changelog.read(hgrevid) | 386 | hgchange = self._hgrepo.changelog.read(hgrevid) |
1778 | 355 | hgparents = self._hgrepo.changelog.parents(hgrevid) | 387 | hgparents = self._hgrepo.changelog.parents(hgrevid) |
1779 | 356 | parent_ids = as_bzr_parents(hgparents, self.lookup_foreign_revision_id) | 388 | parent_ids = as_bzr_parents(hgparents, self.lookup_foreign_revision_id) |
1781 | 357 | return mapping.import_revision(revision_id, parent_ids, hgrevid, hgchange[0], hgchange[1].decode("utf-8"), hgchange[2], hgchange[4].decode("utf-8"), hgchange[5])[0] | 389 | return mapping.import_revision(revision_id, parent_ids, hgrevid, |
1782 | 390 | hgchange[0], hgchange[1].decode("utf-8"), hgchange[2], | ||
1783 | 391 | hgchange[4].decode("utf-8"), hgchange[5])[0] | ||
1784 | 358 | 392 | ||
1785 | 359 | def iter_inventories(self, revision_ids, ordering=None): | 393 | def iter_inventories(self, revision_ids, ordering=None): |
1786 | 360 | for revid in revision_ids: | 394 | for revid in revision_ids: |
1787 | @@ -365,6 +399,7 @@ | |||
1788 | 365 | log = self._hgrepo.changelog.read(hgid) | 399 | log = self._hgrepo.changelog.read(hgid) |
1789 | 366 | manifest = self._hgrepo.manifest.read(log[0]) | 400 | manifest = self._hgrepo.manifest.read(log[0]) |
1790 | 367 | all_relevant_revisions = self.get_ancestry(revision_id)[1:] + [NULL_REVISION] | 401 | all_relevant_revisions = self.get_ancestry(revision_id)[1:] + [NULL_REVISION] |
1791 | 402 | |||
1792 | 368 | pb = ui.ui_factory.nested_progress_bar() | 403 | pb = ui.ui_factory.nested_progress_bar() |
1793 | 369 | try: | 404 | try: |
1794 | 370 | inv = manifest_to_inventory(self._hgrepo, hgid, log, manifest, | 405 | inv = manifest_to_inventory(self._hgrepo, hgid, log, manifest, |
1795 | @@ -379,11 +414,18 @@ | |||
1796 | 379 | return foreign_revid in self._hgrepo.changelog.nodemap | 414 | return foreign_revid in self._hgrepo.changelog.nodemap |
1797 | 380 | 415 | ||
1798 | 381 | def has_revision(self, revision_id): | 416 | def has_revision(self, revision_id): |
1799 | 417 | if revision_id == NULL_REVISION: | ||
1800 | 418 | return True | ||
1801 | 382 | try: | 419 | try: |
1802 | 383 | return self.has_foreign_revision(self.lookup_bzr_revision_id(revision_id)[0]) | 420 | return self.has_foreign_revision(self.lookup_bzr_revision_id(revision_id)[0]) |
1803 | 384 | except errors.NoSuchRevision: | 421 | except errors.NoSuchRevision: |
1804 | 385 | return False | 422 | return False |
1805 | 386 | 423 | ||
1806 | 424 | def get_commit_builder(self, branch, parents, config, timestamp=None, | ||
1807 | 425 | timezone=None, committer=None, revprops=None, | ||
1808 | 426 | revision_id=None): | ||
1809 | 427 | raise NotImplementedError(self.get_commit_builder) | ||
1810 | 428 | |||
1811 | 387 | 429 | ||
1812 | 388 | class HgRemoteRepository(HgRepository): | 430 | class HgRemoteRepository(HgRepository): |
1813 | 389 | 431 | ||
1814 | 390 | 432 | ||
1815 | === modified file 'setup.py' | |||
1816 | --- setup.py 2010-03-25 21:08:21 +0000 | |||
1817 | +++ setup.py 2011-03-23 21:41:04 +0000 | |||
1818 | @@ -1,4 +1,4 @@ | |||
1820 | 1 | #!/usr/bin/env python2.4 | 1 | #!/usr/bin/env python |
1821 | 2 | 2 | ||
1822 | 3 | from info import * | 3 | from info import * |
1823 | 4 | 4 | ||
1824 | 5 | 5 | ||
1825 | === modified file 'tests/__init__.py' | |||
1826 | --- tests/__init__.py 2010-07-31 12:16:16 +0000 | |||
1827 | +++ tests/__init__.py 2011-03-23 21:41:04 +0000 | |||
1828 | @@ -30,6 +30,7 @@ | |||
1829 | 30 | 'test_mapping', | 30 | 'test_mapping', |
1830 | 31 | 'test_parsers', | 31 | 'test_parsers', |
1831 | 32 | 'test_pull', | 32 | 'test_pull', |
1832 | 33 | 'test_fetch', | ||
1833 | 33 | ] | 34 | ] |
1834 | 34 | 35 | ||
1835 | 35 | suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names])) | 36 | suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names])) |
1836 | 36 | 37 | ||
1837 | === modified file 'tests/test_branch.py' | |||
1838 | --- tests/test_branch.py 2009-10-12 09:41:47 +0000 | |||
1839 | +++ tests/test_branch.py 2011-03-23 21:41:04 +0000 | |||
1840 | @@ -18,8 +18,8 @@ | |||
1841 | 18 | TestCase, | 18 | TestCase, |
1842 | 19 | ) | 19 | ) |
1843 | 20 | 20 | ||
1846 | 21 | from bzrlib.plugins.hg import ( | 21 | from bzrlib.plugins.hg.dir import ( |
1847 | 22 | HgBzrDirFormat, | 22 | HgControlDirFormat, |
1848 | 23 | ) | 23 | ) |
1849 | 24 | from bzrlib.plugins.hg.branch import ( | 24 | from bzrlib.plugins.hg.branch import ( |
1850 | 25 | HgBranchFormat, | 25 | HgBranchFormat, |
1851 | @@ -28,13 +28,13 @@ | |||
1852 | 28 | class BranchFormatTests(TestCase): | 28 | class BranchFormatTests(TestCase): |
1853 | 29 | 29 | ||
1854 | 30 | def test_description(self): | 30 | def test_description(self): |
1856 | 31 | self.assertEquals("Mercurial Branch", | 31 | self.assertEquals("Mercurial Branch", |
1857 | 32 | HgBranchFormat().get_format_description()) | 32 | HgBranchFormat().get_format_description()) |
1858 | 33 | 33 | ||
1859 | 34 | 34 | ||
1860 | 35 | class ForeignTestsBranchFactory(object): | 35 | class ForeignTestsBranchFactory(object): |
1861 | 36 | 36 | ||
1862 | 37 | def make_empty_branch(self, transport): | 37 | def make_empty_branch(self, transport): |
1864 | 38 | return HgBzrDirFormat().initialize_on_transport(transport).open_branch() | 38 | return HgControlDirFormat().initialize_on_transport(transport).open_branch() |
1865 | 39 | 39 | ||
1866 | 40 | make_branch = make_empty_branch | 40 | make_branch = make_empty_branch |
1867 | 41 | 41 | ||
1868 | === modified file 'tests/test_dir.py' | |||
1869 | --- tests/test_dir.py 2010-07-31 12:25:11 +0000 | |||
1870 | +++ tests/test_dir.py 2011-03-23 21:41:04 +0000 | |||
1871 | @@ -22,21 +22,28 @@ | |||
1872 | 22 | TestCase, | 22 | TestCase, |
1873 | 23 | ) | 23 | ) |
1874 | 24 | 24 | ||
1877 | 25 | from bzrlib.plugins.hg import ( | 25 | from bzrlib.plugins.hg.dir import ( |
1878 | 26 | HgBzrDirFormat, | 26 | HgControlDirFormat, |
1879 | 27 | ) | 27 | ) |
1880 | 28 | 28 | ||
1881 | 29 | 29 | ||
1883 | 30 | class HgBzrDirFormatTests(TestCase): | 30 | class HgControlDirFormatTests(TestCase): |
1884 | 31 | 31 | ||
1885 | 32 | def test_eq(self): | 32 | def test_eq(self): |
1888 | 33 | format1 = HgBzrDirFormat() | 33 | format1 = HgControlDirFormat() |
1889 | 34 | format2 = HgBzrDirFormat() | 34 | format2 = HgControlDirFormat() |
1890 | 35 | self.assertEquals(format1, format2) | 35 | self.assertEquals(format1, format2) |
1891 | 36 | self.assertEquals(format1, format1) | 36 | self.assertEquals(format1, format1) |
1892 | 37 | bzr_format = format_registry.make_bzrdir("default") | 37 | bzr_format = format_registry.make_bzrdir("default") |
1893 | 38 | self.assertNotEquals(bzr_format, format1) | 38 | self.assertNotEquals(bzr_format, format1) |
1894 | 39 | 39 | ||
1895 | 40 | def test_hash(self): | ||
1896 | 41 | format1 = HgControlDirFormat() | ||
1897 | 42 | format2 = HgControlDirFormat() | ||
1898 | 43 | self.assertEquals(hash(format1), hash(format2)) | ||
1899 | 44 | bzr_format = format_registry.make_bzrdir("default") | ||
1900 | 45 | self.assertNotEquals(hash(bzr_format), hash(format1)) | ||
1901 | 46 | |||
1902 | 40 | def test_network_name(self): | 47 | def test_network_name(self): |
1904 | 41 | format = HgBzrDirFormat() | 48 | format = HgControlDirFormat() |
1905 | 42 | self.assertEquals("hg", format.network_name()) | 49 | self.assertEquals("hg", format.network_name()) |
1906 | 43 | 50 | ||
1907 | === added file 'tests/test_fetch.py' | |||
1908 | --- tests/test_fetch.py 1970-01-01 00:00:00 +0000 | |||
1909 | +++ tests/test_fetch.py 2011-03-23 21:41:04 +0000 | |||
1910 | @@ -0,0 +1,53 @@ | |||
1911 | 1 | # Copyright (C) 2010 Leonid Borisenko <leo.borisenko@gmail.com> | ||
1912 | 2 | |||
1913 | 3 | # This program is free software; you can redistribute it and/or modify | ||
1914 | 4 | # it under the terms of the GNU General Public License as published by | ||
1915 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
1916 | 6 | # (at your option) any later version. | ||
1917 | 7 | |||
1918 | 8 | # This program is distributed in the hope that it will be useful, | ||
1919 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1920 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1921 | 11 | # GNU General Public License for more details. | ||
1922 | 12 | |||
1923 | 13 | # You should have received a copy of the GNU General Public License | ||
1924 | 14 | # along with this program; if not, write to the Free Software | ||
1925 | 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
1926 | 16 | |||
1927 | 17 | """Tests for fetching from Mercurial into Bazaar.""" | ||
1928 | 18 | |||
1929 | 19 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
1930 | 20 | from bzrlib.plugins.hg.ui import ui as hgui | ||
1931 | 21 | from bzrlib.tests import TestCaseWithTransport | ||
1932 | 22 | |||
1933 | 23 | import mercurial.localrepo | ||
1934 | 24 | |||
1935 | 25 | class TestFetching(TestCaseWithTransport): | ||
1936 | 26 | |||
1937 | 27 | def test_recursive_removing_of_empty_directories(self): | ||
1938 | 28 | # Create files in directory of Mercurial repository. | ||
1939 | 29 | self.build_tree([ | ||
1940 | 30 | "hg/", | ||
1941 | 31 | "hg/f1", | ||
1942 | 32 | "hg/d1/", | ||
1943 | 33 | "hg/d1/d2/", | ||
1944 | 34 | "hg/d1/d2/f2", | ||
1945 | 35 | ]) | ||
1946 | 36 | |||
1947 | 37 | # Create Mercurial repository itself and fill it's history. | ||
1948 | 38 | hgrepo = mercurial.localrepo.localrepository(hgui(), "hg", create=True) | ||
1949 | 39 | hgrepo[None].add(["f1", "d1/d2/f2"]) | ||
1950 | 40 | hgrepo.commit("Initial commit") | ||
1951 | 41 | hgrepo[None].remove(["d1/d2/f2"], unlink=True) | ||
1952 | 42 | hgrepo.commit("Remove file f2, so parent directories d2, d1 are empty") | ||
1953 | 43 | |||
1954 | 44 | # Import history from Mercurial repository into Bazaar repository. | ||
1955 | 45 | bzrtree = self.make_branch_and_tree('bzr') | ||
1956 | 46 | hgdir = HgControlDirFormat().open(self.get_transport('hg')) | ||
1957 | 47 | bzrtree.pull(hgdir.open_branch()) | ||
1958 | 48 | |||
1959 | 49 | # As file f2 was deleted, directories d1 and d2 should not exists. | ||
1960 | 50 | self.failIfExists('bzr/d1') | ||
1961 | 51 | |||
1962 | 52 | # Self-assurance check that history was really imported. | ||
1963 | 53 | self.failUnlessExists('bzr/f1') | ||
1964 | 0 | 54 | ||
1965 | === modified file 'tests/test_mapping.py' | |||
1966 | --- tests/test_mapping.py 2010-02-08 13:32:23 +0000 | |||
1967 | +++ tests/test_mapping.py 2011-03-23 21:41:04 +0000 | |||
1968 | @@ -25,6 +25,8 @@ | |||
1969 | 25 | from bzrlib.plugins.hg.mapping import ( | 25 | from bzrlib.plugins.hg.mapping import ( |
1970 | 26 | ExperimentalHgMapping, | 26 | ExperimentalHgMapping, |
1971 | 27 | HgMappingv1, | 27 | HgMappingv1, |
1972 | 28 | convert_converted_from, | ||
1973 | 29 | generate_convert_revision, | ||
1974 | 28 | as_bzr_parents, | 30 | as_bzr_parents, |
1975 | 29 | as_hg_parents, | 31 | as_hg_parents, |
1976 | 30 | escape_path, | 32 | escape_path, |
1977 | @@ -41,7 +43,7 @@ | |||
1978 | 41 | from bzrlib.tests import ( | 43 | from bzrlib.tests import ( |
1979 | 42 | TestCase, | 44 | TestCase, |
1980 | 43 | ) | 45 | ) |
1982 | 44 | 46 | ||
1983 | 45 | class HgMappingTests(object): | 47 | class HgMappingTests(object): |
1984 | 46 | 48 | ||
1985 | 47 | def test_revid_foreign_to_bzr(self): | 49 | def test_revid_foreign_to_bzr(self): |
1986 | @@ -168,3 +170,33 @@ | |||
1987 | 168 | m = {"a" * 20: "reva"} | 170 | m = {"a" * 20: "reva"} |
1988 | 169 | self.assertEquals(("reva",), | 171 | self.assertEquals(("reva",), |
1989 | 170 | as_bzr_parents(("a" * 20, nullid), m.__getitem__)) | 172 | as_bzr_parents(("a" * 20, nullid), m.__getitem__)) |
1990 | 173 | |||
1991 | 174 | |||
1992 | 175 | class ConvertConvertedFromTests(TestCase): | ||
1993 | 176 | |||
1994 | 177 | def test_unspecified(self): | ||
1995 | 178 | self.assertEquals("unspecified foo\n", | ||
1996 | 179 | convert_converted_from("foo")) | ||
1997 | 180 | |||
1998 | 181 | def test_svn(self): | ||
1999 | 182 | self.assertEquals("svn myuuid:42:path\n", | ||
2000 | 183 | convert_converted_from("svn:myuuid/path@42")) | ||
2001 | 184 | |||
2002 | 185 | def test_unknown(self): | ||
2003 | 186 | self.assertRaises(KeyError, | ||
2004 | 187 | convert_converted_from, "unknown:myuuid:42:path") | ||
2005 | 188 | |||
2006 | 189 | |||
2007 | 190 | class GenerateConvertRevisionTests(TestCase): | ||
2008 | 191 | |||
2009 | 192 | def test_generate_convert_revision_svn(self): | ||
2010 | 193 | self.assertEquals("svn:myuuid/path@42", | ||
2011 | 194 | generate_convert_revision("svn myuuid:42:path")) | ||
2012 | 195 | |||
2013 | 196 | def test_generate_convert_revision_unspecified(self): | ||
2014 | 197 | self.assertEquals("bla", | ||
2015 | 198 | generate_convert_revision("unspecified bla")) | ||
2016 | 199 | |||
2017 | 200 | def test_generate_convert_revision_unknown(self): | ||
2018 | 201 | self.assertRaises(KeyError, | ||
2019 | 202 | generate_convert_revision, "bla bla") | ||
2020 | 171 | 203 | ||
2021 | === modified file 'tests/test_parsers.py' | |||
2022 | --- tests/test_parsers.py 2010-02-08 13:32:23 +0000 | |||
2023 | +++ tests/test_parsers.py 2011-03-23 21:41:04 +0000 | |||
2024 | @@ -17,13 +17,15 @@ | |||
2025 | 17 | import mercurial | 17 | import mercurial |
2026 | 18 | 18 | ||
2027 | 19 | from bzrlib.plugins.hg.parsers import ( | 19 | from bzrlib.plugins.hg.parsers import ( |
2028 | 20 | deserialize_file_text, | ||
2029 | 20 | format_changeset, | 21 | format_changeset, |
2030 | 21 | parse_changeset, | 22 | parse_changeset, |
2031 | 23 | serialize_file_text, | ||
2032 | 22 | ) | 24 | ) |
2033 | 23 | from bzrlib.tests import ( | 25 | from bzrlib.tests import ( |
2034 | 24 | TestCase, | 26 | TestCase, |
2035 | 25 | ) | 27 | ) |
2037 | 26 | 28 | ||
2038 | 27 | class ChangesetFormatterTests(TestCase): | 29 | class ChangesetFormatterTests(TestCase): |
2039 | 28 | 30 | ||
2040 | 29 | def test_simple(self): | 31 | def test_simple(self): |
2041 | @@ -116,3 +118,12 @@ | |||
2042 | 116 | Some | 118 | Some |
2043 | 117 | commit | 119 | commit |
2044 | 118 | message""")) | 120 | message""")) |
2045 | 121 | |||
2046 | 122 | |||
2047 | 123 | class TextSerializers(TestCase): | ||
2048 | 124 | |||
2049 | 125 | def test_serialize(self): | ||
2050 | 126 | self.assertEquals("\1\ncopy: bla\n\1\nfoo\n", serialize_file_text({"copy": "bla"}, "foo\n")) | ||
2051 | 127 | |||
2052 | 128 | def test_deserialize(self): | ||
2053 | 129 | self.assertEquals(({"copy": "bla"}, "foo\n"), deserialize_file_text("\1\ncopy: bla\n\1\nfoo\n")) | ||
2054 | 119 | 130 | ||
2055 | === modified file 'tests/test_pull.py' | |||
2056 | --- tests/test_pull.py 2009-10-18 21:57:36 +0000 | |||
2057 | +++ tests/test_pull.py 2011-03-23 21:41:04 +0000 | |||
2058 | @@ -15,7 +15,7 @@ | |||
2059 | 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
2060 | 16 | 16 | ||
2061 | 17 | from bzrlib.inventory import Inventory | 17 | from bzrlib.inventory import Inventory |
2063 | 18 | from bzrlib.plugins.hg import HgBzrDirFormat | 18 | from bzrlib.plugins.hg.dir import HgControlDirFormat |
2064 | 19 | from bzrlib.tests import TestCaseWithTransport | 19 | from bzrlib.tests import TestCaseWithTransport |
2065 | 20 | 20 | ||
2066 | 21 | import base64 | 21 | import base64 |
2067 | @@ -30,7 +30,7 @@ | |||
2068 | 30 | def setUp(self): | 30 | def setUp(self): |
2069 | 31 | super(TestPulling, self).setUp() | 31 | super(TestPulling, self).setUp() |
2070 | 32 | self.build_tree(['hg/', 'hg/a', 'hg/b', 'hg/dir/', 'hg/dir/c']) | 32 | self.build_tree(['hg/', 'hg/a', 'hg/b', 'hg/dir/', 'hg/dir/c']) |
2072 | 33 | hgdir = HgBzrDirFormat().initialize('hg') | 33 | hgdir = HgControlDirFormat().initialize('hg') |
2073 | 34 | self.tree = hgdir.open_workingtree() | 34 | self.tree = hgdir.open_workingtree() |
2074 | 35 | mode = os.lstat('hg/b').st_mode | 35 | mode = os.lstat('hg/b').st_mode |
2075 | 36 | os.chmod('hg/b', mode | stat.S_IEXEC) | 36 | os.chmod('hg/b', mode | stat.S_IEXEC) |
2076 | @@ -53,7 +53,7 @@ | |||
2077 | 53 | file_id='hg:dir') | 53 | file_id='hg:dir') |
2078 | 54 | entry.revision = tip | 54 | entry.revision = tip |
2079 | 55 | entry = revone_inventory.add_path('dir/c', kind='file', | 55 | entry = revone_inventory.add_path('dir/c', kind='file', |
2081 | 56 | file_id='hg:dir:c') | 56 | file_id='hg:dir_sc') |
2082 | 57 | entry.revision = tip | 57 | entry.revision = tip |
2083 | 58 | entry.text_size = len('contents of hg/dir/c\n') | 58 | entry.text_size = len('contents of hg/dir/c\n') |
2084 | 59 | entry.text_sha1 = "958be752affac0fee70471331b96fb3fc1809425" | 59 | entry.text_sha1 = "958be752affac0fee70471331b96fb3fc1809425" |
2085 | @@ -68,7 +68,7 @@ | |||
2086 | 68 | self.revtwo_inventory = copy.deepcopy(revone_inventory) | 68 | self.revtwo_inventory = copy.deepcopy(revone_inventory) |
2087 | 69 | tip = self.tree.last_revision() | 69 | tip = self.tree.last_revision() |
2088 | 70 | entry = self.revtwo_inventory.add_path('dir/d', kind='file', | 70 | entry = self.revtwo_inventory.add_path('dir/d', kind='file', |
2090 | 71 | file_id='hg:dir:d') | 71 | file_id='hg:dir_sd') |
2091 | 72 | entry.revision = tip | 72 | entry.revision = tip |
2092 | 73 | entry.text_size = len('contents of hg/dir/d\n') | 73 | entry.text_size = len('contents of hg/dir/d\n') |
2093 | 74 | entry.text_sha1 = "f48fc342f707bfb4711790e1813c0df4d44e1a23" | 74 | entry.text_sha1 = "f48fc342f707bfb4711790e1813c0df4d44e1a23" |
2094 | @@ -91,7 +91,7 @@ | |||
2095 | 91 | self.tree.commit('change dir/c') | 91 | self.tree.commit('change dir/c') |
2096 | 92 | self.revfour_inventory = copy.deepcopy(self.revthree_inventory) | 92 | self.revfour_inventory = copy.deepcopy(self.revthree_inventory) |
2097 | 93 | tip = self.tree.last_revision() | 93 | tip = self.tree.last_revision() |
2099 | 94 | entry = self.revfour_inventory['hg:dir:c'] | 94 | entry = self.revfour_inventory['hg:dir_sc'] |
2100 | 95 | entry.revision = tip | 95 | entry.revision = tip |
2101 | 96 | entry.text_size = len('new contents') | 96 | entry.text_size = len('new contents') |
2102 | 97 | entry.text_sha1 = "7ffa72b76d5d66da37f4b614b7a822c01f23c183" | 97 | entry.text_sha1 = "7ffa72b76d5d66da37f4b614b7a822c01f23c183" |
2103 | 98 | 98 | ||
2104 | === modified file 'tests/test_repository.py' | |||
2105 | --- tests/test_repository.py 2009-11-12 22:57:40 +0000 | |||
2106 | +++ tests/test_repository.py 2011-03-23 21:41:04 +0000 | |||
2107 | @@ -15,11 +15,11 @@ | |||
2108 | 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
2109 | 16 | 16 | ||
2110 | 17 | 17 | ||
2113 | 18 | from bzrlib.plugins.hg import ( | 18 | from bzrlib.plugins.hg.dir import ( |
2114 | 19 | HgBzrDirFormat, | 19 | HgControlDirFormat, |
2115 | 20 | ) | 20 | ) |
2116 | 21 | 21 | ||
2117 | 22 | class ForeignTestsRepositoryFactory(object): | 22 | class ForeignTestsRepositoryFactory(object): |
2118 | 23 | 23 | ||
2119 | 24 | def make_repository(self, transport): | 24 | def make_repository(self, transport): |
2121 | 25 | return HgBzrDirFormat().initialize_on_transport(transport).open_repository() | 25 | return HgControlDirFormat().initialize_on_transport(transport).open_repository() |
2122 | 26 | 26 | ||
2123 | === modified file 'ui.py' | |||
2124 | --- ui.py 2009-09-29 14:41:23 +0000 | |||
2125 | +++ ui.py 2011-03-23 21:41:04 +0000 | |||
2126 | @@ -27,19 +27,19 @@ | |||
2127 | 27 | 27 | ||
2128 | 28 | def debug(self, *msg): | 28 | def debug(self, *msg): |
2129 | 29 | for x in msg: | 29 | for x in msg: |
2131 | 30 | trace.mutter("hg: %s" % x) | 30 | trace.mutter("hg: %s" % x.rstrip()) |
2132 | 31 | 31 | ||
2133 | 32 | def warn(self, *msg): | 32 | def warn(self, *msg): |
2134 | 33 | for x in msg: | 33 | for x in msg: |
2136 | 34 | trace.warning("hg: %s" % x) | 34 | trace.warning("hg: %s" % x.rstrip()) |
2137 | 35 | 35 | ||
2138 | 36 | def note(self, *msg): | 36 | def note(self, *msg): |
2139 | 37 | for x in msg: | 37 | for x in msg: |
2141 | 38 | trace.mutter("hg: %s" % x) | 38 | trace.mutter("hg: %s" % x.rstrip()) |
2142 | 39 | 39 | ||
2143 | 40 | def status(self, *msg): | 40 | def status(self, *msg): |
2144 | 41 | for x in msg: | 41 | for x in msg: |
2146 | 42 | trace.mutter("hg: %s" % x) | 42 | trace.mutter("hg: %s" % x.rstrip()) |
2147 | 43 | 43 | ||
2148 | 44 | def username(self): | 44 | def username(self): |
2149 | 45 | return config.GlobalConfig().username() | 45 | return config.GlobalConfig().username() |
2150 | 46 | 46 | ||
2151 | === modified file 'util.py' | |||
2152 | --- util.py 2009-10-15 17:27:10 +0000 | |||
2153 | +++ util.py 2011-03-23 21:41:04 +0000 | |||
2154 | @@ -1,4 +1,4 @@ | |||
2156 | 1 | # Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org> | 1 | # Copyright (C) 2009-2010 Jelmer Vernooij <jelmer@samba.org> |
2157 | 2 | # | 2 | # |
2158 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
2159 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
2160 | 5 | 5 | ||
2161 | === modified file 'versionedfiles.py' | |||
2162 | --- versionedfiles.py 2009-12-05 01:58:37 +0000 | |||
2163 | +++ versionedfiles.py 2011-03-23 21:41:04 +0000 | |||
2164 | @@ -75,6 +75,8 @@ | |||
2165 | 75 | ret[(revid, )] = tuple((x,) for x in as_bzr_parents(self._revlog.parents(hg_ref), self._reverse_lookup_id)) | 75 | ret[(revid, )] = tuple((x,) for x in as_bzr_parents(self._revlog.parents(hg_ref), self._reverse_lookup_id)) |
2166 | 76 | except LookupError: | 76 | except LookupError: |
2167 | 77 | ret[(revid, )] = None | 77 | ret[(revid, )] = None |
2168 | 78 | if ret[(revid,)] == (): | ||
2169 | 79 | ret[(revid,)] = (NULL_REVISION,) | ||
2170 | 78 | return ret | 80 | return ret |
2171 | 79 | 81 | ||
2172 | 80 | def keys(self): | 82 | def keys(self): |
2173 | 81 | 83 | ||
2174 | === modified file 'workingtree.py' | |||
2175 | --- workingtree.py 2010-08-05 23:55:52 +0000 | |||
2176 | +++ workingtree.py 2011-03-23 21:41:04 +0000 | |||
2177 | @@ -16,6 +16,11 @@ | |||
2178 | 16 | 16 | ||
2179 | 17 | """Mercurial working tree support.""" | 17 | """Mercurial working tree support.""" |
2180 | 18 | 18 | ||
2181 | 19 | from bzrlib import osutils | ||
2182 | 20 | |||
2183 | 21 | from bzrlib.errors import ( | ||
2184 | 22 | IncompatibleFormat, | ||
2185 | 23 | ) | ||
2186 | 19 | from bzrlib.inventory import ( | 24 | from bzrlib.inventory import ( |
2187 | 20 | Inventory, | 25 | Inventory, |
2188 | 21 | ) | 26 | ) |
2189 | @@ -38,10 +43,21 @@ | |||
2190 | 38 | support the working tree format. | 43 | support the working tree format. |
2191 | 39 | """ | 44 | """ |
2192 | 40 | 45 | ||
2193 | 46 | @property | ||
2194 | 47 | def _matchingbzrdir(self): | ||
2195 | 48 | from bzrlib.plugins.hg.dir import HgControlDirFormat | ||
2196 | 49 | return HgControlDirFormat() | ||
2197 | 50 | |||
2198 | 41 | def get_format_description(self): | 51 | def get_format_description(self): |
2199 | 42 | """See WorkingTreeFormat.get_format_description().""" | 52 | """See WorkingTreeFormat.get_format_description().""" |
2200 | 43 | return "Mercurial Working Tree" | 53 | return "Mercurial Working Tree" |
2201 | 44 | 54 | ||
2202 | 55 | def initialize(self, to_bzrdir): | ||
2203 | 56 | from bzrlib.plugins.hg.dir import HgDir | ||
2204 | 57 | if not isinstance(to_bzrdir, HgDir): | ||
2205 | 58 | raise IncompatibleFormat(self, to_bzrdir._format) | ||
2206 | 59 | return to_bzrdir.create_workingtree() | ||
2207 | 60 | |||
2208 | 45 | 61 | ||
2209 | 46 | class HgWorkingTree(bzrlib.workingtree.WorkingTree): | 62 | class HgWorkingTree(bzrlib.workingtree.WorkingTree): |
2210 | 47 | """An adapter to mercurial repositories for bzr WorkingTree obejcts.""" | 63 | """An adapter to mercurial repositories for bzr WorkingTree obejcts.""" |
2211 | @@ -50,30 +66,44 @@ | |||
2212 | 50 | self._inventory = Inventory() | 66 | self._inventory = Inventory() |
2213 | 51 | self._hgrepo = hgrepo | 67 | self._hgrepo = hgrepo |
2214 | 52 | self.bzrdir = hgdir | 68 | self.bzrdir = hgdir |
2215 | 69 | self.repository = hgdir.open_repository() | ||
2216 | 53 | self._control_files = lockfiles | 70 | self._control_files = lockfiles |
2217 | 54 | self._branch = hgbranch | 71 | self._branch = hgbranch |
2218 | 55 | self._format = HgWorkingTreeFormat() | 72 | self._format = HgWorkingTreeFormat() |
2219 | 56 | self._transport = hgdir.get_workingtree_transport(None) | 73 | self._transport = hgdir.get_workingtree_transport(None) |
2220 | 57 | self.basedir = hgdir.root_transport.local_abspath(".") | 74 | self.basedir = hgdir.root_transport.local_abspath(".") |
2221 | 75 | self._detect_case_handling() | ||
2222 | 76 | self._rules_searcher = None | ||
2223 | 58 | self.views = self._make_views() | 77 | self.views = self._make_views() |
2224 | 59 | 78 | ||
2225 | 79 | def flush(self): | ||
2226 | 80 | pass | ||
2227 | 81 | |||
2228 | 60 | @needs_write_lock | 82 | @needs_write_lock |
2230 | 61 | def add(self, files, ids=None): | 83 | def add(self, files, ids=None, kinds=None): |
2231 | 62 | # hg does not use ids, toss them out | 84 | # hg does not use ids, toss them out |
2232 | 63 | if isinstance(files, basestring): | 85 | if isinstance(files, basestring): |
2233 | 64 | files = [files] | 86 | files = [files] |
2234 | 87 | if kinds is None: | ||
2235 | 88 | kinds = [osutils.file_kind(self.abspath(f)) for f in files] | ||
2236 | 89 | hg_files = [] | ||
2237 | 90 | for file, kind in zip(files, kinds): | ||
2238 | 91 | if kind == "directory": | ||
2239 | 92 | continue | ||
2240 | 93 | hg_files.append(file.encode('utf-8')) | ||
2241 | 94 | |||
2242 | 65 | # hg does not canonicalise paths : make them absolute | 95 | # hg does not canonicalise paths : make them absolute |
2245 | 66 | paths = [(file).encode('utf8') for file in files] | 96 | self._hgrepo[None].add(hg_files) |
2244 | 67 | self._hgrepo[None].add(paths) | ||
2246 | 68 | 97 | ||
2247 | 69 | @needs_write_lock | 98 | @needs_write_lock |
2249 | 70 | def commit(self, message, revprops=None, *args, **kwargs): | 99 | def commit(self, message=None, revprops=None, *args, **kwargs): |
2250 | 71 | # TODO: selected file lists -> match function | 100 | # TODO: selected file lists -> match function |
2251 | 72 | if revprops is None: | 101 | if revprops is None: |
2252 | 73 | extra = {} | 102 | extra = {} |
2253 | 74 | else: | 103 | else: |
2254 | 75 | extra = revprops | 104 | extra = revprops |
2256 | 76 | self._hgrepo.commit(message, extra=extra) | 105 | hgid = self._hgrepo.commit(message.encode("utf-8"), extra=extra, force=True) |
2257 | 106 | return self.bzrdir.open_repository().lookup_foreign_revision_id(hgid) | ||
2258 | 77 | 107 | ||
2259 | 78 | def _reset_data(self): | 108 | def _reset_data(self): |
2260 | 79 | """Reset all cached data.""" | 109 | """Reset all cached data.""" |