Mercurial > hg
annotate hgext/convert/bzr.py @ 8250:1b60efdb8bc5
convert: add copyright and license headers to back-ends
author | Martin Geisler <mg@lazybytes.net> |
---|---|
date | Sun, 26 Apr 2009 01:47:44 +0200 |
parents | 56719b1aa6f4 |
children | 7a0fcdd3828f |
rev | line source |
---|---|
8250
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
1 # bzr.py - bzr support for the convert extension |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
2 # |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
3 # Copyright 2008, 2009 Marek Kubica <marek@xivilization.net> and others |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
4 # |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
5 # This software may be used and distributed according to the terms of the |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
6 # GNU General Public License version 2, incorporated herein by reference. |
1b60efdb8bc5
convert: add copyright and license headers to back-ends
Martin Geisler <mg@lazybytes.net>
parents:
8166
diff
changeset
|
7 |
7053 | 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 | |
10 | |
11 import os | |
12 from mercurial import demandimport | |
13 # these do not work with demandimport, blacklist | |
14 demandimport.ignore.extend([ | |
15 'bzrlib.transactions', | |
16 'bzrlib.urlutils', | |
17 ]) | |
18 | |
19 from mercurial.i18n import _ | |
20 from mercurial import util | |
21 from common import NoRepo, commit, converter_source | |
22 | |
23 try: | |
24 # bazaar imports | |
25 from bzrlib import branch, revision, errors | |
26 from bzrlib.revisionspec import RevisionSpec | |
27 except ImportError: | |
28 pass | |
29 | |
8045
e09a2f2ef85d
convert/bzr: fix file rename replaced by a dir case (issue1583)
Patrick Mezard <pmezard@gmail.com>
parents:
8035
diff
changeset
|
30 supportedkinds = ('file', 'symlink') |
e09a2f2ef85d
convert/bzr: fix file rename replaced by a dir case (issue1583)
Patrick Mezard <pmezard@gmail.com>
parents:
8035
diff
changeset
|
31 |
7053 | 32 class bzr_source(converter_source): |
33 """Reads Bazaar repositories by using the Bazaar Python libraries""" | |
34 | |
35 def __init__(self, ui, path, rev=None): | |
36 super(bzr_source, self).__init__(ui, path, rev=rev) | |
37 | |
7973
db3a68fd9387
convert: attempt to check repo type before checking for tool
Matt Mackall <mpm@selenic.com>
parents:
7060
diff
changeset
|
38 if not os.path.exists(os.path.join(path, '.bzr')): |
db3a68fd9387
convert: attempt to check repo type before checking for tool
Matt Mackall <mpm@selenic.com>
parents:
7060
diff
changeset
|
39 raise NoRepo('%s does not look like a Bazaar repo' % path) |
db3a68fd9387
convert: attempt to check repo type before checking for tool
Matt Mackall <mpm@selenic.com>
parents:
7060
diff
changeset
|
40 |
7053 | 41 try: |
42 # access bzrlib stuff | |
43 branch | |
44 except NameError: | |
45 raise NoRepo('Bazaar modules could not be loaded') | |
46 | |
47 path = os.path.abspath(path) | |
48 self.branch = branch.Branch.open(path) | |
49 self.sourcerepo = self.branch.repository | |
50 self._parentids = {} | |
51 | |
52 def before(self): | |
53 """Before the conversion begins, acquire a read lock | |
54 for all the operations that might need it. Fortunately | |
55 read locks don't block other reads or writes to the | |
56 repository, so this shouldn't have any impact on the usage of | |
57 the source repository. | |
58 | |
59 The alternative would be locking on every operation that | |
60 needs locks (there are currently two: getting the file and | |
61 getting the parent map) and releasing immediately after, | |
62 but this approach can take even 40% longer.""" | |
63 self.sourcerepo.lock_read() | |
64 | |
65 def after(self): | |
66 self.sourcerepo.unlock() | |
67 | |
68 def getheads(self): | |
69 if not self.rev: | |
70 return [self.branch.last_revision()] | |
71 try: | |
72 r = RevisionSpec.from_string(self.rev) | |
73 info = r.in_history(self.branch) | |
74 except errors.BzrError: | |
75 raise util.Abort(_('%s is not a valid revision in current branch') | |
76 % self.rev) | |
77 return [info.rev_id] | |
78 | |
79 def getfile(self, name, rev): | |
80 revtree = self.sourcerepo.revision_tree(rev) | |
81 fileid = revtree.path2id(name) | |
8045
e09a2f2ef85d
convert/bzr: fix file rename replaced by a dir case (issue1583)
Patrick Mezard <pmezard@gmail.com>
parents:
8035
diff
changeset
|
82 if fileid is None or revtree.kind(fileid) not in supportedkinds: |
7053 | 83 # the file is not available anymore - was deleted |
84 raise IOError(_('%s is not available in %s anymore') % | |
85 (name, rev)) | |
86 sio = revtree.get_file(fileid) | |
87 return sio.read() | |
88 | |
89 def getmode(self, name, rev): | |
90 return self._modecache[(name, rev)] | |
91 | |
92 def getchanges(self, version): | |
93 # set up caches: modecache and revtree | |
94 self._modecache = {} | |
95 self._revtree = self.sourcerepo.revision_tree(version) | |
96 # get the parentids from the cache | |
97 parentids = self._parentids.pop(version) | |
98 # only diff against first parent id | |
99 prevtree = self.sourcerepo.revision_tree(parentids[0]) | |
100 return self._gettreechanges(self._revtree, prevtree) | |
101 | |
102 def getcommit(self, version): | |
103 rev = self.sourcerepo.get_revision(version) | |
104 # populate parent id cache | |
105 if not rev.parent_ids: | |
106 parents = [] | |
107 self._parentids[version] = (revision.NULL_REVISION,) | |
108 else: | |
109 parents = self._filterghosts(rev.parent_ids) | |
110 self._parentids[version] = parents | |
111 | |
112 return commit(parents=parents, | |
113 # bzr uses 1 second timezone precision | |
114 date='%d %d' % (rev.timestamp, rev.timezone / 3600), | |
115 author=self.recode(rev.committer), | |
116 # bzr returns bytestrings or unicode, depending on the content | |
117 desc=self.recode(rev.message), | |
118 rev=version) | |
119 | |
120 def gettags(self): | |
121 if not self.branch.supports_tags(): | |
122 return {} | |
123 tagdict = self.branch.tags.get_tag_dict() | |
124 bytetags = {} | |
125 for name, rev in tagdict.iteritems(): | |
126 bytetags[self.recode(name)] = rev | |
127 return bytetags | |
128 | |
129 def getchangedfiles(self, rev, i): | |
130 self._modecache = {} | |
131 curtree = self.sourcerepo.revision_tree(rev) | |
132 if i is not None: | |
8165
78658990c725
convert/bzr: make it work with filemaps (issue1631)
Patrick Mezard <pmezard@gmail.com>
parents:
8148
diff
changeset
|
133 parentid = self._parentids[rev][i] |
7053 | 134 else: |
135 # no parent id, get the empty revision | |
136 parentid = revision.NULL_REVISION | |
137 | |
138 prevtree = self.sourcerepo.revision_tree(parentid) | |
139 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]] | |
140 return changes | |
141 | |
142 def _gettreechanges(self, current, origin): | |
143 revid = current._revision_id; | |
144 changes = [] | |
145 renames = {} | |
146 for (fileid, paths, changed_content, versioned, parent, name, | |
147 kind, executable) in current.iter_changes(origin): | |
148 | |
149 if paths[0] == u'' or paths[1] == u'': | |
150 # ignore changes to tree root | |
151 continue | |
152 | |
153 # bazaar tracks directories, mercurial does not, so | |
154 # we have to rename the directory contents | |
155 if kind[1] == 'directory': | |
8126
13b36eb14324
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard <pmezard@gmail.com>
parents:
8045
diff
changeset
|
156 if kind[0] not in (None, 'directory'): |
13b36eb14324
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard <pmezard@gmail.com>
parents:
8045
diff
changeset
|
157 # Replacing 'something' with a directory, record it |
13b36eb14324
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard <pmezard@gmail.com>
parents:
8045
diff
changeset
|
158 # so it can be removed. |
13b36eb14324
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard <pmezard@gmail.com>
parents:
8045
diff
changeset
|
159 changes.append((self.recode(paths[0]), revid)) |
13b36eb14324
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard <pmezard@gmail.com>
parents:
8045
diff
changeset
|
160 |
7053 | 161 if None not in paths and paths[0] != paths[1]: |
162 # neither an add nor an delete - a move | |
163 # rename all directory contents manually | |
164 subdir = origin.inventory.path2id(paths[0]) | |
165 # get all child-entries of the directory | |
166 for name, entry in origin.inventory.iter_entries(subdir): | |
167 # hg does not track directory renames | |
168 if entry.kind == 'directory': | |
169 continue | |
170 frompath = self.recode(paths[0] + '/' + name) | |
171 topath = self.recode(paths[1] + '/' + name) | |
172 # register the files as changed | |
173 changes.append((frompath, revid)) | |
174 changes.append((topath, revid)) | |
175 # add to mode cache | |
176 mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's') | |
177 or '') | |
178 self._modecache[(topath, revid)] = mode | |
179 # register the change as move | |
180 renames[topath] = frompath | |
181 | |
182 # no futher changes, go to the next change | |
183 continue | |
184 | |
185 # we got unicode paths, need to convert them | |
186 path, topath = [self.recode(part) for part in paths] | |
187 | |
188 if topath is None: | |
189 # file deleted | |
190 changes.append((path, revid)) | |
191 continue | |
192 | |
193 # renamed | |
194 if path and path != topath: | |
195 renames[topath] = path | |
8035
cb77c0fbec39
convert: remove renamed source files (issue1505)
Xavier ALT <dex@phoenix-ind.net>
parents:
7060
diff
changeset
|
196 changes.append((path, revid)) |
7053 | 197 |
198 # populate the mode cache | |
199 kind, executable = [e[1] for e in (kind, executable)] | |
8148
adce97d28389
convert/bzr: fix symlink handling (issue1626)
Patrick Mezard <pmezard@gmail.com>
parents:
8126
diff
changeset
|
200 mode = ((executable and 'x') or (kind == 'symlink' and 'l') |
7053 | 201 or '') |
202 self._modecache[(topath, revid)] = mode | |
203 changes.append((topath, revid)) | |
204 | |
205 return changes, renames | |
206 | |
207 def _filterghosts(self, ids): | |
208 """Filters out ghost revisions which hg does not support, see | |
209 <http://bazaar-vcs.org/GhostRevision> | |
210 """ | |
211 parentmap = self.sourcerepo.get_parent_map(ids) | |
7060
972cce34f345
convert: fixed python2.3 incompatibility in bzr source (generator expression)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
7053
diff
changeset
|
212 parents = tuple([parent for parent in ids if parent in parentmap]) |
7053 | 213 return parents |
214 | |
215 def recode(self, s, encoding=None): | |
216 """This version of recode tries to encode unicode to bytecode, | |
217 and preferably using the UTF-8 codec. | |
218 Other types than Unicode are silently returned, this is by | |
219 intention, e.g. the None-type is not going to be encoded but instead | |
220 just passed through | |
221 """ | |
222 if not encoding: | |
223 encoding = self.encoding or 'utf-8' | |
224 | |
225 if isinstance(s, unicode): | |
226 return s.encode(encoding) | |
227 else: | |
228 # leave it alone | |
229 return s |