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