##// END OF EJS Templates
Merge with crew-stable
Patrick Mezard -
r8149:ddbee2d0 merge default
parent child Browse files
Show More
@@ -1,224 +1,224 b''
1 1 # bzr support for the convert extension
2 2 # This module is for handling 'bzr', that was formerly known as Bazaar-NG;
3 3 # it cannot access 'bar' repositories, but they were never used very much
4 4
5 5 import os
6 6 from mercurial import demandimport
7 7 # these do not work with demandimport, blacklist
8 8 demandimport.ignore.extend([
9 9 'bzrlib.transactions',
10 10 'bzrlib.urlutils',
11 11 ])
12 12
13 13 from mercurial.i18n import _
14 14 from mercurial import util
15 15 from common import NoRepo, commit, converter_source
16 16
17 17 try:
18 18 # bazaar imports
19 19 from bzrlib import branch, revision, errors
20 20 from bzrlib.revisionspec import RevisionSpec
21 21 except ImportError:
22 22 pass
23 23
24 24 supportedkinds = ('file', 'symlink')
25 25
26 26 class bzr_source(converter_source):
27 27 """Reads Bazaar repositories by using the Bazaar Python libraries"""
28 28
29 29 def __init__(self, ui, path, rev=None):
30 30 super(bzr_source, self).__init__(ui, path, rev=rev)
31 31
32 32 if not os.path.exists(os.path.join(path, '.bzr')):
33 33 raise NoRepo('%s does not look like a Bazaar repo' % path)
34 34
35 35 try:
36 36 # access bzrlib stuff
37 37 branch
38 38 except NameError:
39 39 raise NoRepo('Bazaar modules could not be loaded')
40 40
41 41 path = os.path.abspath(path)
42 42 self.branch = branch.Branch.open(path)
43 43 self.sourcerepo = self.branch.repository
44 44 self._parentids = {}
45 45
46 46 def before(self):
47 47 """Before the conversion begins, acquire a read lock
48 48 for all the operations that might need it. Fortunately
49 49 read locks don't block other reads or writes to the
50 50 repository, so this shouldn't have any impact on the usage of
51 51 the source repository.
52 52
53 53 The alternative would be locking on every operation that
54 54 needs locks (there are currently two: getting the file and
55 55 getting the parent map) and releasing immediately after,
56 56 but this approach can take even 40% longer."""
57 57 self.sourcerepo.lock_read()
58 58
59 59 def after(self):
60 60 self.sourcerepo.unlock()
61 61
62 62 def getheads(self):
63 63 if not self.rev:
64 64 return [self.branch.last_revision()]
65 65 try:
66 66 r = RevisionSpec.from_string(self.rev)
67 67 info = r.in_history(self.branch)
68 68 except errors.BzrError:
69 69 raise util.Abort(_('%s is not a valid revision in current branch')
70 70 % self.rev)
71 71 return [info.rev_id]
72 72
73 73 def getfile(self, name, rev):
74 74 revtree = self.sourcerepo.revision_tree(rev)
75 75 fileid = revtree.path2id(name)
76 76 if fileid is None or revtree.kind(fileid) not in supportedkinds:
77 77 # the file is not available anymore - was deleted
78 78 raise IOError(_('%s is not available in %s anymore') %
79 79 (name, rev))
80 80 sio = revtree.get_file(fileid)
81 81 return sio.read()
82 82
83 83 def getmode(self, name, rev):
84 84 return self._modecache[(name, rev)]
85 85
86 86 def getchanges(self, version):
87 87 # set up caches: modecache and revtree
88 88 self._modecache = {}
89 89 self._revtree = self.sourcerepo.revision_tree(version)
90 90 # get the parentids from the cache
91 91 parentids = self._parentids.pop(version)
92 92 # only diff against first parent id
93 93 prevtree = self.sourcerepo.revision_tree(parentids[0])
94 94 return self._gettreechanges(self._revtree, prevtree)
95 95
96 96 def getcommit(self, version):
97 97 rev = self.sourcerepo.get_revision(version)
98 98 # populate parent id cache
99 99 if not rev.parent_ids:
100 100 parents = []
101 101 self._parentids[version] = (revision.NULL_REVISION,)
102 102 else:
103 103 parents = self._filterghosts(rev.parent_ids)
104 104 self._parentids[version] = parents
105 105
106 106 return commit(parents=parents,
107 107 # bzr uses 1 second timezone precision
108 108 date='%d %d' % (rev.timestamp, rev.timezone / 3600),
109 109 author=self.recode(rev.committer),
110 110 # bzr returns bytestrings or unicode, depending on the content
111 111 desc=self.recode(rev.message),
112 112 rev=version)
113 113
114 114 def gettags(self):
115 115 if not self.branch.supports_tags():
116 116 return {}
117 117 tagdict = self.branch.tags.get_tag_dict()
118 118 bytetags = {}
119 119 for name, rev in tagdict.iteritems():
120 120 bytetags[self.recode(name)] = rev
121 121 return bytetags
122 122
123 123 def getchangedfiles(self, rev, i):
124 124 self._modecache = {}
125 125 curtree = self.sourcerepo.revision_tree(rev)
126 126 parentids = self._parentids.pop(rev)
127 127 if i is not None:
128 128 parentid = parentids[i]
129 129 else:
130 130 # no parent id, get the empty revision
131 131 parentid = revision.NULL_REVISION
132 132
133 133 prevtree = self.sourcerepo.revision_tree(parentid)
134 134 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
135 135 return changes
136 136
137 137 def _gettreechanges(self, current, origin):
138 138 revid = current._revision_id;
139 139 changes = []
140 140 renames = {}
141 141 for (fileid, paths, changed_content, versioned, parent, name,
142 142 kind, executable) in current.iter_changes(origin):
143 143
144 144 if paths[0] == u'' or paths[1] == u'':
145 145 # ignore changes to tree root
146 146 continue
147 147
148 148 # bazaar tracks directories, mercurial does not, so
149 149 # we have to rename the directory contents
150 150 if kind[1] == 'directory':
151 151 if kind[0] not in (None, 'directory'):
152 152 # Replacing 'something' with a directory, record it
153 153 # so it can be removed.
154 154 changes.append((self.recode(paths[0]), revid))
155 155
156 156 if None not in paths and paths[0] != paths[1]:
157 157 # neither an add nor an delete - a move
158 158 # rename all directory contents manually
159 159 subdir = origin.inventory.path2id(paths[0])
160 160 # get all child-entries of the directory
161 161 for name, entry in origin.inventory.iter_entries(subdir):
162 162 # hg does not track directory renames
163 163 if entry.kind == 'directory':
164 164 continue
165 165 frompath = self.recode(paths[0] + '/' + name)
166 166 topath = self.recode(paths[1] + '/' + name)
167 167 # register the files as changed
168 168 changes.append((frompath, revid))
169 169 changes.append((topath, revid))
170 170 # add to mode cache
171 171 mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's')
172 172 or '')
173 173 self._modecache[(topath, revid)] = mode
174 174 # register the change as move
175 175 renames[topath] = frompath
176 176
177 177 # no futher changes, go to the next change
178 178 continue
179 179
180 180 # we got unicode paths, need to convert them
181 181 path, topath = [self.recode(part) for part in paths]
182 182
183 183 if topath is None:
184 184 # file deleted
185 185 changes.append((path, revid))
186 186 continue
187 187
188 188 # renamed
189 189 if path and path != topath:
190 190 renames[topath] = path
191 191 changes.append((path, revid))
192 192
193 193 # populate the mode cache
194 194 kind, executable = [e[1] for e in (kind, executable)]
195 mode = ((executable and 'x') or (kind == 'symlink' and 's')
195 mode = ((executable and 'x') or (kind == 'symlink' and 'l')
196 196 or '')
197 197 self._modecache[(topath, revid)] = mode
198 198 changes.append((topath, revid))
199 199
200 200 return changes, renames
201 201
202 202 def _filterghosts(self, ids):
203 203 """Filters out ghost revisions which hg does not support, see
204 204 <http://bazaar-vcs.org/GhostRevision>
205 205 """
206 206 parentmap = self.sourcerepo.get_parent_map(ids)
207 207 parents = tuple([parent for parent in ids if parent in parentmap])
208 208 return parents
209 209
210 210 def recode(self, s, encoding=None):
211 211 """This version of recode tries to encode unicode to bytecode,
212 212 and preferably using the UTF-8 codec.
213 213 Other types than Unicode are silently returned, this is by
214 214 intention, e.g. the None-type is not going to be encoded but instead
215 215 just passed through
216 216 """
217 217 if not encoding:
218 218 encoding = self.encoding or 'utf-8'
219 219
220 220 if isinstance(s, unicode):
221 221 return s.encode(encoding)
222 222 else:
223 223 # leave it alone
224 224 return s
@@ -1,88 +1,91 b''
1 1 #!/bin/sh
2 2
3 3 . "$TESTDIR/bzr-definitions"
4 4
5 5 echo % create and rename on the same file in the same step
6 6 mkdir test-createandrename
7 7 cd test-createandrename
8 8 bzr init -q source
9 9 cd source
10 10 echo a > a
11 11 echo c > c
12 12 echo e > e
13 13 bzr add -q a c e
14 14 bzr commit -q -m 'Initial add: a, c, e'
15 15 bzr mv a b
16 16 bzr mv c d
17 17 bzr mv e f
18 18 echo a2 >> a
19 19 mkdir e
20 20 bzr add -q a e
21 21 bzr commit -q -m 'rename a into b, create a, rename c into d'
22 22 cd ..
23 23 hg convert source source-hg
24 24 glog -R source-hg
25 25 echo "% manifest"
26 26 hg manifest -R source-hg -r tip
27 27 echo "% test --rev option"
28 28 hg convert -r 1 source source-1-hg
29 29 glog -R source-1-hg
30 30 cd ..
31 31
32 32 echo % merge
33 33 mkdir test-merge
34 34 cd test-merge
35 35
36 36 cat > helper.py <<EOF
37 37 import sys
38 38 from bzrlib import workingtree
39 39 wt = workingtree.WorkingTree.open('.')
40 40
41 41 message, stamp = sys.argv[1:]
42 42 wt.commit(message, timestamp=int(stamp))
43 43 EOF
44 44
45 45 bzr init -q source
46 46 cd source
47 47 echo content > a
48 48 echo content2 > b
49 49 bzr add -q a b
50 50 bzr commit -q -m 'Initial add'
51 51 cd ..
52 52 bzr branch -q source source-improve
53 53 cd source
54 54 echo more >> a
55 55 python ../helper.py 'Editing a' 100
56 56 cd ../source-improve
57 57 echo content3 >> b
58 58 python ../helper.py 'Editing b' 200
59 59 cd ../source
60 60 bzr merge -q ../source-improve
61 61 bzr commit -q -m 'Merged improve branch'
62 62 cd ..
63 63 hg convert --datesort source source-hg
64 64 glog -R source-hg
65 65 cd ..
66 66
67 67 echo % symlinks and executable files
68 68 mkdir test-symlinks
69 69 cd test-symlinks
70 70 bzr init -q source
71 71 cd source
72 72 touch program
73 73 chmod +x program
74 74 ln -s program altname
75 bzr add -q altname program
75 mkdir d
76 echo a > d/a
77 ln -s a syma
78 bzr add -q altname program syma d/a
76 79 bzr commit -q -m 'Initial setup'
77 80 touch newprog
78 81 chmod +x newprog
79 82 rm altname
80 83 ln -s newprog altname
81 84 chmod -x program
82 85 bzr add -q newprog
83 86 bzr commit -q -m 'Symlink changed, x bits changed'
84 87 cd ..
85 88 hg convert source source-hg
86 89 manifest source-hg 0
87 90 manifest source-hg tip
88 91 cd ..
@@ -1,58 +1,62 b''
1 1 % create and rename on the same file in the same step
2 2 a => b
3 3 c => d
4 4 e => f
5 5 initializing destination source-hg repository
6 6 scanning source...
7 7 sorting...
8 8 converting...
9 9 1 Initial add: a, c, e
10 10 0 rename a into b, create a, rename c into d
11 11 o 1 "rename a into b, create a, rename c into d" files: a b c d e f
12 12 |
13 13 o 0 "Initial add: a, c, e" files: a c e
14 14
15 15 % manifest
16 16 a
17 17 b
18 18 d
19 19 f
20 20 % test --rev option
21 21 initializing destination source-1-hg repository
22 22 scanning source...
23 23 sorting...
24 24 converting...
25 25 0 Initial add: a, c, e
26 26 o 0 "Initial add: a, c, e" files: a c e
27 27
28 28 % merge
29 29 initializing destination source-hg repository
30 30 scanning source...
31 31 sorting...
32 32 converting...
33 33 3 Initial add
34 34 2 Editing a
35 35 1 Editing b
36 36 0 Merged improve branch
37 37 o 3 "Merged improve branch" files:
38 38 |\
39 39 | o 2 "Editing b" files: b
40 40 | |
41 41 o | 1 "Editing a" files: a
42 42 |/
43 43 o 0 "Initial add" files: a b
44 44
45 45 % symlinks and executable files
46 46 initializing destination source-hg repository
47 47 scanning source...
48 48 sorting...
49 49 converting...
50 50 1 Initial setup
51 51 0 Symlink changed, x bits changed
52 52 % manifest of 0
53 644 altname
53 644 @ altname
54 644 d/a
54 55 755 * program
56 644 @ syma
55 57 % manifest of tip
56 644 altname
58 644 @ altname
59 644 d/a
57 60 755 * newprog
58 61 644 program
62 644 @ syma
General Comments 0
You need to be logged in to leave comments. Login now