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