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