##// END OF EJS Templates
convert/bzr: make it work with filemaps (issue1631)...
Patrick Mezard -
r8165:78658990 default
parent child Browse files
Show More
@@ -1,224 +1,223 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 try:
33 33 # access bzrlib stuff
34 34 branch
35 35 except NameError:
36 36 raise NoRepo('Bazaar modules could not be loaded')
37 37
38 38 if not os.path.exists(os.path.join(path, '.bzr')):
39 39 raise NoRepo('%s does not look like a Bazaar repo' % path)
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 parentids = self._parentids.pop(rev)
127 126 if i is not None:
128 parentid = parentids[i]
127 parentid = self._parentids[rev][i]
129 128 else:
130 129 # no parent id, get the empty revision
131 130 parentid = revision.NULL_REVISION
132 131
133 132 prevtree = self.sourcerepo.revision_tree(parentid)
134 133 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
135 134 return changes
136 135
137 136 def _gettreechanges(self, current, origin):
138 137 revid = current._revision_id;
139 138 changes = []
140 139 renames = {}
141 140 for (fileid, paths, changed_content, versioned, parent, name,
142 141 kind, executable) in current.iter_changes(origin):
143 142
144 143 if paths[0] == u'' or paths[1] == u'':
145 144 # ignore changes to tree root
146 145 continue
147 146
148 147 # bazaar tracks directories, mercurial does not, so
149 148 # we have to rename the directory contents
150 149 if kind[1] == 'directory':
151 150 if kind[0] not in (None, 'directory'):
152 151 # Replacing 'something' with a directory, record it
153 152 # so it can be removed.
154 153 changes.append((self.recode(paths[0]), revid))
155 154
156 155 if None not in paths and paths[0] != paths[1]:
157 156 # neither an add nor an delete - a move
158 157 # rename all directory contents manually
159 158 subdir = origin.inventory.path2id(paths[0])
160 159 # get all child-entries of the directory
161 160 for name, entry in origin.inventory.iter_entries(subdir):
162 161 # hg does not track directory renames
163 162 if entry.kind == 'directory':
164 163 continue
165 164 frompath = self.recode(paths[0] + '/' + name)
166 165 topath = self.recode(paths[1] + '/' + name)
167 166 # register the files as changed
168 167 changes.append((frompath, revid))
169 168 changes.append((topath, revid))
170 169 # add to mode cache
171 170 mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's')
172 171 or '')
173 172 self._modecache[(topath, revid)] = mode
174 173 # register the change as move
175 174 renames[topath] = frompath
176 175
177 176 # no futher changes, go to the next change
178 177 continue
179 178
180 179 # we got unicode paths, need to convert them
181 180 path, topath = [self.recode(part) for part in paths]
182 181
183 182 if topath is None:
184 183 # file deleted
185 184 changes.append((path, revid))
186 185 continue
187 186
188 187 # renamed
189 188 if path and path != topath:
190 189 renames[topath] = path
191 190 changes.append((path, revid))
192 191
193 192 # populate the mode cache
194 193 kind, executable = [e[1] for e in (kind, executable)]
195 194 mode = ((executable and 'x') or (kind == 'symlink' and 'l')
196 195 or '')
197 196 self._modecache[(topath, revid)] = mode
198 197 changes.append((topath, revid))
199 198
200 199 return changes, renames
201 200
202 201 def _filterghosts(self, ids):
203 202 """Filters out ghost revisions which hg does not support, see
204 203 <http://bazaar-vcs.org/GhostRevision>
205 204 """
206 205 parentmap = self.sourcerepo.get_parent_map(ids)
207 206 parents = tuple([parent for parent in ids if parent in parentmap])
208 207 return parents
209 208
210 209 def recode(self, s, encoding=None):
211 210 """This version of recode tries to encode unicode to bytecode,
212 211 and preferably using the UTF-8 codec.
213 212 Other types than Unicode are silently returned, this is by
214 213 intention, e.g. the None-type is not going to be encoded but instead
215 214 just passed through
216 215 """
217 216 if not encoding:
218 217 encoding = self.encoding or 'utf-8'
219 218
220 219 if isinstance(s, unicode):
221 220 return s.encode(encoding)
222 221 else:
223 222 # leave it alone
224 223 return s
@@ -1,91 +1,97 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 echo "% test with filemap"
31 cat > filemap <<EOF
32 exclude a
33 EOF
34 hg convert --filemap filemap source source-filemap-hg
35 hg -R source-filemap-hg manifest -r tip
30 36 cd ..
31 37
32 38 echo % merge
33 39 mkdir test-merge
34 40 cd test-merge
35 41
36 42 cat > helper.py <<EOF
37 43 import sys
38 44 from bzrlib import workingtree
39 45 wt = workingtree.WorkingTree.open('.')
40 46
41 47 message, stamp = sys.argv[1:]
42 48 wt.commit(message, timestamp=int(stamp))
43 49 EOF
44 50
45 51 bzr init -q source
46 52 cd source
47 53 echo content > a
48 54 echo content2 > b
49 55 bzr add -q a b
50 56 bzr commit -q -m 'Initial add'
51 57 cd ..
52 58 bzr branch -q source source-improve
53 59 cd source
54 60 echo more >> a
55 61 python ../helper.py 'Editing a' 100
56 62 cd ../source-improve
57 63 echo content3 >> b
58 64 python ../helper.py 'Editing b' 200
59 65 cd ../source
60 66 bzr merge -q ../source-improve
61 67 bzr commit -q -m 'Merged improve branch'
62 68 cd ..
63 69 hg convert --datesort source source-hg
64 70 glog -R source-hg
65 71 cd ..
66 72
67 73 echo % symlinks and executable files
68 74 mkdir test-symlinks
69 75 cd test-symlinks
70 76 bzr init -q source
71 77 cd source
72 78 touch program
73 79 chmod +x program
74 80 ln -s program altname
75 81 mkdir d
76 82 echo a > d/a
77 83 ln -s a syma
78 84 bzr add -q altname program syma d/a
79 85 bzr commit -q -m 'Initial setup'
80 86 touch newprog
81 87 chmod +x newprog
82 88 rm altname
83 89 ln -s newprog altname
84 90 chmod -x program
85 91 bzr add -q newprog
86 92 bzr commit -q -m 'Symlink changed, x bits changed'
87 93 cd ..
88 94 hg convert source source-hg
89 95 manifest source-hg 0
90 96 manifest source-hg tip
91 97 cd ..
@@ -1,62 +1,72 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 % test with filemap
29 initializing destination source-filemap-hg repository
30 scanning source...
31 sorting...
32 converting...
33 1 Initial add: a, c, e
34 0 rename a into b, create a, rename c into d
35 b
36 d
37 f
28 38 % merge
29 39 initializing destination source-hg repository
30 40 scanning source...
31 41 sorting...
32 42 converting...
33 43 3 Initial add
34 44 2 Editing a
35 45 1 Editing b
36 46 0 Merged improve branch
37 47 o 3 "Merged improve branch" files:
38 48 |\
39 49 | o 2 "Editing b" files: b
40 50 | |
41 51 o | 1 "Editing a" files: a
42 52 |/
43 53 o 0 "Initial add" files: a b
44 54
45 55 % symlinks and executable files
46 56 initializing destination source-hg repository
47 57 scanning source...
48 58 sorting...
49 59 converting...
50 60 1 Initial setup
51 61 0 Symlink changed, x bits changed
52 62 % manifest of 0
53 63 644 @ altname
54 64 644 d/a
55 65 755 * program
56 66 644 @ syma
57 67 % manifest of tip
58 68 644 @ altname
59 69 644 d/a
60 70 755 * newprog
61 71 644 program
62 72 644 @ syma
General Comments 0
You need to be logged in to leave comments. Login now