##// END OF EJS Templates
convert: fix crazy rollback call, broken by recent rollback safety checks...
Matt Mackall -
r15193:a84e3ed6 default
parent child Browse files
Show More
@@ -1,387 +1,387
1 1 # hg.py - hg backend for convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # Notes for hg->hg conversion:
9 9 #
10 10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 11 # of commit messages, but new versions do. Changesets created by
12 12 # those older versions, then converted, may thus have different
13 13 # hashes for changesets that are otherwise identical.
14 14 #
15 15 # * Using "--config convert.hg.saverev=true" will make the source
16 16 # identifier to be stored in the converted revision. This will cause
17 17 # the converted revision to have a different identity than the
18 18 # source.
19 19
20 20
21 21 import os, time, cStringIO
22 22 from mercurial.i18n import _
23 23 from mercurial.node import bin, hex, nullid
24 24 from mercurial import hg, util, context, bookmarks, error
25 25
26 26 from common import NoRepo, commit, converter_source, converter_sink
27 27
28 28 class mercurial_sink(converter_sink):
29 29 def __init__(self, ui, path):
30 30 converter_sink.__init__(self, ui, path)
31 31 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
32 32 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
33 33 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
34 34 self.lastbranch = None
35 35 if os.path.isdir(path) and len(os.listdir(path)) > 0:
36 36 try:
37 37 self.repo = hg.repository(self.ui, path)
38 38 if not self.repo.local():
39 39 raise NoRepo(_('%s is not a local Mercurial repository')
40 40 % path)
41 41 except error.RepoError, err:
42 42 ui.traceback()
43 43 raise NoRepo(err.args[0])
44 44 else:
45 45 try:
46 46 ui.status(_('initializing destination %s repository\n') % path)
47 47 self.repo = hg.repository(self.ui, path, create=True)
48 48 if not self.repo.local():
49 49 raise NoRepo(_('%s is not a local Mercurial repository')
50 50 % path)
51 51 self.created.append(path)
52 52 except error.RepoError:
53 53 ui.traceback()
54 54 raise NoRepo(_("could not create hg repository %s as sink")
55 55 % path)
56 56 self.lock = None
57 57 self.wlock = None
58 58 self.filemapmode = False
59 59
60 60 def before(self):
61 61 self.ui.debug('run hg sink pre-conversion action\n')
62 62 self.wlock = self.repo.wlock()
63 63 self.lock = self.repo.lock()
64 64
65 65 def after(self):
66 66 self.ui.debug('run hg sink post-conversion action\n')
67 67 if self.lock:
68 68 self.lock.release()
69 69 if self.wlock:
70 70 self.wlock.release()
71 71
72 72 def revmapfile(self):
73 73 return self.repo.join("shamap")
74 74
75 75 def authorfile(self):
76 76 return self.repo.join("authormap")
77 77
78 78 def getheads(self):
79 79 h = self.repo.changelog.heads()
80 80 return [hex(x) for x in h]
81 81
82 82 def setbranch(self, branch, pbranches):
83 83 if not self.clonebranches:
84 84 return
85 85
86 86 setbranch = (branch != self.lastbranch)
87 87 self.lastbranch = branch
88 88 if not branch:
89 89 branch = 'default'
90 90 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
91 91 pbranch = pbranches and pbranches[0][1] or 'default'
92 92
93 93 branchpath = os.path.join(self.path, branch)
94 94 if setbranch:
95 95 self.after()
96 96 try:
97 97 self.repo = hg.repository(self.ui, branchpath)
98 98 except:
99 99 self.repo = hg.repository(self.ui, branchpath, create=True)
100 100 self.before()
101 101
102 102 # pbranches may bring revisions from other branches (merge parents)
103 103 # Make sure we have them, or pull them.
104 104 missings = {}
105 105 for b in pbranches:
106 106 try:
107 107 self.repo.lookup(b[0])
108 108 except:
109 109 missings.setdefault(b[1], []).append(b[0])
110 110
111 111 if missings:
112 112 self.after()
113 113 for pbranch, heads in missings.iteritems():
114 114 pbranchpath = os.path.join(self.path, pbranch)
115 115 prepo = hg.peer(self.ui, {}, pbranchpath)
116 116 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
117 117 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
118 118 self.before()
119 119
120 120 def _rewritetags(self, source, revmap, data):
121 121 fp = cStringIO.StringIO()
122 122 for line in data.splitlines():
123 123 s = line.split(' ', 1)
124 124 if len(s) != 2:
125 125 continue
126 126 revid = revmap.get(source.lookuprev(s[0]))
127 127 if not revid:
128 128 continue
129 129 fp.write('%s %s\n' % (revid, s[1]))
130 130 return fp.getvalue()
131 131
132 132 def putcommit(self, files, copies, parents, commit, source, revmap):
133 133
134 134 files = dict(files)
135 135 def getfilectx(repo, memctx, f):
136 136 v = files[f]
137 137 data, mode = source.getfile(f, v)
138 138 if f == '.hgtags':
139 139 data = self._rewritetags(source, revmap, data)
140 140 return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
141 141 copies.get(f))
142 142
143 143 pl = []
144 144 for p in parents:
145 145 if p not in pl:
146 146 pl.append(p)
147 147 parents = pl
148 148 nparents = len(parents)
149 149 if self.filemapmode and nparents == 1:
150 150 m1node = self.repo.changelog.read(bin(parents[0]))[0]
151 151 parent = parents[0]
152 152
153 153 if len(parents) < 2:
154 154 parents.append(nullid)
155 155 if len(parents) < 2:
156 156 parents.append(nullid)
157 157 p2 = parents.pop(0)
158 158
159 159 text = commit.desc
160 160 extra = commit.extra.copy()
161 161 if self.branchnames and commit.branch:
162 162 extra['branch'] = commit.branch
163 163 if commit.rev:
164 164 extra['convert_revision'] = commit.rev
165 165
166 166 while parents:
167 167 p1 = p2
168 168 p2 = parents.pop(0)
169 169 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
170 170 getfilectx, commit.author, commit.date, extra)
171 171 self.repo.commitctx(ctx)
172 172 text = "(octopus merge fixup)\n"
173 173 p2 = hex(self.repo.changelog.tip())
174 174
175 175 if self.filemapmode and nparents == 1:
176 176 man = self.repo.manifest
177 177 mnode = self.repo.changelog.read(bin(p2))[0]
178 178 closed = 'close' in commit.extra
179 179 if not closed and not man.cmp(m1node, man.revision(mnode)):
180 180 self.ui.status(_("filtering out empty revision\n"))
181 self.repo.rollback()
181 self.repo.rollback(force=True)
182 182 return parent
183 183 return p2
184 184
185 185 def puttags(self, tags):
186 186 try:
187 187 parentctx = self.repo[self.tagsbranch]
188 188 tagparent = parentctx.node()
189 189 except error.RepoError:
190 190 parentctx = None
191 191 tagparent = nullid
192 192
193 193 try:
194 194 oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
195 195 except:
196 196 oldlines = []
197 197
198 198 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
199 199 if newlines == oldlines:
200 200 return None, None
201 201 data = "".join(newlines)
202 202 def getfilectx(repo, memctx, f):
203 203 return context.memfilectx(f, data, False, False, None)
204 204
205 205 self.ui.status(_("updating tags\n"))
206 206 date = "%s 0" % int(time.mktime(time.gmtime()))
207 207 extra = {'branch': self.tagsbranch}
208 208 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
209 209 [".hgtags"], getfilectx, "convert-repo", date,
210 210 extra)
211 211 self.repo.commitctx(ctx)
212 212 return hex(self.repo.changelog.tip()), hex(tagparent)
213 213
214 214 def setfilemapmode(self, active):
215 215 self.filemapmode = active
216 216
217 217 def putbookmarks(self, updatedbookmark):
218 218 if not len(updatedbookmark):
219 219 return
220 220
221 221 self.ui.status(_("updating bookmarks\n"))
222 222 for bookmark in updatedbookmark:
223 223 self.repo._bookmarks[bookmark] = bin(updatedbookmark[bookmark])
224 224 bookmarks.write(self.repo)
225 225
226 226
227 227 class mercurial_source(converter_source):
228 228 def __init__(self, ui, path, rev=None):
229 229 converter_source.__init__(self, ui, path, rev)
230 230 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
231 231 self.ignored = set()
232 232 self.saverev = ui.configbool('convert', 'hg.saverev', False)
233 233 try:
234 234 self.repo = hg.repository(self.ui, path)
235 235 # try to provoke an exception if this isn't really a hg
236 236 # repo, but some other bogus compatible-looking url
237 237 if not self.repo.local():
238 238 raise error.RepoError()
239 239 except error.RepoError:
240 240 ui.traceback()
241 241 raise NoRepo(_("%s is not a local Mercurial repository") % path)
242 242 self.lastrev = None
243 243 self.lastctx = None
244 244 self._changescache = None
245 245 self.convertfp = None
246 246 # Restrict converted revisions to startrev descendants
247 247 startnode = ui.config('convert', 'hg.startrev')
248 248 if startnode is not None:
249 249 try:
250 250 startnode = self.repo.lookup(startnode)
251 251 except error.RepoError:
252 252 raise util.Abort(_('%s is not a valid start revision')
253 253 % startnode)
254 254 startrev = self.repo.changelog.rev(startnode)
255 255 children = {startnode: 1}
256 256 for rev in self.repo.changelog.descendants(startrev):
257 257 children[self.repo.changelog.node(rev)] = 1
258 258 self.keep = children.__contains__
259 259 else:
260 260 self.keep = util.always
261 261
262 262 def changectx(self, rev):
263 263 if self.lastrev != rev:
264 264 self.lastctx = self.repo[rev]
265 265 self.lastrev = rev
266 266 return self.lastctx
267 267
268 268 def parents(self, ctx):
269 269 return [p for p in ctx.parents() if p and self.keep(p.node())]
270 270
271 271 def getheads(self):
272 272 if self.rev:
273 273 heads = [self.repo[self.rev].node()]
274 274 else:
275 275 heads = self.repo.heads()
276 276 return [hex(h) for h in heads if self.keep(h)]
277 277
278 278 def getfile(self, name, rev):
279 279 try:
280 280 fctx = self.changectx(rev)[name]
281 281 return fctx.data(), fctx.flags()
282 282 except error.LookupError, err:
283 283 raise IOError(err)
284 284
285 285 def getchanges(self, rev):
286 286 ctx = self.changectx(rev)
287 287 parents = self.parents(ctx)
288 288 if not parents:
289 289 files = sorted(ctx.manifest())
290 290 # getcopies() is not needed for roots, but it is a simple way to
291 291 # detect missing revlogs and abort on errors or populate self.ignored
292 292 self.getcopies(ctx, parents, files)
293 293 return [(f, rev) for f in files if f not in self.ignored], {}
294 294 if self._changescache and self._changescache[0] == rev:
295 295 m, a, r = self._changescache[1]
296 296 else:
297 297 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
298 298 # getcopies() detects missing revlogs early, run it before
299 299 # filtering the changes.
300 300 copies = self.getcopies(ctx, parents, m + a)
301 301 changes = [(name, rev) for name in m + a + r
302 302 if name not in self.ignored]
303 303 return sorted(changes), copies
304 304
305 305 def getcopies(self, ctx, parents, files):
306 306 copies = {}
307 307 for name in files:
308 308 if name in self.ignored:
309 309 continue
310 310 try:
311 311 copysource, copynode = ctx.filectx(name).renamed()
312 312 if copysource in self.ignored or not self.keep(copynode):
313 313 continue
314 314 # Ignore copy sources not in parent revisions
315 315 found = False
316 316 for p in parents:
317 317 if copysource in p:
318 318 found = True
319 319 break
320 320 if not found:
321 321 continue
322 322 copies[name] = copysource
323 323 except TypeError:
324 324 pass
325 325 except error.LookupError, e:
326 326 if not self.ignoreerrors:
327 327 raise
328 328 self.ignored.add(name)
329 329 self.ui.warn(_('ignoring: %s\n') % e)
330 330 return copies
331 331
332 332 def getcommit(self, rev):
333 333 ctx = self.changectx(rev)
334 334 parents = [p.hex() for p in self.parents(ctx)]
335 335 if self.saverev:
336 336 crev = rev
337 337 else:
338 338 crev = None
339 339 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
340 340 desc=ctx.description(), rev=crev, parents=parents,
341 341 branch=ctx.branch(), extra=ctx.extra(),
342 342 sortkey=ctx.rev())
343 343
344 344 def gettags(self):
345 345 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
346 346 return dict([(name, hex(node)) for name, node in tags
347 347 if self.keep(node)])
348 348
349 349 def getchangedfiles(self, rev, i):
350 350 ctx = self.changectx(rev)
351 351 parents = self.parents(ctx)
352 352 if not parents and i is None:
353 353 i = 0
354 354 changes = [], ctx.manifest().keys(), []
355 355 else:
356 356 i = i or 0
357 357 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
358 358 changes = [[f for f in l if f not in self.ignored] for l in changes]
359 359
360 360 if i == 0:
361 361 self._changescache = (rev, changes)
362 362
363 363 return changes[0] + changes[1] + changes[2]
364 364
365 365 def converted(self, rev, destrev):
366 366 if self.convertfp is None:
367 367 self.convertfp = open(self.repo.join('shamap'), 'a')
368 368 self.convertfp.write('%s %s\n' % (destrev, rev))
369 369 self.convertfp.flush()
370 370
371 371 def before(self):
372 372 self.ui.debug('run hg source pre-conversion action\n')
373 373
374 374 def after(self):
375 375 self.ui.debug('run hg source post-conversion action\n')
376 376
377 377 def hasnativeorder(self):
378 378 return True
379 379
380 380 def lookuprev(self, rev):
381 381 try:
382 382 return hex(self.repo.lookup(rev))
383 383 except error.RepoError:
384 384 return None
385 385
386 386 def getbookmarks(self):
387 387 return bookmarks.listbookmarks(self.repo)
General Comments 0
You need to be logged in to leave comments. Login now