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