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