Merge lp:~jelmer/bzr-hg/sprout into lp:~launchpad-pqm/bzr-hg/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
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.

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
=== modified file '.bzrignore'
--- .bzrignore 2009-05-21 15:53:00 +0000
+++ .bzrignore 2011-03-23 21:41:04 +0000
@@ -1,3 +1,4 @@
1*.pyc1*.pyc
2build2build
3.plugins3.plugins
4.testrepository
45
=== added file '.testr.conf'
--- .testr.conf 1970-01-01 00:00:00 +0000
+++ .testr.conf 2011-03-23 21:41:04 +0000
@@ -0,0 +1,4 @@
1[DEFAULT]
2test_command=BZR_PLUGINS_AT=hg@`pwd` bzr selftest ^bzrlib.plugins.hg. Hg Mercurial --subunit $IDOPTION $LISTOPT
3test_id_option=--load-list $IDFILE
4test_list_option=--list
05
=== modified file 'HACKING'
--- HACKING 2009-10-10 01:17:00 +0000
+++ HACKING 2011-03-23 21:41:04 +0000
@@ -3,8 +3,7 @@
33
4Unit testing4Unit testing
5~~~~~~~~~~~~5~~~~~~~~~~~~
6To run the bzr-hg testsuite, simply run 6To run the bzr-hg testsuite, simply run 'bzr selftest -s bp.hg' or
7'bzr selftest --starting-with=bazaar.plugins.hg' or
8run 'make check' in the top-level bzr-hg directory.7run 'make check' in the top-level bzr-hg directory.
98
10Unavailable bzrlib API's9Unavailable bzrlib API's
@@ -24,4 +23,3 @@
24Coding Style, etc23Coding Style, etc
25~~~~~~~~~~~~~~~~~24~~~~~~~~~~~~~~~~~
26Please refer to HACKING in the Bazaar source distribution.25Please refer to HACKING in the Bazaar source distribution.
27
2826
=== modified file 'Makefile'
--- Makefile 2010-07-28 16:15:17 +0000
+++ Makefile 2011-03-23 21:41:04 +0000
@@ -6,7 +6,7 @@
6CTAGS ?= ctags6CTAGS ?= ctags
7PYLINT ?= pylint7PYLINT ?= pylint
8RST2HTML ?= rst2html8RST2HTML ?= rst2html
9TESTS ?= bzrlib.tests.per_foreign_vcs.*Hg bzrlib.plugins.hg9TESTS ?= -s bt.per_foreign_vcs.*Hg -s bp.hg
1010
11all:: build 11all:: build
1212
1313
=== modified file 'NEWS'
--- NEWS 2010-08-14 00:41:20 +0000
+++ NEWS 2011-03-23 21:41:04 +0000
@@ -27,9 +27,14 @@
27 * Put idmap information in a bzr-hg-specific directory. (Jelmer Vernooij,27 * Put idmap information in a bzr-hg-specific directory. (Jelmer Vernooij,
28 #599397)28 #599397)
2929
30 * Add basic support for tags. (Jelmer Vernooij)
31
32 * Check for Mercurial repository before checking for Bazaar repository.
33 (Jelmer Vernooij, #674581, #688455)
34
30 BUG FIXES35 BUG FIXES
3136
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)
3338
34 * Check for Mercurial version before attempting to import it.39 * Check for Mercurial version before attempting to import it.
35 (#432100, Jelmer Vernooij)40 (#432100, Jelmer Vernooij)
@@ -38,7 +43,7 @@
3843
39 * Remove empty directories during fetch. (Jelmer Vernooij)44 * Remove empty directories during fetch. (Jelmer Vernooij)
4045
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.
42 (#486899, Jelmer Vernooij)47 (#486899, Jelmer Vernooij)
4348
44 * Don't warn about development versions of Mercurial. (#492292, Jelmer 49 * Don't warn about development versions of Mercurial. (#492292, Jelmer
@@ -59,6 +64,22 @@
59 * Don't allow probing for Mercurial repositories over HTTP to break 64 * Don't allow probing for Mercurial repositories over HTTP to break
60 bzr. (Jelmer Vernooij)65 bzr. (Jelmer Vernooij)
6166
67 * Fix recursive removing of parent empty directories. (#691994,
68 Leonid Borisenko)
69
70 * WorkingTree.commit()'s message argument is now optional, consistent
71 with bzr.dev. (#692902, Jelmer Vernooij)
72
73 * Fix handling of unknown strings in converted_from extra.
74 (#670870, Jelmer Vernooij)
75
76 * Fix handling of first revision without changes. (Jelmer Vernooij, #688459)
77
78 * Support stop_revision in Branch.pull(). (Jelmer Vernooij, #544701)
79
80 * Provide custom ControlDir.sprout() implementation. Required for
81 compatibility with bzr 2.4. (Jelmer Vernooij, #717937)
82
62 DOCUMENTATION83 DOCUMENTATION
6384
64 * Add some basic instructions in INSTALL. (Martin Pool)85 * Add some basic instructions in INSTALL. (Martin Pool)
6586
=== modified file '__init__.py'
--- __init__.py 2010-08-06 10:51:08 +0000
+++ __init__.py 2011-03-23 21:41:04 +0000
@@ -25,13 +25,12 @@
2525
26from info import (26from info import (
27 bzr_compatible_versions,27 bzr_compatible_versions,
28 hg_compatible_versions,28 hg_compatible_version_strings,
29 bzr_plugin_version as version_info,29 bzr_plugin_version as version_info,
30 )30 )
3131
32bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions)32bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions)
3333
34import bzrlib.bzrdir
35from bzrlib import (34from bzrlib import (
36 errors,35 errors,
37 trace,36 trace,
@@ -39,13 +38,17 @@
39from bzrlib.foreign import (38from bzrlib.foreign import (
40 foreign_vcs_registry,39 foreign_vcs_registry,
41 )40 )
42import bzrlib.lockable_files
43from bzrlib.send import (41from bzrlib.send import (
44 format_registry as send_format_registry,42 format_registry as send_format_registry,
45 )43 )
4644
45from bzrlib.controldir import (
46 network_format_registry as controldir_network_format_registry,
47 ControlDirFormat,
48 Prober,
49 )
50
47_mercurial_loaded = False51_mercurial_loaded = False
48LockWarner = bzrlib.lockable_files._LockWarner
4952
50def lazy_load_mercurial():53def lazy_load_mercurial():
51 global _mercurial_loaded54 global _mercurial_loaded
@@ -61,7 +64,7 @@
61 from mercurial.__version__ import version as hg_version64 from mercurial.__version__ import version as hg_version
62 if hg_version != "unknown":65 if hg_version != "unknown":
63 hg_major_version = ".".join(hg_version.split(".")[:2])66 hg_major_version = ".".join(hg_version.split(".")[:2])
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:
65 raise errors.DependencyNotPresent("mercurial",68 raise errors.DependencyNotPresent("mercurial",
66 'bzr-hg: Mercurial version %s not supported.' % hg_version)69 'bzr-hg: Mercurial version %s not supported.' % hg_version)
67 trace.mutter("bzr-hg: using Mercurial %s" % hg_version)70 trace.mutter("bzr-hg: using Mercurial %s" % hg_version)
@@ -70,266 +73,79 @@
70foreign_vcs_registry.register_lazy("hg",73foreign_vcs_registry.register_lazy("hg",
71 "bzrlib.plugins.hg.mapping", "foreign_hg", "Mercurial")74 "bzrlib.plugins.hg.mapping", "foreign_hg", "Mercurial")
7275
73class HgDummyLock(object):76def has_hg_http_smart_server(transport, external_url):
74 """Lock that doesn't actually lock."""77 if not external_url.startswith("http:") and not external_url.startswith("https:"):
7578 return False
76 def __init__(self, hgrepo):79 url = external_url + "?pairs=%s-%s&cmd=between" % ("0" * 40, "0" * 40)
77 self._hgrepo = hgrepo80 from bzrlib.transport.http._urllib import HttpTransport_urllib, Request
7881 if isinstance(transport, HttpTransport_urllib):
79 def lock_write(self, token=None):82 req = Request('GET', url, accepted_errors=[200, 403, 404, 405])
80 if token is not None:83 req.follow_redirections = True
81 raise errors.TokenLockingNotSupported(self)84 resp = transport._perform(req)
82 self._lock = self._hgrepo.wlock()85 if resp.code == 404:
8386 return False
84 def lock_read(self):87 headers = resp.headers
85 self._lock = None88 else:
8689 try:
87 def peek(self):90 from bzrlib.transport.http._pycurl import PyCurlTransport
88 raise NotImplementedError(self.peek)91 except errors.DependencyNotPresent:
8992 return False
90 def unlock(self):93 else:
91 if self._lock is not None:94 import pycurl
92 self._lock.release()95 from cStringIO import StringIO
9396 if isinstance(transport, PyCurlTransport):
94 def validate_token(self, token):97 conn = transport._get_curl()
95 if token is not None:98 conn.setopt(pycurl.URL, url)
96 raise errors.TokenLockingNotSupported(self)99 transport._set_curl_options(conn)
97100 conn.setopt(pycurl.HTTPGET, 1)
98101 conn.setopt(pycurl.NOBODY, 1)
99class HgLock(object):102 header = StringIO()
100 """A lock that thunks through to Hg."""103 data = StringIO()
101104 conn.setopt(pycurl.HEADERFUNCTION, header.write)
102 def __init__(self, hgrepo):105 conn.setopt(pycurl.WRITEFUNCTION, data.write)
103 self._hgrepo = hgrepo106 transport._curl_perform(conn, header)
104107 code = conn.getinfo(pycurl.HTTP_CODE)
105 def lock_write(self, token=None):108 if code == 404:
106 if token is not None:109 raise errors.NoSuchFile(transport._path)
107 raise errors.TokenLockingNotSupported(self)110 headers = transport._parse_headers(header)
108 self._lock = self._hgrepo.wlock()111 else:
109112 return False
110 def lock_read(self):113 ct = headers.getheader("Content-Type")
111 self._lock = self._hgrepo.lock()114 return ct.startswith("application/mercurial")
112115
113 def peek(self):116
114 raise NotImplementedError(self.peek)117def has_hg_dumb_repository(transport):
115118 try:
116 def unlock(self):119 return transport.has(".hg/requires")
117 self._lock.release()120 except (errors.NoSuchFile, errors.PermissionDenied):
118121 return False
119 def validate_token(self, token):122
120 if token is not None:123
121 raise errors.TokenLockingNotSupported(self)124class HgProber(Prober):
122125
123 def break_lock(self):126 # Perhaps retrieve list from mercurial.hg.schemes ?
124 pass127 _supported_schemes = ["http", "https", "file", "ssh"]
125128
126129 def probe_transport(self, transport):
127class HgLockableFiles(bzrlib.lockable_files.LockableFiles):130 try:
128 """Hg specific lockable files abstraction."""131 external_url = transport.external_url()
129132 except errors.InProcessTransport:
130 def __init__(self, lock, transport):133 raise errors.NotBranchError(path=transport.base)
131 self._lock = lock134 scheme = external_url.split(":")[0]
132 self._transaction = None135 if scheme not in self._supported_schemes:
133 self._lock_mode = None136 raise errors.NotBranchError(path=transport.base)
134 self._transport = transport137 if (not has_hg_dumb_repository(transport) and
135 self._lock_warner = LockWarner(repr(self))138 not has_hg_http_smart_server(transport, external_url)):
136
137
138class HgDir(bzrlib.bzrdir.BzrDir):
139 """An adapter to the '.hg' dir used by mercurial."""
140
141 def __init__(self, hgrepo, transport, lockfiles, format):
142 self._format = format
143 self.root_transport = transport
144 self.transport = transport.clone('.hg')
145 self._hgrepo = hgrepo
146 self._lockfiles = lockfiles
147
148 def backup_bzrdir(self):
149 self.root_transport.copy_tree(".hg", ".hg.backup")
150 return (self.root_transport.abspath(".hg"),
151 self.root_transport.abspath(".hg.backup"))
152
153 def break_lock(self):
154 """Mercurial locks never break."""
155 raise NotImplementedError(self.break_lock)
156
157 def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
158 """Clone this hg dir to url."""
159 self._make_tail(url)
160 if url.startswith('file://'):
161 url = url[len('file://'):]
162 url = url.encode('utf8')
163 result = self._format.initialize(url)
164 result._hgrepo.pull(self._hgrepo)
165 return result
166
167 def create_branch(self, name=None):
168 """'create' a branch for this dir."""
169 return self.open_branch(name=name)
170
171 def create_repository(self, shared=False):
172 """'create' a repository for this dir."""
173 if shared:
174 # dont know enough about hg yet to do 'shared repositories' in it.
175 raise errors.IncompatibleFormat(self._format, self._format)
176 return self.open_repository()
177
178 def create_workingtree(self, revision_id=None, from_branch=None,
179 accelerator_tree=None, hardlink=False):
180 """'create' a workingtree for this dir."""
181 if revision_id is not None:
182 raise NotImplementedError("revision_id argument not yet supported")
183 if from_branch is not None:
184 raise NotImplementedError("from_branch argument not yet supported")
185 return self.open_workingtree()
186
187 def destroy_branch(self, name=None):
188 if name is not None:
189 raise errors.NoColocatedBranchSupport(self)
190 raise errors.UnsupportedOperation(self.destroy_branch, self)
191
192 def destroy_workingtree(self):
193 raise errors.UnsupportedOperation(self.destroy_workingtree, self)
194
195 def destroy_repository(self):
196 raise errors.UnsupportedOperation(self.destroy_repository, self)
197
198 def get_branch_transport(self, branch_format, name=None):
199 if name is not None:
200 raise errors.NoColocatedBranchSupport(self)
201 if branch_format is None:
202 return self.transport
203 if isinstance(branch_format, HgBzrDirFormat):
204 return self.transport
205 raise errors.IncompatibleFormat(branch_format, self._format)
206
207 get_repository_transport = get_branch_transport
208 get_workingtree_transport = get_branch_transport
209
210 def is_supported(self):
211 return True
212
213 def needs_format_conversion(self, format=None):
214 return (format is not HgBzrDirFormat)
215
216 def open_branch(self, name=None, unsupported=False,
217 ignore_fallbacks=False):
218 """'create' a branch for this dir."""
219 if name is not None:
220 raise errors.NoColocatedBranchSupport(self)
221 from bzrlib.plugins.hg.branch import HgLocalBranch, HgRemoteBranch
222 if self._hgrepo.local():
223 branch_klass = HgLocalBranch
224 else:
225 branch_klass = HgRemoteBranch
226 return branch_klass(self._hgrepo, self, self._lockfiles)
227
228 def open_repository(self, shared=False):
229 """'open' a repository for this dir."""
230 from bzrlib.plugins.hg.repository import (
231 HgLocalRepository,
232 HgRemoteRepository,
233 )
234 if self._hgrepo.local():
235 repo_klass = HgLocalRepository
236 else:
237 repo_klass = HgRemoteRepository
238 return repo_klass(self._hgrepo, self, self._lockfiles)
239
240 def open_workingtree(self, shared=False, recommend_upgrade=False):
241 """'open' a workingtree for this dir."""
242 from bzrlib.plugins.hg.workingtree import HgWorkingTree
243 return HgWorkingTree(self._hgrepo, self.open_branch(), self,
244 self._lockfiles)
245
246 def cloning_metadir(self, stacked=False):
247 return bzrlib.bzrdir.format_registry.make_bzrdir("default-rich-root")
248
249
250class HgToSomethingConverter(bzrlib.bzrdir.Converter):
251 """A class to upgrade an hg dir to something else."""
252
253 def __init__(self, format):
254 self.format = format
255 if self.format is None:
256 self.format = bzrlib.bzrdir.BzrDirFormat.get_default_format()
257
258 def convert(self, bzrdir, pb):
259 source_repo = bzrdir.open_repository()
260 source_branch = bzrdir.open_branch()
261 target = self.format.initialize_on_transport(bzrdir.root_transport)
262 target_repo = target.create_repository()
263 target_repo.fetch(source_repo, pb=pb)
264 target_branch = target.create_branch()
265 target_branch.generate_revision_history(source_branch.last_revision())
266 target_wt = target.create_workingtree()
267 bzrdir.root_transport.delete_tree(".hg")
268 return target
269
270
271class HgBzrDirFormat(bzrlib.bzrdir.BzrDirFormat):
272 """The .hg directory control format."""
273
274 def __init__(self):
275 super(HgBzrDirFormat, self).__init__()
276 self.workingtree_format = None
277
278 def get_converter(self, format):
279 """We should write a converter."""
280 return HgToSomethingConverter(format)
281
282 def network_name(self):
283 return "hg"
284
285 def get_format_description(self):
286 return "Mercurial Branch"
287
288 def initialize_on_transport(self, transport):
289 """Initialize a new .not dir in the base directory of a Transport."""
290 return self.open(transport, _create=True)
291
292 def __eq__(self, other):
293 return type(self) == type(other)
294
295 @classmethod
296 def _known_formats(self):
297 return set([HgBzrDirFormat()])
298
299 def open(self, transport, _create=False, _found=None):
300 """Open this directory.
301
302 :param _create: create the hg dir on the fly. private to HgBzrDirFormat.
303 """
304 # we dont grok readonly - hg isn't integrated with transport.
305 if transport.base.startswith('readonly+'):
306 transport = transport._decorated
307 if transport.base.startswith('file://'):
308 path = transport.local_abspath('.').encode('utf-8')
309 lock_class = HgLock
310 else:
311 path = transport.base
312 lock_class = HgDummyLock
313 lazy_load_mercurial()
314 import mercurial.hg
315 from bzrlib.plugins.hg.ui import ui
316 repository = mercurial.hg.repository(ui(), path, create=_create)
317 lockfiles = HgLockableFiles(lock_class(repository), transport)
318 return HgDir(repository, transport, lockfiles, self)
319
320 @classmethod
321 def probe_transport(klass, transport):
322 """Our format is present if the transport ends in '.not/'."""
323 # little ugly, but works
324 format = klass()
325 from bzrlib.transport.local import LocalTransport
326 lazy_load_mercurial()
327 from mercurial import error as hg_errors
328 if isinstance(transport, LocalTransport) and not transport.has(".hg"):
329 # Explicitly check for .hg directories here, so we avoid139 # Explicitly check for .hg directories here, so we avoid
330 # loading foreign branches through Mercurial.140 # loading foreign branches through Mercurial.
331 raise errors.NotBranchError(path=transport.base)141 raise errors.NotBranchError(path=transport.base)
142
143 lazy_load_mercurial()
144 from mercurial import error as hg_errors
145
332 import urllib2146 import urllib2
147 from bzrlib.plugins.hg.dir import HgControlDirFormat
148 format = HgControlDirFormat()
333 try:149 try:
334 format.open(transport)150 format.open(transport)
335 except hg_errors.RepoError, e:151 except hg_errors.RepoError, e:
@@ -342,11 +158,68 @@
342 raise errors.NotBranchError(path=transport.base)158 raise errors.NotBranchError(path=transport.base)
343 return format159 return format
344160
345161 @classmethod
346bzrlib.bzrdir.BzrDirFormat.register_control_format(HgBzrDirFormat)162 def known_formats(cls):
347163 from bzrlib.plugins.hg.dir import HgControlDirFormat
348bzrlib.bzrdir.format_registry.register("hg",164 return set([HgControlDirFormat()])
349 HgBzrDirFormat, "Mercurial repository. ", native=False, hidden=False)165
166
167ControlDirFormat.register_prober(HgProber)
168ControlDirFormat._server_probers.insert(0, HgProber)
169if not getattr(Prober, "known_formats", False): # bzr < 2.4
170 from bzrlib.plugins.hg.dir import HgControlDirFormat
171 ControlDirFormat.register_format(HgControlDirFormat())
172
173controldir_network_format_registry.register_lazy("hg",
174 "bzrlib.plugins.hg.dir", "HgControlDirFormat")
175
176bzrlib.bzrdir.format_registry.register_lazy("hg",
177 "bzrlib.plugins.hg.dir", "HgControlDirFormat",
178 "Mercurial repository. ", native=False, hidden=False)
179
180from bzrlib.repository import (
181 format_registry as repository_format_registry,
182 network_format_registry as repository_network_format_registry,
183 )
184repository_network_format_registry.register_lazy('hg',
185 'bzrlib.plugins.hg.repository', 'HgRepositoryFormat')
186
187
188from bzrlib.branch import (
189 network_format_registry as branch_network_format_registry,
190 )
191branch_network_format_registry.register_lazy(
192 "hg", "bzrlib.plugins.hg.branch", "HgBranchFormat")
193
194try:
195 from bzrlib.branch import (
196 format_registry as branch_format_registry,
197 )
198except ImportError: # bzr < 2.4
199 pass
200else:
201 branch_format_registry.register_extra_lazy(
202 "bzrlib.plugins.hg.branch", "HgBranchFormat")
203
204try:
205 from bzrlib.workingtree import (
206 format_registry as workingtree_format_registry,
207 )
208except ImportError: # bzr < 2.4
209 pass
210else:
211 workingtree_format_registry.register_extra_lazy(
212 "bzrlib.plugins.hg.workingtree", "HgWorkingTreeFormat")
213
214
215try:
216 register_extra_lazy_repository_format = getattr(repository_format_registry,
217 "register_extra_lazy")
218except AttributeError: # bzr < 2.4
219 pass
220else:
221 register_extra_lazy_repository_format('bzrlib.plugins.hg.repository',
222 'HgRepositoryFormat')
350223
351send_format_registry.register_lazy('hg', 'bzrlib.plugins.hg.send',224send_format_registry.register_lazy('hg', 'bzrlib.plugins.hg.send',
352 'send_hg', 'Mecurial bundle format')225 'send_hg', 'Mecurial bundle format')
@@ -360,15 +233,14 @@
360 )233 )
361plugin_cmds.register_lazy('cmd_hg_import', [], 'bzrlib.plugins.hg.commands')234plugin_cmds.register_lazy('cmd_hg_import', [], 'bzrlib.plugins.hg.commands')
362235
363try:236from bzrlib.revisionspec import dwim_revspecs, RevisionSpec_dwim
364 from bzrlib.revisionspec import dwim_revspecs237if getattr(RevisionSpec_dwim, "append_possible_lazy_revspec", None):
365except ImportError:238 RevisionSpec_dwim.append_possible_lazy_revspec(
366 pass239 "bzrlib.plugins.hg.revspec", "RevisionSpec_hg")
367else:240else: # bzr < 2.4
368 from bzrlib.plugins.hg.revspec import RevisionSpec_hg241 from bzrlib.plugins.hg.revspec import RevisionSpec_hg
369 dwim_revspecs.append(RevisionSpec_hg)242 dwim_revspecs.append(RevisionSpec_hg)
370243
371
372def test_suite():244def test_suite():
373 from unittest import TestSuite, TestLoader245 from unittest import TestSuite, TestLoader
374 from bzrlib.plugins.hg import tests246 from bzrlib.plugins.hg import tests
375247
=== modified file 'branch.py'
--- branch.py 2010-08-14 00:38:48 +0000
+++ branch.py 2011-03-23 21:41:04 +0000
@@ -1,5 +1,5 @@
1# Copyright (C) 2005, 2006 Canonical Ltd1# Copyright (C) 2005, 2006, 2011 Canonical Ltd
2# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>2# Copyright (C) 2008-2011 Jelmer Vernooij <jelmer@samba.org>
3#3#
4# This program is free software; you can redistribute it and/or modify4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by5# it under the terms of the GNU General Public License as published by
@@ -39,6 +39,10 @@
39from bzrlib.repository import (39from bzrlib.repository import (
40 InterRepository,40 InterRepository,
41 )41 )
42from bzrlib.tag import (
43 BasicTags,
44 DisabledTags,
45 )
4246
43from bzrlib.plugins.hg.changegroup import (47from bzrlib.plugins.hg.changegroup import (
44 dchangegroup,48 dchangegroup,
@@ -48,6 +52,49 @@
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."
4953
5054
55class HgTags(BasicTags):
56
57 def __init__(self, branch):
58 self.branch = branch
59
60 def _get_hg_tags(self):
61 raise NotImplementedError(self._get_hg_tags)
62
63 def get_tag_dict(self):
64 ret = {}
65 hgtags = self._get_hg_tags()
66 for name, value in hgtags.iteritems():
67 ret[name] = self.branch.repository.lookup_foreign_revision_id(value)
68 return ret
69
70 def set_tag(self, name, value):
71 self.branch.repository._hgrepo.tag([name],
72 self.branch.repository.lookup_bzr_revision_id(value)[0],
73 "Create tag %s" % name,
74 True,
75 self.branch.get_config().username(), None)
76
77
78class LocalHgTags(HgTags):
79
80 def _get_hg_tags(self):
81 return self.branch.repository._hgrepo.tags()
82
83
84class FileHgTags(HgTags):
85
86 def __init__(self, branch, revid):
87 self.branch = branch
88 self.revid = revid
89
90 def _get_hg_tags(self):
91 revtree = self.branch.repository.revision_tree(self.revid)
92 f = revtree.get_file_text(revtree.path2id(".hgtags"), ".hgtags")
93 for l in f.readlines():
94 (name, hgtag) = l.strip().split(" ")
95 yield name, hgtag
96
97
51class HgBranchFormat(BranchFormat):98class HgBranchFormat(BranchFormat):
52 """Mercurial Branch Format.99 """Mercurial Branch Format.
53100
@@ -56,6 +103,11 @@
56 support the branch format.103 support the branch format.
57 """104 """
58105
106 @property
107 def _matchingbzrdir(self):
108 from bzrlib.plugins.hg.dir import HgControlDirFormat
109 return HgControlDirFormat()
110
59 def get_format_description(self):111 def get_format_description(self):
60 """See BranchFormat.get_format_description()."""112 """See BranchFormat.get_format_description()."""
61 return "Mercurial Branch"113 return "Mercurial Branch"
@@ -67,6 +119,25 @@
67 from bzrlib.plugins.hg.tests.test_branch import ForeignTestsBranchFactory119 from bzrlib.plugins.hg.tests.test_branch import ForeignTestsBranchFactory
68 return ForeignTestsBranchFactory()120 return ForeignTestsBranchFactory()
69121
122 def initialize(self, a_bzrdir, name=None, repository=None):
123 from bzrlib.plugins.hg.dir import HgDir
124 if name is None:
125 name = 'default'
126 if not isinstance(a_bzrdir, HgDir):
127 raise errors.IncompatibleFormat(self, a_bzrdir._format)
128 bm = a_bzrdir._hgrepo.branchmap()
129 if name in bm:
130 raise errors.AlreadyBranchError(a_bzrdir.user_url)
131 return a_bzrdir.open_branch(name=name)
132
133 def make_tags(self, branch):
134 """See bzrlib.branch.BranchFormat.make_tags()."""
135 if getattr(branch.repository._hgrepo, "tags", None) is not None:
136 return LocalHgTags(branch)
137 else:
138 return DisabledTags(branch)
139
140
70141
71class HgBranchConfig(object):142class HgBranchConfig(object):
72 """Access Branch Configuration data for an HgBranch.143 """Access Branch Configuration data for an HgBranch.
@@ -78,6 +149,9 @@
78 self._branch = branch149 self._branch = branch
79 self._ui = branch.repository._hgrepo.ui150 self._ui = branch.repository._hgrepo.ui
80151
152 def username(self):
153 return self._ui.config("username", "default")
154
81 def get_nickname(self):155 def get_nickname(self):
82 # remove the trailing / and take the basename.156 # remove the trailing / and take the basename.
83 return os.path.basename(self._branch.base[:-1])157 return os.path.basename(self._branch.base[:-1])
@@ -92,13 +166,13 @@
92 return False166 return False
93167
94 def get_user_option(self, name):168 def get_user_option(self, name):
95 return None169 return self._ui.config(name, "bazaar")
96170
97 def get_user_option_as_bool(self, name):171 def get_user_option_as_bool(self, name):
98 return False172 return False
99173
100 def set_user_option(self, name, value, warn_masked=False):174 def set_user_option(self, name, value, warn_masked=False):
101 pass # FIXME: Uhm?175 self._ui.setconfig(name, "bazaar", value)
102176
103 def log_format(self):177 def log_format(self):
104 """What log format should be used"""178 """What log format should be used"""
@@ -120,14 +194,22 @@
120class HgBranch(ForeignBranch):194class HgBranch(ForeignBranch):
121 """An adapter to mercurial repositories for bzr Branch objects."""195 """An adapter to mercurial repositories for bzr Branch objects."""
122196
123 def __init__(self, hgrepo, hgdir, lockfiles):197 @property
124 self._format = HgBranchFormat()198 def control_url(self):
199 return self.bzrdir.control_url
200
201 @property
202 def control_transport(self):
203 return self.bzrdir.control_transport
204
205 def __init__(self, hgrepo, name, hgdir, lockfiles):
125 self.repository = hgdir.open_repository()206 self.repository = hgdir.open_repository()
126 ForeignBranch.__init__(self, self.repository.get_mapping())207 ForeignBranch.__init__(self, self.repository.get_mapping())
127 self._hgrepo = hgrepo208 self._hgrepo = hgrepo
128 self.bzrdir = hgdir209 self.bzrdir = hgdir
129 self.control_files = lockfiles210 self.control_files = lockfiles
130 self.base = hgdir.root_transport.base211 self.base = hgdir.root_transport.base
212 self.name = name
131213
132 def _check(self):214 def _check(self):
133 # TODO: Call out to mercurial for consistency checking?215 # TODO: Call out to mercurial for consistency checking?
@@ -195,21 +277,42 @@
195 revision_id = source_revision_id277 revision_id = source_revision_id
196 destination.generate_revision_history(revision_id)278 destination.generate_revision_history(revision_id)
197279
280 def _tip(self):
281 try:
282 return self._hgrepo.branchmap()[self.name][0]
283 except KeyError:
284 import mercurial.node
285 return mercurial.node.nullid
286
198287
199class HgLocalBranch(HgBranch):288class HgLocalBranch(HgBranch):
200289
290 def __init__(self, hgrepo, name, hgdir, lockfiles):
291 self._format = HgBranchFormat()
292 super(HgLocalBranch, self).__init__(hgrepo, name, hgdir, lockfiles)
293
294 def supports_tags(self):
295 return True
296
201 @needs_read_lock297 @needs_read_lock
202 def last_revision(self):298 def last_revision(self):
203 tip = self._hgrepo.lookup("tip")299 tip = self._tip()
204 return self.repository.lookup_foreign_revision_id(tip,300 return self.repository.lookup_foreign_revision_id(tip,
205 mapping=self.mapping)301 mapping=self.mapping)
206302
207303
208class HgRemoteBranch(HgBranch):304class HgRemoteBranch(HgBranch):
209305
306 def __init__(self, hgrepo, name, hgdir, lockfiles):
307 self._format = HgBranchFormat()
308 super(HgRemoteBranch, self).__init__(hgrepo, name, hgdir, lockfiles)
309
310 def supports_tags(self):
311 return getattr(self.repository._hgrepo, "tags", None) is not None
312
210 @needs_read_lock313 @needs_read_lock
211 def last_revision(self):314 def last_revision(self):
212 tip = self._hgrepo.lookup("tip")315 tip = self._tip()
213 return self.mapping.revision_id_foreign_to_bzr(tip)316 return self.mapping.revision_id_foreign_to_bzr(tip)
214317
215318
@@ -234,7 +337,15 @@
234 result.old_revno, result.old_revid = self.target.last_revision_info()337 result.old_revno, result.old_revid = self.target.last_revision_info()
235 inter = InterRepository.get(self.source.repository,338 inter = InterRepository.get(self.source.repository,
236 self.target.repository)339 self.target.repository)
340 if stop_revision is None:
341 stop_revision = self.source.last_revision()
237 inter.fetch(revision_id=stop_revision)342 inter.fetch(revision_id=stop_revision)
343 if overwrite:
344 req_base = None
345 else:
346 req_base = self.target.last_revision()
347 self.target.generate_revision_history(stop_revision,
348 req_base, self.source)
238 result.new_revno, result.new_revid = self.target.last_revision_info()349 result.new_revno, result.new_revid = self.target.last_revision_info()
239 return result350 return result
240351
@@ -284,6 +395,8 @@
284 self.target.generate_revision_history(self.source.last_revision(),395 self.target.generate_revision_history(self.source.last_revision(),
285 req_base, self.source)396 req_base, self.source)
286 result.new_revno, result.new_revid = self.target.last_revision_info()397 result.new_revno, result.new_revid = self.target.last_revision_info()
398 tags = FileHgTags(self.target, result.new_revid)
399 result.tag_conflicts = tags.merge_to(self.target.tags, overwrite)
287 return result400 return result
288401
289 def push(self, overwrite=False, stop_revision=None):402 def push(self, overwrite=False, stop_revision=None):
@@ -294,11 +407,18 @@
294 result.old_revid = self.target.last_revision()407 result.old_revid = self.target.last_revision()
295 inter = InterRepository.get(self.source.repository,408 inter = InterRepository.get(self.source.repository,
296 self.target.repository)409 self.target.repository)
410 if stop_revision is not None:
411 stop_revision = self.source.last_revision()
297 inter.fetch(revision_id=stop_revision)412 inter.fetch(revision_id=stop_revision)
298 self.target.generate_revision_history(self.source.last_revision(),413 if overwrite:
299 self.target.last_revision(),414 req_base = None
300 self.source)415 else:
416 req_base = self.target.last_revision()
417 self.target.generate_revision_history(stop_revision, req_base,
418 self.source)
301 result.new_revid = self.target.last_revision()419 result.new_revid = self.target.last_revision()
420 tags = FileHgTags(self.target, result.new_revid)
421 result.tag_conflicts = tags.merge_to(self.target.tags, overwrite)
302 return result422 return result
303423
304424
305425
=== added file 'dir.py'
--- dir.py 1970-01-01 00:00:00 +0000
+++ dir.py 2011-03-23 21:41:04 +0000
@@ -0,0 +1,395 @@
1# Copyright (C) 2005-2011 Canonical Ltd
2# Copyright (C) 2008-2010 Jelmer Vernooij <jelmer@samba.org>
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18"""Mercurial control directory support.
19
20"""
21
22import bzrlib.bzrdir
23import bzrlib.lockable_files
24from bzrlib import (
25 errors,
26 urlutils,
27 )
28
29from bzrlib.controldir import (
30 ControlDir,
31 ControlDirFormat,
32 )
33try:
34 from bzrlib.controldir import Converter
35except ImportError: # bzr < 2.4
36 from bzrlib.bzrdir import Converter
37from bzrlib.plugins.hg import (
38 lazy_load_mercurial,
39 )
40
41
42class HgControlDirConfig(object):
43
44 def get_default_stack_on(self):
45 return None
46
47 def set_default_stack_on(self, value):
48 raise errors.BzrError("Cannot set configuration")
49
50
51class HgDir(ControlDir):
52 """An adapter to the '.hg' dir used by mercurial."""
53
54 @property
55 def user_transport(self):
56 return self.root_transport
57
58 @property
59 def control_transport(self):
60 return self.transport
61
62 def is_control_filename(self, filename):
63 return (filename == ".hg" or filename.startswith(".hg/"))
64
65 def __init__(self, hgrepo, transport, lockfiles, format):
66 self._format = format
67 self.root_transport = transport
68 self.transport = transport.clone('.hg')
69 self._hgrepo = hgrepo
70 self._lockfiles = lockfiles
71
72 def backup_bzrdir(self):
73 self.root_transport.copy_tree(".hg", ".hg.backup")
74 return (self.root_transport.abspath(".hg"),
75 self.root_transport.abspath(".hg.backup"))
76
77 def break_lock(self):
78 """Mercurial locks never break."""
79 raise NotImplementedError(self.break_lock)
80
81 def clone_on_transport(self, transport, revision_id=None,
82 force_new_repo=False, preserve_stacking=False, stacked_on=None,
83 create_prefix=False, use_existing_dir=True, no_tree=False):
84 """See ControlDir.clone_on_transport."""
85 path = transport.local_abspath(".")
86 result = self._format.initialize(path)
87 result._hgrepo.pull(self._hgrepo)
88 return result
89
90 def create_branch(self, name=None, repository=None):
91 """'create' a branch for this dir."""
92 return self.open_branch(name=name)
93
94 def create_repository(self, shared=False):
95 """'create' a repository for this dir."""
96 if shared:
97 raise errors.IncompatibleFormat(self._format, self._format)
98 return self.open_repository()
99
100 def create_workingtree(self, revision_id=None, from_branch=None,
101 accelerator_tree=None, hardlink=False):
102 """'create' a workingtree for this dir."""
103 if revision_id is not None:
104 raise NotImplementedError("revision_id argument not yet supported")
105 if from_branch is not None:
106 raise NotImplementedError("from_branch argument not yet supported")
107 return self.open_workingtree()
108
109 def destroy_branch(self, name=None):
110 raise errors.UnsupportedOperation(self.destroy_branch, self)
111
112 def destroy_workingtree(self):
113 raise errors.UnsupportedOperation(self.destroy_workingtree, self)
114
115 def destroy_repository(self):
116 raise errors.UnsupportedOperation(self.destroy_repository, self)
117
118 def get_branch_transport(self, branch_format, name=None):
119 if branch_format is None:
120 return self.transport
121 if isinstance(branch_format, HgControlDirFormat):
122 return self.transport
123 raise errors.IncompatibleFormat(branch_format, self._format)
124
125 get_repository_transport = get_branch_transport
126 get_workingtree_transport = get_branch_transport
127
128 def is_supported(self):
129 return True
130
131 def needs_format_conversion(self, format=None):
132 return (format is not HgControlDirFormat)
133
134 def open_branch(self, name=None, unsupported=False,
135 ignore_fallbacks=False):
136 """'create' a branch for this dir."""
137 if name is None:
138 name = 'default'
139 from bzrlib.plugins.hg.branch import HgLocalBranch, HgRemoteBranch
140 if self._hgrepo.local():
141 branch_klass = HgLocalBranch
142 else:
143 branch_klass = HgRemoteBranch
144 return branch_klass(self._hgrepo, name, self, self._lockfiles)
145
146 def find_repository(self, shared=False):
147 return self.open_repository(shared)
148
149 def open_repository(self, shared=False):
150 """'open' a repository for this dir."""
151 from bzrlib.plugins.hg.repository import (
152 HgLocalRepository,
153 HgRemoteRepository,
154 )
155 if self._hgrepo.local():
156 repo_klass = HgLocalRepository
157 else:
158 repo_klass = HgRemoteRepository
159 return repo_klass(self._hgrepo, self, self._lockfiles)
160
161 def open_workingtree(self, shared=False, recommend_upgrade=False):
162 """'open' a workingtree for this dir."""
163 from bzrlib.plugins.hg.workingtree import HgWorkingTree
164 return HgWorkingTree(self._hgrepo, self.open_branch(), self,
165 self._lockfiles)
166
167 def cloning_metadir(self, stacked=False):
168 return bzrlib.bzrdir.format_registry.make_bzrdir("default-rich-root")
169
170 def get_config(self):
171 return HgControlDirConfig()
172
173 def sprout(self, url, revision_id=None, force_new_repo=False,
174 recurse='down', possible_transports=None,
175 accelerator_tree=None, hardlink=False, stacked=False,
176 source_branch=None, create_tree_if_local=True):
177 from bzrlib.repository import InterRepository
178 from bzrlib.transport.local import LocalTransport
179 from bzrlib.transport import get_transport
180 target_transport = get_transport(url, possible_transports)
181 target_transport.ensure_base()
182 cloning_format = self.cloning_metadir()
183 # Create/update the result branch
184 result = cloning_format.initialize_on_transport(target_transport)
185 source_branch = self.open_branch()
186 source_repository = self.find_repository()
187 try:
188 result_repo = result.find_repository()
189 except errors.NoRepositoryPresent:
190 result_repo = result.create_repository()
191 target_is_empty = True
192 else:
193 target_is_empty = None # Unknown
194 if stacked:
195 raise errors.IncompatibleRepositories(source_repository, result_repo)
196 interrepo = InterRepository.get(source_repository, result_repo)
197 interrepo.fetch(revision_id=revision_id)
198 result_branch = source_branch.sprout(result,
199 revision_id=revision_id, repository=result_repo)
200 if (create_tree_if_local and isinstance(target_transport, LocalTransport)
201 and (result_repo is None or result_repo.make_working_trees())):
202 wt = result.create_workingtree(accelerator_tree=accelerator_tree,
203 hardlink=hardlink, from_branch=result_branch)
204 wt.lock_write()
205 try:
206 if wt.path2id('') is None:
207 try:
208 wt.set_root_id(self.open_workingtree.get_root_id())
209 except errors.NoWorkingTree:
210 pass
211 finally:
212 wt.unlock()
213 return result
214
215
216class HgToSomethingConverter(Converter):
217 """A class to upgrade an hg dir to something else."""
218
219 def __init__(self, format):
220 self.format = format
221 if self.format is None:
222 self.format = ControlDirFormat.get_default_format()
223
224 def convert(self, bzrdir, pb):
225 source_repo = bzrdir.open_repository()
226 source_branch = bzrdir.open_branch()
227 target = self.format.initialize_on_transport(bzrdir.root_transport)
228 target_repo = target.create_repository()
229 target_repo.fetch(source_repo, pb=pb)
230 target_branch = target.create_branch()
231 target_branch.generate_revision_history(source_branch.last_revision())
232 target_wt = target.create_workingtree()
233 bzrdir.root_transport.delete_tree(".hg")
234 return target
235
236
237LockWarner = bzrlib.lockable_files._LockWarner
238
239
240class HgLockableFiles(bzrlib.lockable_files.LockableFiles):
241 """Hg specific lockable files abstraction."""
242
243 def __init__(self, lock, transport):
244 self.lock_name = "hg lock"
245 self._lock = lock
246 self._transaction = None
247 self._lock_mode = None
248 self._transport = transport
249 self._lock_warner = LockWarner(repr(self))
250
251
252class HgDummyLock(object):
253 """Lock that doesn't actually lock."""
254
255 def __init__(self, hgrepo):
256 self._hgrepo = hgrepo
257
258 def lock_write(self, token=None):
259 if token is not None:
260 raise errors.TokenLockingNotSupported(self)
261 self._lock = self._hgrepo.wlock()
262
263 def lock_read(self):
264 self._lock = None
265
266 def peek(self):
267 raise NotImplementedError(self.peek)
268
269 def unlock(self):
270 if self._lock is not None:
271 self._lock.release()
272
273 def validate_token(self, token):
274 if token is not None:
275 raise errors.TokenLockingNotSupported(self)
276
277
278class HgLock(object):
279 """A lock that thunks through to Hg."""
280
281 def __init__(self, hgrepo):
282 self._hgrepo = hgrepo
283
284 def lock_write(self, token=None):
285 if token is not None:
286 raise errors.TokenLockingNotSupported(self)
287 self._lock = self._hgrepo.wlock()
288
289 def lock_read(self):
290 self._lock = self._hgrepo.lock()
291
292 def peek(self):
293 raise NotImplementedError(self.peek)
294
295 def unlock(self):
296 self._lock.release()
297
298 def validate_token(self, token):
299 if token is not None:
300 raise errors.TokenLockingNotSupported(self)
301
302 def break_lock(self):
303 pass
304
305
306class HgControlDirFormat(ControlDirFormat):
307 """The .hg directory control format."""
308
309 colocated_branches = True
310 fixed_components = True
311
312 def __init__(self):
313 super(HgControlDirFormat, self).__init__()
314 self.workingtree_format = None
315
316 def get_converter(self, format):
317 """We should write a converter."""
318 return HgToSomethingConverter(format)
319
320 def network_name(self):
321 return "hg"
322
323 def get_format_description(self):
324 return "Mercurial Branch"
325
326 def initialize_on_transport_ex(self, transport, use_existing_dir=False,
327 create_prefix=False, force_new_repo=False, stacked_on=None,
328 stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
329 shared_repo=False, vfs_only=False):
330 from bzrlib import trace
331 from bzrlib.bzrdir import CreateRepository
332 from bzrlib.transport import do_catching_redirections
333 def make_directory(transport):
334 transport.mkdir('.')
335 return transport
336 def redirected(transport, e, redirection_notice):
337 trace.note(redirection_notice)
338 return transport._redirected_to(e.source, e.target)
339 try:
340 transport = do_catching_redirections(make_directory, transport,
341 redirected)
342 except errors.FileExists:
343 if not use_existing_dir:
344 raise
345 except errors.NoSuchFile:
346 if not create_prefix:
347 raise
348 transport.create_prefix()
349 controldir = self.initialize_on_transport(transport)
350 repository = controldir.open_repository()
351 repository.lock_write()
352 return (repository, controldir, False, CreateRepository(controldir))
353
354 def get_branch_format(self):
355 from bzrlib.plugins.hg.branch import HgBranchFormat
356 return HgBranchFormat()
357
358 @property
359 def repository_format(self):
360 from bzrlib.plugins.hg.repository import HgRepositoryFormat
361 return HgRepositoryFormat()
362
363 def initialize_on_transport(self, transport):
364 """Initialize a new .not dir in the base directory of a Transport."""
365 return self.open(transport, _create=True)
366
367 def __eq__(self, other):
368 return type(self) == type(other)
369
370 def __hash__(self):
371 return hash((self.__class__,))
372
373 def open(self, transport, _create=False, _found=None):
374 """Open this directory.
375
376 :param _create: create the hg dir on the fly. private to
377 HgControlDirFormat.
378 """
379 # we dont grok readonly - hg isn't integrated with transport.
380 try:
381 url = transport.external_url()
382 except errors.InProcessTransport:
383 raise errors.NotBranchError(transport.base)
384 if url.startswith('file://'):
385 path = transport.local_abspath('.').encode('utf-8')
386 lock_class = HgLock
387 else:
388 path = url
389 lock_class = HgDummyLock
390 lazy_load_mercurial()
391 import mercurial.hg
392 from bzrlib.plugins.hg.ui import ui
393 repository = mercurial.hg.repository(ui(), path, create=_create)
394 lockfiles = HgLockableFiles(lock_class(repository), transport)
395 return HgDir(repository, transport, lockfiles, self)
0396
=== modified file 'fetch.py'
--- fetch.py 2010-08-14 00:41:20 +0000
+++ fetch.py 2011-03-23 21:41:04 +0000
@@ -70,6 +70,8 @@
70 get_overlay,70 get_overlay,
71 )71 )
72from bzrlib.plugins.hg.parsers import (72from bzrlib.plugins.hg.parsers import (
73 chunkiter,
74 deserialize_file_text,
73 parse_changeset,75 parse_changeset,
74 parse_manifest,76 parse_manifest,
75 unpack_chunk_iter,77 unpack_chunk_iter,
@@ -145,6 +147,12 @@
145 # Set of directories that have been created in this delta, their file id147 # Set of directories that have been created in this delta, their file id
146 # as value.148 # as value.
147 directories = {}149 directories = {}
150 if basis_inv is None:
151 # Root is mandatory
152 extra, root_id = inventory_create_directory(directories,
153 basis_inv, other_inv, "", lookup_file_id, revid)
154 for e in extra:
155 yield e
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,
149 # with the set of removed children as value.157 # with the set of removed children as value.
150 maybe_empty_dirs = defaultdict(set)158 maybe_empty_dirs = defaultdict(set)
@@ -209,17 +217,20 @@
209 ie.symlink_target = lookup_symlink((fileid, ie.revision))217 ie.symlink_target = lookup_symlink((fileid, ie.revision))
210 yield (old_path, path, fileid, ie)218 yield (old_path, path, fileid, ie)
211 # Remove empty directories219 # Remove empty directories
212 for path in sorted(maybe_empty_dirs.keys(), reverse=True):220 while maybe_empty_dirs:
213 if maybe_empty_dirs[path] is None:221 for path in sorted(maybe_empty_dirs.keys(), reverse=True):
214 # Stuff was added to this directory in this revision, don't bother222 removed_children = maybe_empty_dirs.pop(path)
215 continue223 if removed_children is None:
216 file_id = basis_inv.path2id(path)224 # Stuff was added to this directory in this revision,
217 # Is this directory really empty ?225 # don't bother
218 if set(basis_inv[file_id].children.keys()) == maybe_empty_dirs[path]:226 continue
219 yield (path, None, file_id, None)227 file_id = basis_inv.path2id(path)
220 dirname = os.path.dirname(path)228 # Is this directory really empty ?
221 if maybe_empty_dirs[dirname] is not None:229 if set(basis_inv[file_id].children.keys()) == removed_children:
222 maybe_empty_dirs[dirname].add(basis_inv[file_id].name)230 yield (path, None, file_id, None)
231 dirname = os.path.dirname(path)
232 if maybe_empty_dirs[dirname] is not None:
233 maybe_empty_dirs[dirname].add(basis_inv[file_id].name)
223234
224235
225def create_directory_texts(texts, invdelta):236def create_directory_texts(texts, invdelta):
@@ -231,7 +242,8 @@
231 def generate_stream():242 def generate_stream():
232 for (old_path, new_path, fileid, ie) in invdelta:243 for (old_path, new_path, fileid, ie) in invdelta:
233 if old_path is None and ie.kind == "directory":244 if old_path is None and ie.kind == "directory":
234 record = FulltextContentFactory((fileid, ie.revision), (), None, "")245 record = FulltextContentFactory((fileid, ie.revision), (),
246 None, "")
235 record.parents = ()247 record.parents = ()
236 yield record248 yield record
237 texts.insert_record_stream(generate_stream())249 texts.insert_record_stream(generate_stream())
@@ -264,7 +276,10 @@
264 if expected_files != files:276 if expected_files != files:
265 raise AssertionError277 raise AssertionError
266 lookup = [m.__getitem__ for m, f in manifest_parents[:2]]278 lookup = [m.__getitem__ for m, f in manifest_parents[:2]]
267 (manifest, flags) = manifest_and_flags_from_tree(parent_trees, tree,279 for i in range(2):
280 if len(lookup) <= i:
281 lookup.append({}.__getitem__)
282 (manifest, flags, unusual_fileids) = manifest_and_flags_from_tree(parent_trees, tree,
268 mapping, lookup)283 mapping, lookup)
269 if set(manifest.keys()) != set(expected_manifest.keys()):284 if set(manifest.keys()) != set(expected_manifest.keys()):
270 raise AssertionError("Different contents in manifests: %r, %r" %285 raise AssertionError("Different contents in manifests: %r, %r" %
@@ -313,6 +328,18 @@
313 ret.append(self._inventories[revid])328 ret.append(self._inventories[revid])
314 return ret329 return ret
315330
331 def _lookup_file_target(self, key):
332 return str(self._symlink_targets[key]).decode("utf-8")
333
334 def _lookup_file_metadata(self, key):
335 try:
336 return self._text_metadata[key]
337 except KeyError:
338 (fileid, revision) = key
339 stream = self.target.texts.get_record_stream([key], 'unordered', True)
340 record = stream.next()
341 return (record.sha1, len(record.get_bytes_as("fulltext")))
342
316 def _import_manifest_delta(self, manifest, flags, files, rev,343 def _import_manifest_delta(self, manifest, flags, files, rev,
317 mapping):344 mapping):
318 parent_invs = self._get_inventories(rev.parent_ids)345 parent_invs = self._get_inventories(rev.parent_ids)
@@ -333,8 +360,8 @@
333 invdelta = list(manifest_to_inventory_delta(mapping.generate_file_id,360 invdelta = list(manifest_to_inventory_delta(mapping.generate_file_id,
334 basis_inv, other_inv, (basis_manifest, basis_flags),361 basis_inv, other_inv, (basis_manifest, basis_flags),
335 (manifest, flags), rev.revision_id, files,362 (manifest, flags), rev.revision_id, files,
336 self._text_metadata.__getitem__,363 self._lookup_file_metadata,
337 self._symlink_targets.__getitem__))364 self._lookup_file_target))
338 return basis_inv, invdelta365 return basis_inv, invdelta
339366
340 def _get_target_fulltext(self, key):367 def _get_target_fulltext(self, key):
@@ -342,31 +369,37 @@
342 return self._symlink_targets[key]369 return self._symlink_targets[key]
343 return self._target_overlay.get_file_fulltext(key)370 return self._target_overlay.get_file_fulltext(key)
344371
345 def _unpack_texts(self, cg, mapping, filetext_map, pb):372 def _create_text_record(self, fileid, revision, parents, kind, fulltext):
373 key = (fileid, revision)
374 if kind == "symlink":
375 self._symlink_targets[key] = fulltext
376 bzr_fulltext = ""
377 else:
378 (meta, bzr_fulltext) = deserialize_file_text(str(fulltext))
379 record = FulltextContentFactory(key, [(fileid, p) for p in parents],
380 osutils.sha_string(bzr_fulltext), bzr_fulltext)
381 self._text_metadata[key] = (record.sha1, len(bzr_fulltext))
382 return record
383
384 def _unpack_texts(self, cg, mapping, kind_map, pb):
346 i = 0385 i = 0
347 # Texts386 # Texts
348 while 1:387 while True:
349 f = mercurial.changegroup.getchunk(cg)388 f = mercurial.changegroup.getchunk(cg)
350 if not f:389 if not f:
351 break390 break
352 i += 1391 i += 1
353 pb.update("fetching texts", i, len(filetext_map))392 pb.update("fetching texts", i, len(kind_map))
354 fileid = mapping.generate_file_id(f)393 fileid = mapping.generate_file_id(f)
355 chunkiter = mercurial.changegroup.chunkiter(cg)394 itertextchunks = chunkiter(cg)
356 def get_text(node):395 def get_text(node):
357 key = iter(filetext_map[fileid][node]).next()396 key = iter(kind_map[fileid][node]).next()
358 return self._get_target_fulltext(key)397 return self._get_target_fulltext(key)
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):
360 for revision, (kind, parents) in filetext_map[fileid][hgkey].iteritems():399 for revision, kind in kind_map[fileid][hgkey].iteritems():
361 key = (fileid, revision)400 text_parents = ()
362 if kind == "symlink":401 yield self._create_text_record(fileid, revision,
363 self._symlink_targets[key] = fulltext402 text_parents, kind, fulltext)
364 bzr_fulltext = ""
365 else:
366 bzr_fulltext = fulltext
367 record = FulltextContentFactory(key, [(fileid, p) for p in parents], osutils.sha_string(bzr_fulltext), bzr_fulltext)
368 self._text_metadata[key] = (record.sha1, len(bzr_fulltext))
369 yield record
370403
371 def _add_inventories(self, todo, mapping, pb):404 def _add_inventories(self, todo, mapping, pb):
372 assert isinstance(todo, list)405 assert isinstance(todo, list)
@@ -444,7 +477,66 @@
444 return r477 return r
445 raise AssertionError478 raise AssertionError
446479
447 def _unpack_manifests(self, chunkiter, mapping, filetext_map, todo, pb):480 def _determine_text_parents(self, parents, path, fileid, revid, kind_map):
481 ret = []
482 for parent in parents:
483 tp = self._determine_text_parent(parent, path, fileid, revid, kind_map)
484 if tp is not None:
485 ret.append(tp)
486 return ret
487
488 def _determine_text_parent(self, parent, path, fileid, revid, kind_map):
489 path2id = getattr(parent, "path2id", None)
490 if path2id is None: # manifest
491 parent_node = parent.get(path)
492 if parent_node is None:
493 # Didn't exist in parent
494 return None
495 revisions = kind_map[fileid][parent_node].keys()
496 tp = self._find_most_recent_ancestor(revisions, revid)
497 return tp
498 elif path2id(path) == fileid:
499 # FIXME: Handle situation where path is not actually in
500 # parent
501 return parent[fileid].revision
502 else:
503 return None
504
505 def _process_manifest(self, manifest, flags, revid, mapping, kind_map):
506 """Process a manifest.
507
508 :param manifest: Mercurial manifest (dict of path -> node)
509 :param flags: Mercurial manifest flags (dict of path -> mode)
510 :param revid: Bazaar revision id
511 :param mapping: Bzr<->Hg mapping to use
512 :param kind_map: Mapping of (fileid, node, revid) -> kind
513 """
514 self._target_overlay.remember_manifest(revid,
515 self._revisions[revid].parent_ids, (manifest, flags))
516 if not self._files[revid]:
517 # Avoid fetching inventories and parent manifests
518 # unnecessarily
519 return
520 rev = self._revisions[revid]
521 parents = []
522 for previd in rev.parent_ids:
523 try:
524 inv = self.target.get_inventory(previd)
525 except errors.NoSuchRevision:
526 parents.append(self._target_overlay.get_manifest_and_flags_by_revid(previd)[0])
527 else:
528 parents.append(inv)
529 for path in self._files[revid]:
530 assert type(path) is str
531 fileid = mapping.generate_file_id(path)
532 if not path in manifest:
533 # Path no longer exists
534 continue
535 kind = flags_kind(flags, path)
536 node = manifest[path]
537 kind_map[fileid][node][revid] = kind
538
539 def _unpack_manifests(self, chunkiter, mapping, kind_map, todo, pb):
448 """Unpack the manifest deltas.540 """Unpack the manifest deltas.
449541
450 :param chunkiter: Iterator over delta chunks for the manifest.542 :param chunkiter: Iterator over delta chunks for the manifest.
@@ -458,42 +550,8 @@
458 for revid in self._manifest2rev_map[hgkey]:550 for revid in self._manifest2rev_map[hgkey]:
459 todo.append(revid)551 todo.append(revid)
460 yield (revid, self._revisions[revid].parent_ids, fulltext)552 yield (revid, self._revisions[revid].parent_ids, fulltext)
461 self._target_overlay.remember_manifest(revid,553 self._process_manifest(manifest, flags, revid, mapping,
462 self._revisions[revid].parent_ids, (manifest, flags))554 kind_map)
463 if not self._files[revid]:
464 # Avoid fetching inventories and parent manifests
465 # unnecessarily
466 continue
467 rev = self._revisions[revid]
468 parents = []
469 for previd in rev.parent_ids:
470 try:
471 inv = self.target.get_inventory(previd)
472 except errors.NoSuchRevision:
473 parents.append(self._target_overlay.get_manifest_and_flags_by_revid(previd)[0])
474 else:
475 parents.append(inv)
476 for path in self._files[revid]:
477 assert type(path) is str
478 fileid = mapping.generate_file_id(path)
479 if not path in manifest:
480 # Path still has to actually exist..
481 continue
482 kind = flags_kind(flags, path)
483 text_parents = []
484 for parent in parents:
485 path2id = getattr(parent, "path2id", None)
486 if path2id is None: # manifest
487 node = parent.get(path)
488 if node is None:
489 continue
490 revisions = filetext_map[fileid][node]
491 tp = self._find_most_recent_ancestor(revisions.keys(), revid)
492 text_parents.append(tp)
493 elif path2id(path) == fileid:
494 # FIXME: Handle situation where path is not actually in parent
495 text_parents.append(parent[fileid].revision)
496 filetext_map[fileid][manifest[path]][revid] = (kind, text_parents)
497555
498 def addchangegroup(self, cg, mapping):556 def addchangegroup(self, cg, mapping):
499 """Import a Mercurial changegroup into the target repository.557 """Import a Mercurial changegroup into the target repository.
@@ -502,26 +560,27 @@
502 :param mapping: Mercurial mapping560 :param mapping: Mercurial mapping
503 """561 """
504 # Changesets562 # Changesets
505 chunkiter = mercurial.changegroup.chunkiter(cg)563 changesetchunks = chunkiter(cg)
506 pb = ui.ui_factory.nested_progress_bar()564 pb = ui.ui_factory.nested_progress_bar()
507 try:565 try:
508 self._unpack_changesets(chunkiter, mapping, pb)566 self._unpack_changesets(changesetchunks, mapping, pb)
509 finally:567 finally:
510 pb.finished()568 pb.finished()
511 # Manifests569 # Manifests
512 manifestchunks = mercurial.changegroup.chunkiter(cg)570 manifestchunks = chunkiter(cg)
513 filetext_map = defaultdict(lambda: defaultdict(dict))571 kind_map = defaultdict(lambda: defaultdict(dict))
514 todo = []572 todo = []
515 pb = ui.ui_factory.nested_progress_bar()573 pb = ui.ui_factory.nested_progress_bar()
516 try:574 try:
517 self._target_overlay.remember_manifest_texts(self._unpack_manifests(manifestchunks, mapping, filetext_map, todo, pb))575 self._target_overlay.remember_manifest_texts(
576 self._unpack_manifests(manifestchunks, mapping, kind_map, todo, pb))
518 finally:577 finally:
519 pb.finished()578 pb.finished()
520 # Texts579 # Texts
521 pb = ui.ui_factory.nested_progress_bar()580 pb = ui.ui_factory.nested_progress_bar()
522 try:581 try:
523 self.target.texts.insert_record_stream(582 self.target.texts.insert_record_stream(
524 self._unpack_texts(cg, mapping, filetext_map, pb))583 self._unpack_texts(cg, mapping, kind_map, pb))
525 finally:584 finally:
526 pb.finished()585 pb.finished()
527 # Adding actual data586 # Adding actual data
528587
=== modified file 'info.py'
--- info.py 2010-08-05 23:55:52 +0000
+++ info.py 2011-03-23 21:41:04 +0000
@@ -1,7 +1,6 @@
1bzr_plugin_name = 'hg'1bzr_plugin_name = 'hg'
22
3bzr_compatible_versions = [(1, x, 0) for x in [13, 14, 15, 16, 17, 18]] + \3bzr_compatible_versions = [(2, x, 0) for x in [3, 4]]
4 [(2, x, 0) for x in [0, 1, 2]]
54
6bzr_minimum_version = bzr_compatible_versions[0]5bzr_minimum_version = bzr_compatible_versions[0]
76
@@ -11,4 +10,6 @@
1110
12bzr_control_formats = {"Mercurial": {'.hg/': None}}11bzr_control_formats = {"Mercurial": {'.hg/': None}}
1312
14hg_compatible_versions = ["1.6"]13hg_compatible_versions = [(1, 6), (1, 7), (1, 8)]
14
15hg_compatible_version_strings = ["%d.%d" % x for x in hg_compatible_versions]
1516
=== modified file 'mapping.py'
--- mapping.py 2010-02-08 13:32:23 +0000
+++ mapping.py 2011-03-23 21:41:04 +0000
@@ -42,12 +42,15 @@
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.
4343
44 """44 """
45 (kind, revid) = rev.split(":", 1)45 try:
46 (kind, revid) = rev.split(":", 1)
47 except ValueError:
48 return "unspecified %s\n" % rev
46 if kind == "svn":49 if kind == "svn":
47 url, revnum = revid.rsplit('@', 1)50 url, revnum = revid.rsplit('@', 1)
48 revnum = int(revnum)51 revnum = int(revnum)
49 parts = url.split('/', 1)52 parts = url.split('/', 1)
50 uuid = parts.pop(0)[4:]53 uuid = parts.pop(0)
51 mod = ''54 mod = ''
52 if parts:55 if parts:
53 mod = parts[0]56 mod = parts[0]
@@ -65,7 +68,9 @@
65 (uuid, revnumstr, branchpathstr) = revid.split(":", 2)68 (uuid, revnumstr, branchpathstr) = revid.split(":", 2)
66 revnum = int(revnumstr)69 revnum = int(revnumstr)
67 branchpath = urllib.unquote(branchpathstr)70 branchpath = urllib.unquote(branchpathstr)
68 return "svn:%s%s@%s" % (uuid, branchpath, revnum)71 return "svn:%s/%s@%s" % (uuid, branchpath, revnum)
72 elif kind == "unspecified":
73 return revid
69 else:74 else:
70 raise KeyError("Unknown VCS '%s'" % kind)75 raise KeyError("Unknown VCS '%s'" % kind)
7176
@@ -175,6 +180,7 @@
175 :param parent_node_lookup: 2-tuple with functions to look up the nodes180 :param parent_node_lookup: 2-tuple with functions to look up the nodes
176 of paths in the tree's parents181 of paths in the tree's parents
177 """182 """
183 assert len(parent_node_lookup) == 2
178 unusual_fileids = {}184 unusual_fileids = {}
179 def get_text_parents(path):185 def get_text_parents(path):
180 assert type(path) == str186 assert type(path) == str
@@ -184,6 +190,7 @@
184 ret.append(lookup(path))190 ret.append(lookup(path))
185 except KeyError:191 except KeyError:
186 ret.append(mercurial.node.nullid)192 ret.append(mercurial.node.nullid)
193 assert len(ret) == 2
187 return tuple(ret)194 return tuple(ret)
188 manifest = {}195 manifest = {}
189 flags = {}196 flags = {}
@@ -263,7 +270,7 @@
263 elif len(revision_id) == 40:270 elif len(revision_id) == 40:
264 hexhgrevid = revision_id271 hexhgrevid = revision_id
265 else:272 else:
266 raise AssertionError273 raise AssertionError("Invalid hg id %r" % revision_id)
267 return "%s:%s" % (cls.revid_prefix, hexhgrevid)274 return "%s:%s" % (cls.revid_prefix, hexhgrevid)
268275
269 @classmethod276 @classmethod
270277
=== modified file 'overlay.py'
--- overlay.py 2009-11-26 21:33:32 +0000
+++ overlay.py 2011-03-23 21:41:04 +0000
@@ -122,7 +122,7 @@
122 """122 """
123 if self.manifests_vf is not None:123 if self.manifests_vf is not None:
124 self.manifests_vf.insert_record_stream(124 self.manifests_vf.insert_record_stream(
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)))
126126
127 def _get_cached_manifest(self, revid):127 def _get_cached_manifest(self, revid):
128 """Attempt to retrieve a cached manifest.128 """Attempt to retrieve a cached manifest.
129129
=== modified file 'parsers.py'
--- parsers.py 2010-02-08 13:32:23 +0000
+++ parsers.py 2011-03-23 21:41:04 +0000
@@ -25,6 +25,7 @@
25"""25"""
2626
27import mercurial.changelog27import mercurial.changelog
28import mercurial.changegroup
28import mercurial.manifest29import mercurial.manifest
29import mercurial.mdiff30import mercurial.mdiff
30import mercurial.node31import mercurial.node
@@ -81,6 +82,7 @@
81 :param text: Text to parse82 :param text: Text to parse
82 :return: Tuple with (manifest, user, (time, timezone), files, desc, extra)83 :return: Tuple with (manifest, user, (time, timezone), files, desc, extra)
83 """84 """
85 text = str(text)
84 last = text.index("\n\n")86 last = text.index("\n\n")
85 desc = text[last + 2:].decode("utf-8")87 desc = text[last + 2:].decode("utf-8")
86 l = text[:last].split('\n')88 l = text[:last].split('\n')
@@ -179,3 +181,34 @@
179 node = mercurial.node.hex(node)181 node = mercurial.node.hex(node)
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, "")))
181 return "".join(lines)183 return "".join(lines)
184
185
186def serialize_file_text(meta, text):
187 if meta or text.startswith('\1\n'):
188 mt = ["%s: %s\n" % (k, v) for k, v in sorted(meta.iteritems())]
189 text = "\1\n%s\1\n%s" % ("".join(mt), text)
190 return text
191
192
193def deserialize_file_text(text):
194 if not text.startswith("\1\n"):
195 return ({}, text)
196 s = text.index('\1\n', 2)
197 mt = text[2:s]
198 meta = {}
199 for l in mt.splitlines():
200 k, v = l.split(": ", 1)
201 meta[k] = v
202 return meta, text[s+2:]
203
204
205def chunkiter(source, progress=None):
206 """iterate through the chunks in source, yielding a sequence of chunks
207 (strings)"""
208 while 1:
209 c = mercurial.changegroup.getchunk(source)
210 if not c:
211 break
212 elif progress is not None:
213 progress()
214 yield c
182215
=== modified file 'repository.py'
--- repository.py 2010-07-31 18:38:03 +0000
+++ repository.py 2011-03-23 21:41:04 +0000
@@ -62,8 +62,23 @@
62 support the repository format.62 support the repository format.
63 """63 """
64 rich_root_data = True64 rich_root_data = True
6565 fast_deltas = True
66 def initialize(self, url, shared=False, _internal=False):66 supports_leaving_lock = False
67 supports_funky_characters = True
68 supports_external_lookups = False
69 supports_full_versioned_files = False
70
71 @property
72 def _matchingbzrdir(self):
73 from bzrlib.plugins.hg.dir import HgControlDirFormat
74 return HgControlDirFormat()
75
76 def initialize(self, controldir, shared=False, _internal=False):
77 from bzrlib.plugins.hg.dir import HgDir
78 if shared:
79 raise errors.IncompatibleFormat(self, controldir._format)
80 if isinstance(controldir, HgDir):
81 return controldir.open_repository()
67 raise errors.UninitializableFormat(self)82 raise errors.UninitializableFormat(self)
6883
69 def is_supported(self):84 def is_supported(self):
@@ -126,6 +141,7 @@
126 return ancestry_cache[some_revision_id]141 return ancestry_cache[some_revision_id]
127 except KeyError:142 except KeyError:
128 pass143 pass
144 assert some_revision_id in all_relevant_revisions
129 ancestry = set()145 ancestry = set()
130 # add what can be reached from some_revision_id146 # add what can be reached from some_revision_id
131 # TODO: must factor this trivial iteration in bzrlib.graph cleanly.147 # TODO: must factor this trivial iteration in bzrlib.graph cleanly.
@@ -287,6 +303,8 @@
287303
288 _serializer = None304 _serializer = None
289305
306 chk_bytes = None
307
290 def __init__(self, hgrepo, hgdir, lockfiles):308 def __init__(self, hgrepo, hgdir, lockfiles):
291 ForeignRepository.__init__(self, HgRepositoryFormat(), hgdir, lockfiles)309 ForeignRepository.__init__(self, HgRepositoryFormat(), hgdir, lockfiles)
292 self._hgrepo = hgrepo310 self._hgrepo = hgrepo
@@ -303,6 +321,9 @@
303 self.inventories = None321 self.inventories = None
304 self.texts = None322 self.texts = None
305323
324 def revision_graph_can_have_wrong_parents(self):
325 return False
326
306 def _warn_if_deprecated(self):327 def _warn_if_deprecated(self):
307 # This class isn't deprecated328 # This class isn't deprecated
308 pass329 pass
@@ -341,20 +362,33 @@
341362
342 def lookup_bzr_revision_id(self, revision_id):363 def lookup_bzr_revision_id(self, revision_id):
343 """See ForeignRepository.lookup_bzr_revision_id()."""364 """See ForeignRepository.lookup_bzr_revision_id()."""
344 assert type(revision_id) is str365 assert type(revision_id) is str, "invalid revid: %r" % revision_id
345 # TODO: Handle round-tripped revisions366 # TODO: Handle round-tripped revisions
346 try:367 try:
347 return mapping_registry.revision_id_bzr_to_foreign(revision_id)368 return mapping_registry.revision_id_bzr_to_foreign(revision_id)
348 except errors.InvalidRevisionId:369 except errors.InvalidRevisionId:
349 raise errors.NoSuchRevision(self, revision_id)370 raise errors.NoSuchRevision(self, revision_id)
350371
372 def get_parent_map(self, revids):
373 ret = {}
374 for revid in revids:
375 hgrevid, mapping = self.lookup_bzr_revision_id(revid)
376 # FIXME: what about extra parents?
377 hgparents = self._hgrepo.changelog.parents(hgrevid)
378 ret[revid] = as_bzr_parents(hgparents, self.lookup_foreign_revision_id)
379 return ret
380
351 def get_revision(self, revision_id):381 def get_revision(self, revision_id):
352 assert type(revision_id) is str382 if not type(revision_id) is str:
383 raise errors.InvalidRevisionId(revision_id, self)
353 hgrevid, mapping = self.lookup_bzr_revision_id(revision_id)384 hgrevid, mapping = self.lookup_bzr_revision_id(revision_id)
385 assert mapping is not None
354 hgchange = self._hgrepo.changelog.read(hgrevid)386 hgchange = self._hgrepo.changelog.read(hgrevid)
355 hgparents = self._hgrepo.changelog.parents(hgrevid)387 hgparents = self._hgrepo.changelog.parents(hgrevid)
356 parent_ids = as_bzr_parents(hgparents, self.lookup_foreign_revision_id)388 parent_ids = as_bzr_parents(hgparents, self.lookup_foreign_revision_id)
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,
390 hgchange[0], hgchange[1].decode("utf-8"), hgchange[2],
391 hgchange[4].decode("utf-8"), hgchange[5])[0]
358392
359 def iter_inventories(self, revision_ids, ordering=None):393 def iter_inventories(self, revision_ids, ordering=None):
360 for revid in revision_ids:394 for revid in revision_ids:
@@ -365,6 +399,7 @@
365 log = self._hgrepo.changelog.read(hgid)399 log = self._hgrepo.changelog.read(hgid)
366 manifest = self._hgrepo.manifest.read(log[0])400 manifest = self._hgrepo.manifest.read(log[0])
367 all_relevant_revisions = self.get_ancestry(revision_id)[1:] + [NULL_REVISION]401 all_relevant_revisions = self.get_ancestry(revision_id)[1:] + [NULL_REVISION]
402
368 pb = ui.ui_factory.nested_progress_bar()403 pb = ui.ui_factory.nested_progress_bar()
369 try:404 try:
370 inv = manifest_to_inventory(self._hgrepo, hgid, log, manifest,405 inv = manifest_to_inventory(self._hgrepo, hgid, log, manifest,
@@ -379,11 +414,18 @@
379 return foreign_revid in self._hgrepo.changelog.nodemap414 return foreign_revid in self._hgrepo.changelog.nodemap
380415
381 def has_revision(self, revision_id):416 def has_revision(self, revision_id):
417 if revision_id == NULL_REVISION:
418 return True
382 try:419 try:
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])
384 except errors.NoSuchRevision:421 except errors.NoSuchRevision:
385 return False422 return False
386423
424 def get_commit_builder(self, branch, parents, config, timestamp=None,
425 timezone=None, committer=None, revprops=None,
426 revision_id=None):
427 raise NotImplementedError(self.get_commit_builder)
428
387429
388class HgRemoteRepository(HgRepository):430class HgRemoteRepository(HgRepository):
389431
390432
=== modified file 'setup.py'
--- setup.py 2010-03-25 21:08:21 +0000
+++ setup.py 2011-03-23 21:41:04 +0000
@@ -1,4 +1,4 @@
1#!/usr/bin/env python2.41#!/usr/bin/env python
22
3from info import *3from info import *
44
55
=== modified file 'tests/__init__.py'
--- tests/__init__.py 2010-07-31 12:16:16 +0000
+++ tests/__init__.py 2011-03-23 21:41:04 +0000
@@ -30,6 +30,7 @@
30 'test_mapping',30 'test_mapping',
31 'test_parsers',31 'test_parsers',
32 'test_pull',32 'test_pull',
33 'test_fetch',
33 ]34 ]
3435
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]))
3637
=== modified file 'tests/test_branch.py'
--- tests/test_branch.py 2009-10-12 09:41:47 +0000
+++ tests/test_branch.py 2011-03-23 21:41:04 +0000
@@ -18,8 +18,8 @@
18 TestCase,18 TestCase,
19 )19 )
2020
21from bzrlib.plugins.hg import (21from bzrlib.plugins.hg.dir import (
22 HgBzrDirFormat,22 HgControlDirFormat,
23 )23 )
24from bzrlib.plugins.hg.branch import (24from bzrlib.plugins.hg.branch import (
25 HgBranchFormat,25 HgBranchFormat,
@@ -28,13 +28,13 @@
28class BranchFormatTests(TestCase):28class BranchFormatTests(TestCase):
2929
30 def test_description(self):30 def test_description(self):
31 self.assertEquals("Mercurial Branch", 31 self.assertEquals("Mercurial Branch",
32 HgBranchFormat().get_format_description())32 HgBranchFormat().get_format_description())
3333
3434
35class ForeignTestsBranchFactory(object):35class ForeignTestsBranchFactory(object):
3636
37 def make_empty_branch(self, transport):37 def make_empty_branch(self, transport):
38 return HgBzrDirFormat().initialize_on_transport(transport).open_branch()38 return HgControlDirFormat().initialize_on_transport(transport).open_branch()
3939
40 make_branch = make_empty_branch40 make_branch = make_empty_branch
4141
=== modified file 'tests/test_dir.py'
--- tests/test_dir.py 2010-07-31 12:25:11 +0000
+++ tests/test_dir.py 2011-03-23 21:41:04 +0000
@@ -22,21 +22,28 @@
22 TestCase,22 TestCase,
23 )23 )
2424
25from bzrlib.plugins.hg import (25from bzrlib.plugins.hg.dir import (
26 HgBzrDirFormat,26 HgControlDirFormat,
27 )27 )
2828
2929
30class HgBzrDirFormatTests(TestCase):30class HgControlDirFormatTests(TestCase):
3131
32 def test_eq(self):32 def test_eq(self):
33 format1 = HgBzrDirFormat()33 format1 = HgControlDirFormat()
34 format2 = HgBzrDirFormat()34 format2 = HgControlDirFormat()
35 self.assertEquals(format1, format2)35 self.assertEquals(format1, format2)
36 self.assertEquals(format1, format1)36 self.assertEquals(format1, format1)
37 bzr_format = format_registry.make_bzrdir("default")37 bzr_format = format_registry.make_bzrdir("default")
38 self.assertNotEquals(bzr_format, format1)38 self.assertNotEquals(bzr_format, format1)
3939
40 def test_hash(self):
41 format1 = HgControlDirFormat()
42 format2 = HgControlDirFormat()
43 self.assertEquals(hash(format1), hash(format2))
44 bzr_format = format_registry.make_bzrdir("default")
45 self.assertNotEquals(hash(bzr_format), hash(format1))
46
40 def test_network_name(self):47 def test_network_name(self):
41 format = HgBzrDirFormat()48 format = HgControlDirFormat()
42 self.assertEquals("hg", format.network_name())49 self.assertEquals("hg", format.network_name())
4350
=== added file 'tests/test_fetch.py'
--- tests/test_fetch.py 1970-01-01 00:00:00 +0000
+++ tests/test_fetch.py 2011-03-23 21:41:04 +0000
@@ -0,0 +1,53 @@
1# Copyright (C) 2010 Leonid Borisenko <leo.borisenko@gmail.com>
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
17"""Tests for fetching from Mercurial into Bazaar."""
18
19from bzrlib.plugins.hg.dir import HgControlDirFormat
20from bzrlib.plugins.hg.ui import ui as hgui
21from bzrlib.tests import TestCaseWithTransport
22
23import mercurial.localrepo
24
25class TestFetching(TestCaseWithTransport):
26
27 def test_recursive_removing_of_empty_directories(self):
28 # Create files in directory of Mercurial repository.
29 self.build_tree([
30 "hg/",
31 "hg/f1",
32 "hg/d1/",
33 "hg/d1/d2/",
34 "hg/d1/d2/f2",
35 ])
36
37 # Create Mercurial repository itself and fill it's history.
38 hgrepo = mercurial.localrepo.localrepository(hgui(), "hg", create=True)
39 hgrepo[None].add(["f1", "d1/d2/f2"])
40 hgrepo.commit("Initial commit")
41 hgrepo[None].remove(["d1/d2/f2"], unlink=True)
42 hgrepo.commit("Remove file f2, so parent directories d2, d1 are empty")
43
44 # Import history from Mercurial repository into Bazaar repository.
45 bzrtree = self.make_branch_and_tree('bzr')
46 hgdir = HgControlDirFormat().open(self.get_transport('hg'))
47 bzrtree.pull(hgdir.open_branch())
48
49 # As file f2 was deleted, directories d1 and d2 should not exists.
50 self.failIfExists('bzr/d1')
51
52 # Self-assurance check that history was really imported.
53 self.failUnlessExists('bzr/f1')
054
=== modified file 'tests/test_mapping.py'
--- tests/test_mapping.py 2010-02-08 13:32:23 +0000
+++ tests/test_mapping.py 2011-03-23 21:41:04 +0000
@@ -25,6 +25,8 @@
25from bzrlib.plugins.hg.mapping import (25from bzrlib.plugins.hg.mapping import (
26 ExperimentalHgMapping,26 ExperimentalHgMapping,
27 HgMappingv1,27 HgMappingv1,
28 convert_converted_from,
29 generate_convert_revision,
28 as_bzr_parents,30 as_bzr_parents,
29 as_hg_parents,31 as_hg_parents,
30 escape_path,32 escape_path,
@@ -41,7 +43,7 @@
41from bzrlib.tests import (43from bzrlib.tests import (
42 TestCase,44 TestCase,
43 )45 )
44 46
45class HgMappingTests(object):47class HgMappingTests(object):
4648
47 def test_revid_foreign_to_bzr(self):49 def test_revid_foreign_to_bzr(self):
@@ -168,3 +170,33 @@
168 m = {"a" * 20: "reva"}170 m = {"a" * 20: "reva"}
169 self.assertEquals(("reva",), 171 self.assertEquals(("reva",),
170 as_bzr_parents(("a" * 20, nullid), m.__getitem__))172 as_bzr_parents(("a" * 20, nullid), m.__getitem__))
173
174
175class ConvertConvertedFromTests(TestCase):
176
177 def test_unspecified(self):
178 self.assertEquals("unspecified foo\n",
179 convert_converted_from("foo"))
180
181 def test_svn(self):
182 self.assertEquals("svn myuuid:42:path\n",
183 convert_converted_from("svn:myuuid/path@42"))
184
185 def test_unknown(self):
186 self.assertRaises(KeyError,
187 convert_converted_from, "unknown:myuuid:42:path")
188
189
190class GenerateConvertRevisionTests(TestCase):
191
192 def test_generate_convert_revision_svn(self):
193 self.assertEquals("svn:myuuid/path@42",
194 generate_convert_revision("svn myuuid:42:path"))
195
196 def test_generate_convert_revision_unspecified(self):
197 self.assertEquals("bla",
198 generate_convert_revision("unspecified bla"))
199
200 def test_generate_convert_revision_unknown(self):
201 self.assertRaises(KeyError,
202 generate_convert_revision, "bla bla")
171203
=== modified file 'tests/test_parsers.py'
--- tests/test_parsers.py 2010-02-08 13:32:23 +0000
+++ tests/test_parsers.py 2011-03-23 21:41:04 +0000
@@ -17,13 +17,15 @@
17import mercurial17import mercurial
1818
19from bzrlib.plugins.hg.parsers import (19from bzrlib.plugins.hg.parsers import (
20 deserialize_file_text,
20 format_changeset,21 format_changeset,
21 parse_changeset,22 parse_changeset,
23 serialize_file_text,
22 )24 )
23from bzrlib.tests import (25from bzrlib.tests import (
24 TestCase,26 TestCase,
25 )27 )
26 28
27class ChangesetFormatterTests(TestCase):29class ChangesetFormatterTests(TestCase):
2830
29 def test_simple(self):31 def test_simple(self):
@@ -116,3 +118,12 @@
116Some118Some
117commit119commit
118message"""))120message"""))
121
122
123class TextSerializers(TestCase):
124
125 def test_serialize(self):
126 self.assertEquals("\1\ncopy: bla\n\1\nfoo\n", serialize_file_text({"copy": "bla"}, "foo\n"))
127
128 def test_deserialize(self):
129 self.assertEquals(({"copy": "bla"}, "foo\n"), deserialize_file_text("\1\ncopy: bla\n\1\nfoo\n"))
119130
=== modified file 'tests/test_pull.py'
--- tests/test_pull.py 2009-10-18 21:57:36 +0000
+++ tests/test_pull.py 2011-03-23 21:41:04 +0000
@@ -15,7 +15,7 @@
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1616
17from bzrlib.inventory import Inventory17from bzrlib.inventory import Inventory
18from bzrlib.plugins.hg import HgBzrDirFormat18from bzrlib.plugins.hg.dir import HgControlDirFormat
19from bzrlib.tests import TestCaseWithTransport19from bzrlib.tests import TestCaseWithTransport
2020
21import base6421import base64
@@ -30,7 +30,7 @@
30 def setUp(self):30 def setUp(self):
31 super(TestPulling, self).setUp()31 super(TestPulling, self).setUp()
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'])
33 hgdir = HgBzrDirFormat().initialize('hg')33 hgdir = HgControlDirFormat().initialize('hg')
34 self.tree = hgdir.open_workingtree()34 self.tree = hgdir.open_workingtree()
35 mode = os.lstat('hg/b').st_mode35 mode = os.lstat('hg/b').st_mode
36 os.chmod('hg/b', mode | stat.S_IEXEC)36 os.chmod('hg/b', mode | stat.S_IEXEC)
@@ -53,7 +53,7 @@
53 file_id='hg:dir')53 file_id='hg:dir')
54 entry.revision = tip54 entry.revision = tip
55 entry = revone_inventory.add_path('dir/c', kind='file',55 entry = revone_inventory.add_path('dir/c', kind='file',
56 file_id='hg:dir:c')56 file_id='hg:dir_sc')
57 entry.revision = tip57 entry.revision = tip
58 entry.text_size = len('contents of hg/dir/c\n')58 entry.text_size = len('contents of hg/dir/c\n')
59 entry.text_sha1 = "958be752affac0fee70471331b96fb3fc1809425"59 entry.text_sha1 = "958be752affac0fee70471331b96fb3fc1809425"
@@ -68,7 +68,7 @@
68 self.revtwo_inventory = copy.deepcopy(revone_inventory)68 self.revtwo_inventory = copy.deepcopy(revone_inventory)
69 tip = self.tree.last_revision()69 tip = self.tree.last_revision()
70 entry = self.revtwo_inventory.add_path('dir/d', kind='file',70 entry = self.revtwo_inventory.add_path('dir/d', kind='file',
71 file_id='hg:dir:d')71 file_id='hg:dir_sd')
72 entry.revision = tip72 entry.revision = tip
73 entry.text_size = len('contents of hg/dir/d\n')73 entry.text_size = len('contents of hg/dir/d\n')
74 entry.text_sha1 = "f48fc342f707bfb4711790e1813c0df4d44e1a23"74 entry.text_sha1 = "f48fc342f707bfb4711790e1813c0df4d44e1a23"
@@ -91,7 +91,7 @@
91 self.tree.commit('change dir/c')91 self.tree.commit('change dir/c')
92 self.revfour_inventory = copy.deepcopy(self.revthree_inventory)92 self.revfour_inventory = copy.deepcopy(self.revthree_inventory)
93 tip = self.tree.last_revision()93 tip = self.tree.last_revision()
94 entry = self.revfour_inventory['hg:dir:c']94 entry = self.revfour_inventory['hg:dir_sc']
95 entry.revision = tip95 entry.revision = tip
96 entry.text_size = len('new contents')96 entry.text_size = len('new contents')
97 entry.text_sha1 = "7ffa72b76d5d66da37f4b614b7a822c01f23c183"97 entry.text_sha1 = "7ffa72b76d5d66da37f4b614b7a822c01f23c183"
9898
=== modified file 'tests/test_repository.py'
--- tests/test_repository.py 2009-11-12 22:57:40 +0000
+++ tests/test_repository.py 2011-03-23 21:41:04 +0000
@@ -15,11 +15,11 @@
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1616
1717
18from bzrlib.plugins.hg import (18from bzrlib.plugins.hg.dir import (
19 HgBzrDirFormat,19 HgControlDirFormat,
20 )20 )
2121
22class ForeignTestsRepositoryFactory(object):22class ForeignTestsRepositoryFactory(object):
2323
24 def make_repository(self, transport):24 def make_repository(self, transport):
25 return HgBzrDirFormat().initialize_on_transport(transport).open_repository()25 return HgControlDirFormat().initialize_on_transport(transport).open_repository()
2626
=== modified file 'ui.py'
--- ui.py 2009-09-29 14:41:23 +0000
+++ ui.py 2011-03-23 21:41:04 +0000
@@ -27,19 +27,19 @@
2727
28 def debug(self, *msg):28 def debug(self, *msg):
29 for x in msg:29 for x in msg:
30 trace.mutter("hg: %s" % x)30 trace.mutter("hg: %s" % x.rstrip())
3131
32 def warn(self, *msg):32 def warn(self, *msg):
33 for x in msg:33 for x in msg:
34 trace.warning("hg: %s" % x)34 trace.warning("hg: %s" % x.rstrip())
3535
36 def note(self, *msg):36 def note(self, *msg):
37 for x in msg:37 for x in msg:
38 trace.mutter("hg: %s" % x)38 trace.mutter("hg: %s" % x.rstrip())
3939
40 def status(self, *msg):40 def status(self, *msg):
41 for x in msg:41 for x in msg:
42 trace.mutter("hg: %s" % x)42 trace.mutter("hg: %s" % x.rstrip())
4343
44 def username(self):44 def username(self):
45 return config.GlobalConfig().username()45 return config.GlobalConfig().username()
4646
=== modified file 'util.py'
--- util.py 2009-10-15 17:27:10 +0000
+++ util.py 2011-03-23 21:41:04 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>1# Copyright (C) 2009-2010 Jelmer Vernooij <jelmer@samba.org>
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
55
=== modified file 'versionedfiles.py'
--- versionedfiles.py 2009-12-05 01:58:37 +0000
+++ versionedfiles.py 2011-03-23 21:41:04 +0000
@@ -75,6 +75,8 @@
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))
76 except LookupError:76 except LookupError:
77 ret[(revid, )] = None77 ret[(revid, )] = None
78 if ret[(revid,)] == ():
79 ret[(revid,)] = (NULL_REVISION,)
78 return ret80 return ret
7981
80 def keys(self):82 def keys(self):
8183
=== modified file 'workingtree.py'
--- workingtree.py 2010-08-05 23:55:52 +0000
+++ workingtree.py 2011-03-23 21:41:04 +0000
@@ -16,6 +16,11 @@
1616
17"""Mercurial working tree support."""17"""Mercurial working tree support."""
1818
19from bzrlib import osutils
20
21from bzrlib.errors import (
22 IncompatibleFormat,
23 )
19from bzrlib.inventory import (24from bzrlib.inventory import (
20 Inventory,25 Inventory,
21 )26 )
@@ -38,10 +43,21 @@
38 support the working tree format.43 support the working tree format.
39 """44 """
4045
46 @property
47 def _matchingbzrdir(self):
48 from bzrlib.plugins.hg.dir import HgControlDirFormat
49 return HgControlDirFormat()
50
41 def get_format_description(self):51 def get_format_description(self):
42 """See WorkingTreeFormat.get_format_description()."""52 """See WorkingTreeFormat.get_format_description()."""
43 return "Mercurial Working Tree"53 return "Mercurial Working Tree"
4454
55 def initialize(self, to_bzrdir):
56 from bzrlib.plugins.hg.dir import HgDir
57 if not isinstance(to_bzrdir, HgDir):
58 raise IncompatibleFormat(self, to_bzrdir._format)
59 return to_bzrdir.create_workingtree()
60
4561
46class HgWorkingTree(bzrlib.workingtree.WorkingTree):62class HgWorkingTree(bzrlib.workingtree.WorkingTree):
47 """An adapter to mercurial repositories for bzr WorkingTree obejcts."""63 """An adapter to mercurial repositories for bzr WorkingTree obejcts."""
@@ -50,30 +66,44 @@
50 self._inventory = Inventory()66 self._inventory = Inventory()
51 self._hgrepo = hgrepo67 self._hgrepo = hgrepo
52 self.bzrdir = hgdir68 self.bzrdir = hgdir
69 self.repository = hgdir.open_repository()
53 self._control_files = lockfiles70 self._control_files = lockfiles
54 self._branch = hgbranch71 self._branch = hgbranch
55 self._format = HgWorkingTreeFormat()72 self._format = HgWorkingTreeFormat()
56 self._transport = hgdir.get_workingtree_transport(None)73 self._transport = hgdir.get_workingtree_transport(None)
57 self.basedir = hgdir.root_transport.local_abspath(".")74 self.basedir = hgdir.root_transport.local_abspath(".")
75 self._detect_case_handling()
76 self._rules_searcher = None
58 self.views = self._make_views()77 self.views = self._make_views()
5978
79 def flush(self):
80 pass
81
60 @needs_write_lock82 @needs_write_lock
61 def add(self, files, ids=None):83 def add(self, files, ids=None, kinds=None):
62 # hg does not use ids, toss them out84 # hg does not use ids, toss them out
63 if isinstance(files, basestring):85 if isinstance(files, basestring):
64 files = [files]86 files = [files]
87 if kinds is None:
88 kinds = [osutils.file_kind(self.abspath(f)) for f in files]
89 hg_files = []
90 for file, kind in zip(files, kinds):
91 if kind == "directory":
92 continue
93 hg_files.append(file.encode('utf-8'))
94
65 # hg does not canonicalise paths : make them absolute95 # hg does not canonicalise paths : make them absolute
66 paths = [(file).encode('utf8') for file in files]96 self._hgrepo[None].add(hg_files)
67 self._hgrepo[None].add(paths)
6897
69 @needs_write_lock98 @needs_write_lock
70 def commit(self, message, revprops=None, *args, **kwargs):99 def commit(self, message=None, revprops=None, *args, **kwargs):
71 # TODO: selected file lists -> match function100 # TODO: selected file lists -> match function
72 if revprops is None:101 if revprops is None:
73 extra = {}102 extra = {}
74 else:103 else:
75 extra = revprops104 extra = revprops
76 self._hgrepo.commit(message, extra=extra)105 hgid = self._hgrepo.commit(message.encode("utf-8"), extra=extra, force=True)
106 return self.bzrdir.open_repository().lookup_foreign_revision_id(hgid)
77107
78 def _reset_data(self):108 def _reset_data(self):
79 """Reset all cached data."""109 """Reset all cached data."""

Subscribers

People subscribed via source and target branches