##// END OF EJS Templates
convert: brz 3.3.0 moved NoSuchFile exception to breezy.transport
av6 -
r50842:43bfddcb stable
parent child Browse files
Show More
@@ -1,337 +1,343 b''
1 # bzr.py - bzr support for the convert extension
1 # bzr.py - bzr support for the convert extension
2 #
2 #
3 # Copyright 2008, 2009 Marek Kubica <marek@xivilization.net> and others
3 # Copyright 2008, 2009 Marek Kubica <marek@xivilization.net> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # This module is for handling Breezy imports or `brz`, but it's also compatible
8 # This module is for handling Breezy imports or `brz`, but it's also compatible
9 # with Bazaar or `bzr`, that was formerly known as Bazaar-NG;
9 # with Bazaar or `bzr`, that was formerly known as Bazaar-NG;
10 # it cannot access `bar` repositories, but they were never used very much.
10 # it cannot access `bar` repositories, but they were never used very much.
11
11
12 import os
12 import os
13
13
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15 from mercurial import (
15 from mercurial import (
16 demandimport,
16 demandimport,
17 error,
17 error,
18 util,
18 util,
19 )
19 )
20 from . import common
20 from . import common
21
21
22
22
23 # these do not work with demandimport, blacklist
23 # these do not work with demandimport, blacklist
24 demandimport.IGNORES.update(
24 demandimport.IGNORES.update(
25 [
25 [
26 'breezy.transactions',
26 'breezy.transactions',
27 'breezy.urlutils',
27 'breezy.urlutils',
28 'ElementPath',
28 'ElementPath',
29 ]
29 ]
30 )
30 )
31
31
32 try:
32 try:
33 # bazaar imports
33 # bazaar imports
34 import breezy.bzr.bzrdir
34 import breezy.bzr.bzrdir
35 import breezy.errors
35 import breezy.errors
36 import breezy.revision
36 import breezy.revision
37 import breezy.revisionspec
37 import breezy.revisionspec
38
38
39 bzrdir = breezy.bzr.bzrdir
39 bzrdir = breezy.bzr.bzrdir
40 errors = breezy.errors
40 errors = breezy.errors
41 revision = breezy.revision
41 revision = breezy.revision
42 revisionspec = breezy.revisionspec
42 revisionspec = breezy.revisionspec
43 revisionspec.RevisionSpec
43 revisionspec.RevisionSpec
44
45 try:
46 # brz 3.3.0 (revno: 7614.2.2)
47 from breezy.transport import NoSuchFile
48 except ImportError:
49 from breezy.errors import NoSuchFile
44 except ImportError:
50 except ImportError:
45 pass
51 pass
46
52
47 supportedkinds = ('file', 'symlink')
53 supportedkinds = ('file', 'symlink')
48
54
49
55
50 class bzr_source(common.converter_source):
56 class bzr_source(common.converter_source):
51 """Reads Bazaar repositories by using the Bazaar Python libraries"""
57 """Reads Bazaar repositories by using the Bazaar Python libraries"""
52
58
53 def __init__(self, ui, repotype, path, revs=None):
59 def __init__(self, ui, repotype, path, revs=None):
54 super(bzr_source, self).__init__(ui, repotype, path, revs=revs)
60 super(bzr_source, self).__init__(ui, repotype, path, revs=revs)
55
61
56 if not os.path.exists(os.path.join(path, b'.bzr')):
62 if not os.path.exists(os.path.join(path, b'.bzr')):
57 raise common.NoRepo(
63 raise common.NoRepo(
58 _(b'%s does not look like a Bazaar repository') % path
64 _(b'%s does not look like a Bazaar repository') % path
59 )
65 )
60
66
61 try:
67 try:
62 # access breezy stuff
68 # access breezy stuff
63 bzrdir
69 bzrdir
64 except NameError:
70 except NameError:
65 raise common.NoRepo(_(b'Bazaar modules could not be loaded'))
71 raise common.NoRepo(_(b'Bazaar modules could not be loaded'))
66
72
67 path = util.abspath(path)
73 path = util.abspath(path)
68 self._checkrepotype(path)
74 self._checkrepotype(path)
69 try:
75 try:
70 bzr_dir = bzrdir.BzrDir.open(path.decode())
76 bzr_dir = bzrdir.BzrDir.open(path.decode())
71 self.sourcerepo = bzr_dir.open_repository()
77 self.sourcerepo = bzr_dir.open_repository()
72 except errors.NoRepositoryPresent:
78 except errors.NoRepositoryPresent:
73 raise common.NoRepo(
79 raise common.NoRepo(
74 _(b'%s does not look like a Bazaar repository') % path
80 _(b'%s does not look like a Bazaar repository') % path
75 )
81 )
76 self._parentids = {}
82 self._parentids = {}
77 self._saverev = ui.configbool(b'convert', b'bzr.saverev')
83 self._saverev = ui.configbool(b'convert', b'bzr.saverev')
78
84
79 def _checkrepotype(self, path):
85 def _checkrepotype(self, path):
80 # Lightweight checkouts detection is informational but probably
86 # Lightweight checkouts detection is informational but probably
81 # fragile at API level. It should not terminate the conversion.
87 # fragile at API level. It should not terminate the conversion.
82 try:
88 try:
83 dir = bzrdir.BzrDir.open_containing(path.decode())[0]
89 dir = bzrdir.BzrDir.open_containing(path.decode())[0]
84 try:
90 try:
85 tree = dir.open_workingtree(recommend_upgrade=False)
91 tree = dir.open_workingtree(recommend_upgrade=False)
86 branch = tree.branch
92 branch = tree.branch
87 except (errors.NoWorkingTree, errors.NotLocalUrl):
93 except (errors.NoWorkingTree, errors.NotLocalUrl):
88 tree = None
94 tree = None
89 branch = dir.open_branch()
95 branch = dir.open_branch()
90 if (
96 if (
91 tree is not None
97 tree is not None
92 and tree.controldir.root_transport.base
98 and tree.controldir.root_transport.base
93 != branch.controldir.root_transport.base
99 != branch.controldir.root_transport.base
94 ):
100 ):
95 self.ui.warn(
101 self.ui.warn(
96 _(
102 _(
97 b'warning: lightweight checkouts may cause '
103 b'warning: lightweight checkouts may cause '
98 b'conversion failures, try with a regular '
104 b'conversion failures, try with a regular '
99 b'branch instead.\n'
105 b'branch instead.\n'
100 )
106 )
101 )
107 )
102 except Exception:
108 except Exception:
103 self.ui.note(_(b'bzr source type could not be determined\n'))
109 self.ui.note(_(b'bzr source type could not be determined\n'))
104
110
105 def before(self):
111 def before(self):
106 """Before the conversion begins, acquire a read lock
112 """Before the conversion begins, acquire a read lock
107 for all the operations that might need it. Fortunately
113 for all the operations that might need it. Fortunately
108 read locks don't block other reads or writes to the
114 read locks don't block other reads or writes to the
109 repository, so this shouldn't have any impact on the usage of
115 repository, so this shouldn't have any impact on the usage of
110 the source repository.
116 the source repository.
111
117
112 The alternative would be locking on every operation that
118 The alternative would be locking on every operation that
113 needs locks (there are currently two: getting the file and
119 needs locks (there are currently two: getting the file and
114 getting the parent map) and releasing immediately after,
120 getting the parent map) and releasing immediately after,
115 but this approach can take even 40% longer."""
121 but this approach can take even 40% longer."""
116 self.sourcerepo.lock_read()
122 self.sourcerepo.lock_read()
117
123
118 def after(self):
124 def after(self):
119 self.sourcerepo.unlock()
125 self.sourcerepo.unlock()
120
126
121 def _bzrbranches(self):
127 def _bzrbranches(self):
122 return self.sourcerepo.find_branches(using=True)
128 return self.sourcerepo.find_branches(using=True)
123
129
124 def getheads(self):
130 def getheads(self):
125 if not self.revs:
131 if not self.revs:
126 # Set using=True to avoid nested repositories (see issue3254)
132 # Set using=True to avoid nested repositories (see issue3254)
127 heads = sorted([b.last_revision() for b in self._bzrbranches()])
133 heads = sorted([b.last_revision() for b in self._bzrbranches()])
128 else:
134 else:
129 revid = None
135 revid = None
130 for branch in self._bzrbranches():
136 for branch in self._bzrbranches():
131 try:
137 try:
132 revspec = self.revs[0].decode()
138 revspec = self.revs[0].decode()
133 r = revisionspec.RevisionSpec.from_string(revspec)
139 r = revisionspec.RevisionSpec.from_string(revspec)
134 info = r.in_history(branch)
140 info = r.in_history(branch)
135 except errors.BzrError:
141 except errors.BzrError:
136 pass
142 pass
137 revid = info.rev_id
143 revid = info.rev_id
138 if revid is None:
144 if revid is None:
139 raise error.Abort(
145 raise error.Abort(
140 _(b'%s is not a valid revision') % self.revs[0]
146 _(b'%s is not a valid revision') % self.revs[0]
141 )
147 )
142 heads = [revid]
148 heads = [revid]
143 # Empty repositories return 'null:', which cannot be retrieved
149 # Empty repositories return 'null:', which cannot be retrieved
144 heads = [h for h in heads if h != b'null:']
150 heads = [h for h in heads if h != b'null:']
145 return heads
151 return heads
146
152
147 def getfile(self, name, rev):
153 def getfile(self, name, rev):
148 name = name.decode()
154 name = name.decode()
149 revtree = self.sourcerepo.revision_tree(rev)
155 revtree = self.sourcerepo.revision_tree(rev)
150
156
151 try:
157 try:
152 kind = revtree.kind(name)
158 kind = revtree.kind(name)
153 except breezy.errors.NoSuchFile:
159 except NoSuchFile:
154 return None, None
160 return None, None
155 if kind not in supportedkinds:
161 if kind not in supportedkinds:
156 # the file is not available anymore - was deleted
162 # the file is not available anymore - was deleted
157 return None, None
163 return None, None
158 mode = self._modecache[(name.encode(), rev)]
164 mode = self._modecache[(name.encode(), rev)]
159 if kind == 'symlink':
165 if kind == 'symlink':
160 target = revtree.get_symlink_target(name)
166 target = revtree.get_symlink_target(name)
161 if target is None:
167 if target is None:
162 raise error.Abort(
168 raise error.Abort(
163 _(b'%s.%s symlink has no target') % (name, rev)
169 _(b'%s.%s symlink has no target') % (name, rev)
164 )
170 )
165 return target.encode(), mode
171 return target.encode(), mode
166 else:
172 else:
167 sio = revtree.get_file(name)
173 sio = revtree.get_file(name)
168 return sio.read(), mode
174 return sio.read(), mode
169
175
170 def getchanges(self, version, full):
176 def getchanges(self, version, full):
171 if full:
177 if full:
172 raise error.Abort(_(b"convert from cvs does not support --full"))
178 raise error.Abort(_(b"convert from cvs does not support --full"))
173 self._modecache = {}
179 self._modecache = {}
174 self._revtree = self.sourcerepo.revision_tree(version)
180 self._revtree = self.sourcerepo.revision_tree(version)
175 # get the parentids from the cache
181 # get the parentids from the cache
176 parentids = self._parentids.pop(version)
182 parentids = self._parentids.pop(version)
177 # only diff against first parent id
183 # only diff against first parent id
178 prevtree = self.sourcerepo.revision_tree(parentids[0])
184 prevtree = self.sourcerepo.revision_tree(parentids[0])
179 files, changes = self._gettreechanges(self._revtree, prevtree)
185 files, changes = self._gettreechanges(self._revtree, prevtree)
180 return files, changes, set()
186 return files, changes, set()
181
187
182 def getcommit(self, version):
188 def getcommit(self, version):
183 rev = self.sourcerepo.get_revision(version)
189 rev = self.sourcerepo.get_revision(version)
184 # populate parent id cache
190 # populate parent id cache
185 if not rev.parent_ids:
191 if not rev.parent_ids:
186 parents = []
192 parents = []
187 self._parentids[version] = (revision.NULL_REVISION,)
193 self._parentids[version] = (revision.NULL_REVISION,)
188 else:
194 else:
189 parents = self._filterghosts(rev.parent_ids)
195 parents = self._filterghosts(rev.parent_ids)
190 self._parentids[version] = parents
196 self._parentids[version] = parents
191
197
192 branch = rev.properties.get('branch-nick', 'default')
198 branch = rev.properties.get('branch-nick', 'default')
193 if branch == 'trunk':
199 if branch == 'trunk':
194 branch = 'default'
200 branch = 'default'
195 return common.commit(
201 return common.commit(
196 parents=parents,
202 parents=parents,
197 date=b'%d %d' % (rev.timestamp, -rev.timezone),
203 date=b'%d %d' % (rev.timestamp, -rev.timezone),
198 author=self.recode(rev.committer),
204 author=self.recode(rev.committer),
199 desc=self.recode(rev.message),
205 desc=self.recode(rev.message),
200 branch=branch.encode('utf8'),
206 branch=branch.encode('utf8'),
201 rev=version,
207 rev=version,
202 saverev=self._saverev,
208 saverev=self._saverev,
203 )
209 )
204
210
205 def gettags(self):
211 def gettags(self):
206 bytetags = {}
212 bytetags = {}
207 for branch in self._bzrbranches():
213 for branch in self._bzrbranches():
208 if not branch.supports_tags():
214 if not branch.supports_tags():
209 return {}
215 return {}
210 tagdict = branch.tags.get_tag_dict()
216 tagdict = branch.tags.get_tag_dict()
211 for name, rev in tagdict.items():
217 for name, rev in tagdict.items():
212 bytetags[self.recode(name)] = rev
218 bytetags[self.recode(name)] = rev
213 return bytetags
219 return bytetags
214
220
215 def getchangedfiles(self, rev, i):
221 def getchangedfiles(self, rev, i):
216 self._modecache = {}
222 self._modecache = {}
217 curtree = self.sourcerepo.revision_tree(rev)
223 curtree = self.sourcerepo.revision_tree(rev)
218 if i is not None:
224 if i is not None:
219 parentid = self._parentids[rev][i]
225 parentid = self._parentids[rev][i]
220 else:
226 else:
221 # no parent id, get the empty revision
227 # no parent id, get the empty revision
222 parentid = revision.NULL_REVISION
228 parentid = revision.NULL_REVISION
223
229
224 prevtree = self.sourcerepo.revision_tree(parentid)
230 prevtree = self.sourcerepo.revision_tree(parentid)
225 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
231 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
226 return changes
232 return changes
227
233
228 def _gettreechanges(self, current, origin):
234 def _gettreechanges(self, current, origin):
229 revid = current._revision_id
235 revid = current._revision_id
230 changes = []
236 changes = []
231 renames = {}
237 renames = {}
232 seen = set()
238 seen = set()
233
239
234 # Fall back to the deprecated attribute for legacy installations.
240 # Fall back to the deprecated attribute for legacy installations.
235 try:
241 try:
236 inventory = origin.root_inventory
242 inventory = origin.root_inventory
237 except AttributeError:
243 except AttributeError:
238 inventory = origin.inventory
244 inventory = origin.inventory
239
245
240 # Process the entries by reverse lexicographic name order to
246 # Process the entries by reverse lexicographic name order to
241 # handle nested renames correctly, most specific first.
247 # handle nested renames correctly, most specific first.
242
248
243 def key(c):
249 def key(c):
244 return c.path[0] or c.path[1] or ""
250 return c.path[0] or c.path[1] or ""
245
251
246 curchanges = sorted(
252 curchanges = sorted(
247 current.iter_changes(origin),
253 current.iter_changes(origin),
248 key=key,
254 key=key,
249 reverse=True,
255 reverse=True,
250 )
256 )
251 for change in curchanges:
257 for change in curchanges:
252 paths = change.path
258 paths = change.path
253 kind = change.kind
259 kind = change.kind
254 executable = change.executable
260 executable = change.executable
255 if paths[0] == u'' or paths[1] == u'':
261 if paths[0] == u'' or paths[1] == u'':
256 # ignore changes to tree root
262 # ignore changes to tree root
257 continue
263 continue
258
264
259 # bazaar tracks directories, mercurial does not, so
265 # bazaar tracks directories, mercurial does not, so
260 # we have to rename the directory contents
266 # we have to rename the directory contents
261 if kind[1] == 'directory':
267 if kind[1] == 'directory':
262 if kind[0] not in (None, 'directory'):
268 if kind[0] not in (None, 'directory'):
263 # Replacing 'something' with a directory, record it
269 # Replacing 'something' with a directory, record it
264 # so it can be removed.
270 # so it can be removed.
265 changes.append((self.recode(paths[0]), revid))
271 changes.append((self.recode(paths[0]), revid))
266
272
267 if kind[0] == 'directory' and None not in paths:
273 if kind[0] == 'directory' and None not in paths:
268 renaming = paths[0] != paths[1]
274 renaming = paths[0] != paths[1]
269 # neither an add nor an delete - a move
275 # neither an add nor an delete - a move
270 # rename all directory contents manually
276 # rename all directory contents manually
271 subdir = inventory.path2id(paths[0])
277 subdir = inventory.path2id(paths[0])
272 # get all child-entries of the directory
278 # get all child-entries of the directory
273 for name, entry in inventory.iter_entries(subdir):
279 for name, entry in inventory.iter_entries(subdir):
274 # hg does not track directory renames
280 # hg does not track directory renames
275 if entry.kind == 'directory':
281 if entry.kind == 'directory':
276 continue
282 continue
277 frompath = self.recode(paths[0] + '/' + name)
283 frompath = self.recode(paths[0] + '/' + name)
278 if frompath in seen:
284 if frompath in seen:
279 # Already handled by a more specific change entry
285 # Already handled by a more specific change entry
280 # This is important when you have:
286 # This is important when you have:
281 # a => b
287 # a => b
282 # a/c => a/c
288 # a/c => a/c
283 # Here a/c must not be renamed into b/c
289 # Here a/c must not be renamed into b/c
284 continue
290 continue
285 seen.add(frompath)
291 seen.add(frompath)
286 if not renaming:
292 if not renaming:
287 continue
293 continue
288 topath = self.recode(paths[1] + '/' + name)
294 topath = self.recode(paths[1] + '/' + name)
289 # register the files as changed
295 # register the files as changed
290 changes.append((frompath, revid))
296 changes.append((frompath, revid))
291 changes.append((topath, revid))
297 changes.append((topath, revid))
292 # add to mode cache
298 # add to mode cache
293 mode = (
299 mode = (
294 (entry.executable and b'x')
300 (entry.executable and b'x')
295 or (entry.kind == 'symlink' and b's')
301 or (entry.kind == 'symlink' and b's')
296 or b''
302 or b''
297 )
303 )
298 self._modecache[(topath, revid)] = mode
304 self._modecache[(topath, revid)] = mode
299 # register the change as move
305 # register the change as move
300 renames[topath] = frompath
306 renames[topath] = frompath
301
307
302 # no further changes, go to the next change
308 # no further changes, go to the next change
303 continue
309 continue
304
310
305 # we got unicode paths, need to convert them
311 # we got unicode paths, need to convert them
306 path, topath = paths
312 path, topath = paths
307 if path is not None:
313 if path is not None:
308 path = self.recode(path)
314 path = self.recode(path)
309 if topath is not None:
315 if topath is not None:
310 topath = self.recode(topath)
316 topath = self.recode(topath)
311 seen.add(path or topath)
317 seen.add(path or topath)
312
318
313 if topath is None:
319 if topath is None:
314 # file deleted
320 # file deleted
315 changes.append((path, revid))
321 changes.append((path, revid))
316 continue
322 continue
317
323
318 # renamed
324 # renamed
319 if path and path != topath:
325 if path and path != topath:
320 renames[topath] = path
326 renames[topath] = path
321 changes.append((path, revid))
327 changes.append((path, revid))
322
328
323 # populate the mode cache
329 # populate the mode cache
324 kind, executable = [e[1] for e in (kind, executable)]
330 kind, executable = [e[1] for e in (kind, executable)]
325 mode = (executable and b'x') or (kind == 'symlink' and b'l') or b''
331 mode = (executable and b'x') or (kind == 'symlink' and b'l') or b''
326 self._modecache[(topath, revid)] = mode
332 self._modecache[(topath, revid)] = mode
327 changes.append((topath, revid))
333 changes.append((topath, revid))
328
334
329 return changes, renames
335 return changes, renames
330
336
331 def _filterghosts(self, ids):
337 def _filterghosts(self, ids):
332 """Filters out ghost revisions which hg does not support, see
338 """Filters out ghost revisions which hg does not support, see
333 <http://bazaar-vcs.org/GhostRevision>
339 <http://bazaar-vcs.org/GhostRevision>
334 """
340 """
335 parentmap = self.sourcerepo.get_parent_map(ids)
341 parentmap = self.sourcerepo.get_parent_map(ids)
336 parents = tuple([parent for parent in ids if parent in parentmap])
342 parents = tuple([parent for parent in ids if parent in parentmap])
337 return parents
343 return parents
General Comments 0
You need to be logged in to leave comments. Login now