##// END OF EJS Templates
test-convert: test before() and after() conversion actions
Patrick Mezard -
r5805:e422305e default
parent child Browse files
Show More
@@ -1,276 +1,284 b''
1 1 # hg backend for convert extension
2 2
3 3 # Notes for hg->hg conversion:
4 4 #
5 5 # * Old versions of Mercurial didn't trim the whitespace from the ends
6 6 # of commit messages, but new versions do. Changesets created by
7 7 # those older versions, then converted, may thus have different
8 8 # hashes for changesets that are otherwise identical.
9 9 #
10 10 # * By default, the source revision is stored in the converted
11 11 # revision. This will cause the converted revision to have a
12 12 # different identity than the source. To avoid this, use the
13 13 # following option: "--config convert.hg.saverev=false"
14 14
15 15
16 16 import os, time
17 17 from mercurial.i18n import _
18 18 from mercurial.node import *
19 19 from mercurial import hg, lock, revlog, util
20 20
21 21 from common import NoRepo, commit, converter_source, converter_sink
22 22
23 23 class mercurial_sink(converter_sink):
24 24 def __init__(self, ui, path):
25 25 converter_sink.__init__(self, ui, path)
26 26 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
27 27 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
28 28 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
29 29 self.lastbranch = None
30 30 if os.path.isdir(path) and len(os.listdir(path)) > 0:
31 31 try:
32 32 self.repo = hg.repository(self.ui, path)
33 33 except hg.RepoError, err:
34 34 ui.print_exc()
35 35 raise NoRepo(err.args[0])
36 36 else:
37 37 try:
38 38 ui.status(_('initializing destination %s repository\n') % path)
39 39 self.repo = hg.repository(self.ui, path, create=True)
40 40 self.created.append(path)
41 41 except hg.RepoError, err:
42 42 ui.print_exc()
43 43 raise NoRepo("could not create hg repo %s as sink" % path)
44 44 self.lock = None
45 45 self.wlock = None
46 46 self.filemapmode = False
47 47
48 48 def before(self):
49 self.ui.debug(_('run hg sink pre-conversion action\n'))
49 50 self.wlock = self.repo.wlock()
50 51 self.lock = self.repo.lock()
51 52 self.repo.dirstate.clear()
52 53
53 54 def after(self):
55 self.ui.debug(_('run hg sink post-conversion action\n'))
54 56 self.repo.dirstate.invalidate()
55 57 self.lock = None
56 58 self.wlock = None
57 59
58 60 def revmapfile(self):
59 61 return os.path.join(self.path, ".hg", "shamap")
60 62
61 63 def authorfile(self):
62 64 return os.path.join(self.path, ".hg", "authormap")
63 65
64 66 def getheads(self):
65 67 h = self.repo.changelog.heads()
66 68 return [ hex(x) for x in h ]
67 69
68 70 def putfile(self, f, e, data):
69 71 self.repo.wwrite(f, data, e)
70 72 if f not in self.repo.dirstate:
71 73 self.repo.dirstate.normallookup(f)
72 74
73 75 def copyfile(self, source, dest):
74 76 self.repo.copy(source, dest)
75 77
76 78 def delfile(self, f):
77 79 try:
78 80 util.unlink(self.repo.wjoin(f))
79 81 #self.repo.remove([f])
80 82 except OSError:
81 83 pass
82 84
83 85 def setbranch(self, branch, pbranch, parents):
84 86 if (not self.clonebranches) or (branch == self.lastbranch):
85 87 return
86 88
87 89 self.lastbranch = branch
88 90 self.after()
89 91 if not branch:
90 92 branch = 'default'
91 93 if not pbranch:
92 94 pbranch = 'default'
93 95
94 96 branchpath = os.path.join(self.path, branch)
95 97 try:
96 98 self.repo = hg.repository(self.ui, branchpath)
97 99 except:
98 100 if not parents:
99 101 self.repo = hg.repository(self.ui, branchpath, create=True)
100 102 else:
101 103 self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch))
102 104 hg.clone(self.ui, os.path.join(self.path, pbranch),
103 105 branchpath, rev=parents, update=False,
104 106 stream=True)
105 107 self.repo = hg.repository(self.ui, branchpath)
106 108 self.before()
107 109
108 110 def putcommit(self, files, parents, commit):
109 111 seen = {}
110 112 pl = []
111 113 for p in parents:
112 114 if p not in seen:
113 115 pl.append(p)
114 116 seen[p] = 1
115 117 parents = pl
116 118 nparents = len(parents)
117 119 if self.filemapmode and nparents == 1:
118 120 m1node = self.repo.changelog.read(bin(parents[0]))[0]
119 121 parent = parents[0]
120 122
121 123 if len(parents) < 2: parents.append("0" * 40)
122 124 if len(parents) < 2: parents.append("0" * 40)
123 125 p2 = parents.pop(0)
124 126
125 127 text = commit.desc
126 128 extra = commit.extra.copy()
127 129 if self.branchnames and commit.branch:
128 130 extra['branch'] = commit.branch
129 131 if commit.rev:
130 132 extra['convert_revision'] = commit.rev
131 133
132 134 while parents:
133 135 p1 = p2
134 136 p2 = parents.pop(0)
135 137 a = self.repo.rawcommit(files, text, commit.author, commit.date,
136 138 bin(p1), bin(p2), extra=extra)
137 139 self.repo.dirstate.clear()
138 140 text = "(octopus merge fixup)\n"
139 141 p2 = hg.hex(self.repo.changelog.tip())
140 142
141 143 if self.filemapmode and nparents == 1:
142 144 man = self.repo.manifest
143 145 mnode = self.repo.changelog.read(bin(p2))[0]
144 146 if not man.cmp(m1node, man.revision(mnode)):
145 147 self.repo.rollback()
146 148 self.repo.dirstate.clear()
147 149 return parent
148 150 return p2
149 151
150 152 def puttags(self, tags):
151 153 try:
152 154 old = self.repo.wfile(".hgtags").read()
153 155 oldlines = old.splitlines(1)
154 156 oldlines.sort()
155 157 except:
156 158 oldlines = []
157 159
158 160 k = tags.keys()
159 161 k.sort()
160 162 newlines = []
161 163 for tag in k:
162 164 newlines.append("%s %s\n" % (tags[tag], tag))
163 165
164 166 newlines.sort()
165 167
166 168 if newlines != oldlines:
167 169 self.ui.status("updating tags\n")
168 170 f = self.repo.wfile(".hgtags", "w")
169 171 f.write("".join(newlines))
170 172 f.close()
171 173 if not oldlines: self.repo.add([".hgtags"])
172 174 date = "%s 0" % int(time.mktime(time.gmtime()))
173 175 extra = {}
174 176 if self.tagsbranch != 'default':
175 177 extra['branch'] = self.tagsbranch
176 178 try:
177 179 tagparent = self.repo.changectx(self.tagsbranch).node()
178 180 except hg.RepoError, inst:
179 181 tagparent = nullid
180 182 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
181 183 date, tagparent, nullid)
182 184 return hex(self.repo.changelog.tip())
183 185
184 186 def setfilemapmode(self, active):
185 187 self.filemapmode = active
186 188
187 189 class mercurial_source(converter_source):
188 190 def __init__(self, ui, path, rev=None):
189 191 converter_source.__init__(self, ui, path, rev)
190 192 self.saverev = ui.configbool('convert', 'hg.saverev', True)
191 193 try:
192 194 self.repo = hg.repository(self.ui, path)
193 195 # try to provoke an exception if this isn't really a hg
194 196 # repo, but some other bogus compatible-looking url
195 197 if not self.repo.local():
196 198 raise hg.RepoError()
197 199 except hg.RepoError:
198 200 ui.print_exc()
199 201 raise NoRepo("%s is not a local Mercurial repo" % path)
200 202 self.lastrev = None
201 203 self.lastctx = None
202 204 self._changescache = None
203 205 self.convertfp = None
204 206
205 207 def changectx(self, rev):
206 208 if self.lastrev != rev:
207 209 self.lastctx = self.repo.changectx(rev)
208 210 self.lastrev = rev
209 211 return self.lastctx
210 212
211 213 def getheads(self):
212 214 if self.rev:
213 215 return [hex(self.repo.changectx(self.rev).node())]
214 216 else:
215 217 return [hex(node) for node in self.repo.heads()]
216 218
217 219 def getfile(self, name, rev):
218 220 try:
219 221 return self.changectx(rev).filectx(name).data()
220 222 except revlog.LookupError, err:
221 223 raise IOError(err)
222 224
223 225 def getmode(self, name, rev):
224 226 m = self.changectx(rev).manifest()
225 227 return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
226 228
227 229 def getchanges(self, rev):
228 230 ctx = self.changectx(rev)
229 231 if self._changescache and self._changescache[0] == rev:
230 232 m, a, r = self._changescache[1]
231 233 else:
232 234 m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
233 235 changes = [(name, rev) for name in m + a + r]
234 236 changes.sort()
235 237 return (changes, self.getcopies(ctx, m + a))
236 238
237 239 def getcopies(self, ctx, files):
238 240 copies = {}
239 241 for name in files:
240 242 try:
241 243 copies[name] = ctx.filectx(name).renamed()[0]
242 244 except TypeError:
243 245 pass
244 246 return copies
245 247
246 248 def getcommit(self, rev):
247 249 ctx = self.changectx(rev)
248 250 parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
249 251 if self.saverev:
250 252 crev = rev
251 253 else:
252 254 crev = None
253 255 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
254 256 desc=ctx.description(), rev=crev, parents=parents,
255 257 branch=ctx.branch(), extra=ctx.extra())
256 258
257 259 def gettags(self):
258 260 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
259 261 return dict([(name, hex(node)) for name, node in tags])
260 262
261 263 def getchangedfiles(self, rev, i):
262 264 ctx = self.changectx(rev)
263 265 i = i or 0
264 266 changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3]
265 267
266 268 if i == 0:
267 269 self._changescache = (rev, changes)
268 270
269 271 return changes[0] + changes[1] + changes[2]
270 272
271 273 def converted(self, rev, destrev):
272 274 if self.convertfp is None:
273 275 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
274 276 'a')
275 277 self.convertfp.write('%s %s\n' % (destrev, rev))
276 278 self.convertfp.flush()
279
280 def before(self):
281 self.ui.debug(_('run hg source pre-conversion action\n'))
282
283 def after(self):
284 self.ui.debug(_('run hg source post-conversion action\n'))
@@ -1,41 +1,46 b''
1 1 #!/bin/sh
2 2
3 3 cat >> $HGRCPATH <<EOF
4 4 [extensions]
5 5 convert=
6 6 [convert]
7 7 hg.saverev=False
8 8 EOF
9 9
10 10 hg help convert
11 11
12 12 hg init a
13 13 cd a
14 14 echo a > a
15 15 hg ci -d'0 0' -Ama
16 16 hg cp a b
17 17 hg ci -d'1 0' -mb
18 18 hg rm a
19 19 hg ci -d'2 0' -mc
20 20 hg mv b a
21 21 hg ci -d'3 0' -md
22 22 echo a >> a
23 23 hg ci -d'4 0' -me
24 24
25 25 cd ..
26 26 hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
27 27 hg --cwd a-hg pull ../a
28 28
29 29 touch bogusfile
30 30 echo % should fail
31 31 hg convert a bogusfile
32 32
33 33 mkdir bogusdir
34 34 chmod 000 bogusdir
35 35
36 36 echo % should fail
37 37 hg convert a bogusdir
38 38
39 39 echo % should succeed
40 40 chmod 700 bogusdir
41 41 hg convert a bogusdir
42
43 echo % test pre and post conversion actions
44 echo 'include b' > filemap
45 hg convert --debug --filemap filemap a partialb | \
46 grep 'run hg'
@@ -1,114 +1,119 b''
1 1 hg convert [OPTION]... SOURCE [DEST [MAPFILE]]
2 2
3 3 Convert a foreign SCM repository to a Mercurial one.
4 4
5 5 Accepted source formats:
6 6 - Mercurial
7 7 - CVS
8 8 - Darcs
9 9 - git
10 10 - Subversion
11 11
12 12 Accepted destination formats:
13 13 - Mercurial
14 14 - Subversion (history on branches is not preserved)
15 15
16 16 If no revision is given, all revisions will be converted. Otherwise,
17 17 convert will only import up to the named revision (given in a format
18 18 understood by the source).
19 19
20 20 If no destination directory name is specified, it defaults to the
21 21 basename of the source with '-hg' appended. If the destination
22 22 repository doesn't exist, it will be created.
23 23
24 24 If <MAPFILE> isn't given, it will be put in a default location
25 25 (<dest>/.hg/shamap by default). The <MAPFILE> is a simple text
26 26 file that maps each source commit ID to the destination ID for
27 27 that revision, like so:
28 28 <source ID> <destination ID>
29 29
30 30 If the file doesn't exist, it's automatically created. It's updated
31 31 on each commit copied, so convert-repo can be interrupted and can
32 32 be run repeatedly to copy new commits.
33 33
34 34 The [username mapping] file is a simple text file that maps each source
35 35 commit author to a destination commit author. It is handy for source SCMs
36 36 that use unix logins to identify authors (eg: CVS). One line per author
37 37 mapping and the line format is:
38 38 srcauthor=whatever string you want
39 39
40 40 The filemap is a file that allows filtering and remapping of files
41 41 and directories. Comment lines start with '#'. Each line can
42 42 contain one of the following directives:
43 43
44 44 include path/to/file
45 45
46 46 exclude path/to/file
47 47
48 48 rename from/file to/file
49 49
50 50 The 'include' directive causes a file, or all files under a
51 51 directory, to be included in the destination repository, and the
52 52 exclusion of all other files and dirs not explicitely included.
53 53 The 'exclude' directive causes files or directories to be omitted.
54 54 The 'rename' directive renames a file or directory. To rename from a
55 55 subdirectory into the root of the repository, use '.' as the path to
56 56 rename to.
57 57
58 58 Back end options:
59 59
60 60 --config convert.hg.clonebranches=False (boolean)
61 61 hg target: XXX not documented
62 62 --config convert.hg.saverev=True (boolean)
63 63 hg source: allow target to preserve source revision ID
64 64 --config convert.hg.tagsbranch=default (branch name)
65 65 hg target: XXX not documented
66 66 --config convert.hg.usebranchnames=True (boolean)
67 67 hg target: preserve branch names
68 68
69 69 --config convert.svn.branches=branches (directory name)
70 70 svn source: specify the directory containing branches
71 71 --config convert.svn.tags=tags (directory name)
72 72 svn source: specify the directory containing tags
73 73 --config convert.svn.trunk=trunk (directory name)
74 74 svn source: specify the name of the trunk branch
75 75
76 76 options:
77 77
78 78 -A --authors username mapping filename
79 79 -d --dest-type destination repository type
80 80 --filemap remap file names using contents of file
81 81 -r --rev import up to target revision REV
82 82 -s --source-type source repository type
83 83 --datesort try to sort changesets by date
84 84
85 85 use "hg -v help convert" to show global options
86 86 adding a
87 87 assuming destination a-hg
88 88 initializing destination a-hg repository
89 89 scanning source...
90 90 sorting...
91 91 converting...
92 92 4 a
93 93 3 b
94 94 2 c
95 95 1 d
96 96 0 e
97 97 pulling from ../a
98 98 searching for changes
99 99 no changes found
100 100 % should fail
101 101 initializing destination bogusfile repository
102 102 abort: cannot create new bundle repository
103 103 % should fail
104 104 abort: Permission denied: bogusdir
105 105 % should succeed
106 106 initializing destination bogusdir repository
107 107 scanning source...
108 108 sorting...
109 109 converting...
110 110 4 a
111 111 3 b
112 112 2 c
113 113 1 d
114 114 0 e
115 % test pre and post conversion actions
116 run hg source pre-conversion action
117 run hg sink pre-conversion action
118 run hg sink post-conversion action
119 run hg source post-conversion action
General Comments 0
You need to be logged in to leave comments. Login now