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