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