##// END OF EJS Templates
convert: use the new 'phase.registernew' function
Boris Feld -
r33455:b1bc8cf3 default
parent child Browse files
Show More
@@ -1,655 +1,655 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 or any later version.
6 # GNU General Public License version 2 or any later version.
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 # * Using "--config convert.hg.saverev=true" will make the source
15 # * Using "--config convert.hg.saverev=true" will make the source
16 # identifier to be stored in the converted revision. This will cause
16 # identifier to be stored in the converted revision. This will cause
17 # the converted revision to have a different identity than the
17 # the converted revision to have a different identity than the
18 # source.
18 # source.
19 from __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 import os
21 import os
22 import re
22 import re
23 import time
23 import time
24
24
25 from mercurial.i18n import _
25 from mercurial.i18n import _
26 from mercurial import (
26 from mercurial import (
27 bookmarks,
27 bookmarks,
28 context,
28 context,
29 error,
29 error,
30 exchange,
30 exchange,
31 hg,
31 hg,
32 lock as lockmod,
32 lock as lockmod,
33 merge as mergemod,
33 merge as mergemod,
34 node as nodemod,
34 node as nodemod,
35 phases,
35 phases,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 )
38 )
39 stringio = util.stringio
39 stringio = util.stringio
40
40
41 from . import common
41 from . import common
42 mapfile = common.mapfile
42 mapfile = common.mapfile
43 NoRepo = common.NoRepo
43 NoRepo = common.NoRepo
44
44
45 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
45 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
46
46
47 class mercurial_sink(common.converter_sink):
47 class mercurial_sink(common.converter_sink):
48 def __init__(self, ui, path):
48 def __init__(self, ui, path):
49 common.converter_sink.__init__(self, ui, path)
49 common.converter_sink.__init__(self, ui, path)
50 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
50 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
51 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
51 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
52 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
52 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
53 self.lastbranch = None
53 self.lastbranch = None
54 if os.path.isdir(path) and len(os.listdir(path)) > 0:
54 if os.path.isdir(path) and len(os.listdir(path)) > 0:
55 try:
55 try:
56 self.repo = hg.repository(self.ui, path)
56 self.repo = hg.repository(self.ui, path)
57 if not self.repo.local():
57 if not self.repo.local():
58 raise NoRepo(_('%s is not a local Mercurial repository')
58 raise NoRepo(_('%s is not a local Mercurial repository')
59 % path)
59 % path)
60 except error.RepoError as err:
60 except error.RepoError as err:
61 ui.traceback()
61 ui.traceback()
62 raise NoRepo(err.args[0])
62 raise NoRepo(err.args[0])
63 else:
63 else:
64 try:
64 try:
65 ui.status(_('initializing destination %s repository\n') % path)
65 ui.status(_('initializing destination %s repository\n') % path)
66 self.repo = hg.repository(self.ui, path, create=True)
66 self.repo = hg.repository(self.ui, path, create=True)
67 if not self.repo.local():
67 if not self.repo.local():
68 raise NoRepo(_('%s is not a local Mercurial repository')
68 raise NoRepo(_('%s is not a local Mercurial repository')
69 % path)
69 % path)
70 self.created.append(path)
70 self.created.append(path)
71 except error.RepoError:
71 except error.RepoError:
72 ui.traceback()
72 ui.traceback()
73 raise NoRepo(_("could not create hg repository %s as sink")
73 raise NoRepo(_("could not create hg repository %s as sink")
74 % path)
74 % path)
75 self.lock = None
75 self.lock = None
76 self.wlock = None
76 self.wlock = None
77 self.filemapmode = False
77 self.filemapmode = False
78 self.subrevmaps = {}
78 self.subrevmaps = {}
79
79
80 def before(self):
80 def before(self):
81 self.ui.debug('run hg sink pre-conversion action\n')
81 self.ui.debug('run hg sink pre-conversion action\n')
82 self.wlock = self.repo.wlock()
82 self.wlock = self.repo.wlock()
83 self.lock = self.repo.lock()
83 self.lock = self.repo.lock()
84
84
85 def after(self):
85 def after(self):
86 self.ui.debug('run hg sink post-conversion action\n')
86 self.ui.debug('run hg sink post-conversion action\n')
87 if self.lock:
87 if self.lock:
88 self.lock.release()
88 self.lock.release()
89 if self.wlock:
89 if self.wlock:
90 self.wlock.release()
90 self.wlock.release()
91
91
92 def revmapfile(self):
92 def revmapfile(self):
93 return self.repo.vfs.join("shamap")
93 return self.repo.vfs.join("shamap")
94
94
95 def authorfile(self):
95 def authorfile(self):
96 return self.repo.vfs.join("authormap")
96 return self.repo.vfs.join("authormap")
97
97
98 def setbranch(self, branch, pbranches):
98 def setbranch(self, branch, pbranches):
99 if not self.clonebranches:
99 if not self.clonebranches:
100 return
100 return
101
101
102 setbranch = (branch != self.lastbranch)
102 setbranch = (branch != self.lastbranch)
103 self.lastbranch = branch
103 self.lastbranch = branch
104 if not branch:
104 if not branch:
105 branch = 'default'
105 branch = 'default'
106 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
106 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
107 if pbranches:
107 if pbranches:
108 pbranch = pbranches[0][1]
108 pbranch = pbranches[0][1]
109 else:
109 else:
110 pbranch = 'default'
110 pbranch = 'default'
111
111
112 branchpath = os.path.join(self.path, branch)
112 branchpath = os.path.join(self.path, branch)
113 if setbranch:
113 if setbranch:
114 self.after()
114 self.after()
115 try:
115 try:
116 self.repo = hg.repository(self.ui, branchpath)
116 self.repo = hg.repository(self.ui, branchpath)
117 except Exception:
117 except Exception:
118 self.repo = hg.repository(self.ui, branchpath, create=True)
118 self.repo = hg.repository(self.ui, branchpath, create=True)
119 self.before()
119 self.before()
120
120
121 # pbranches may bring revisions from other branches (merge parents)
121 # pbranches may bring revisions from other branches (merge parents)
122 # Make sure we have them, or pull them.
122 # Make sure we have them, or pull them.
123 missings = {}
123 missings = {}
124 for b in pbranches:
124 for b in pbranches:
125 try:
125 try:
126 self.repo.lookup(b[0])
126 self.repo.lookup(b[0])
127 except Exception:
127 except Exception:
128 missings.setdefault(b[1], []).append(b[0])
128 missings.setdefault(b[1], []).append(b[0])
129
129
130 if missings:
130 if missings:
131 self.after()
131 self.after()
132 for pbranch, heads in sorted(missings.iteritems()):
132 for pbranch, heads in sorted(missings.iteritems()):
133 pbranchpath = os.path.join(self.path, pbranch)
133 pbranchpath = os.path.join(self.path, pbranch)
134 prepo = hg.peer(self.ui, {}, pbranchpath)
134 prepo = hg.peer(self.ui, {}, pbranchpath)
135 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
135 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
136 exchange.pull(self.repo, prepo,
136 exchange.pull(self.repo, prepo,
137 [prepo.lookup(h) for h in heads])
137 [prepo.lookup(h) for h in heads])
138 self.before()
138 self.before()
139
139
140 def _rewritetags(self, source, revmap, data):
140 def _rewritetags(self, source, revmap, data):
141 fp = stringio()
141 fp = stringio()
142 for line in data.splitlines():
142 for line in data.splitlines():
143 s = line.split(' ', 1)
143 s = line.split(' ', 1)
144 if len(s) != 2:
144 if len(s) != 2:
145 continue
145 continue
146 revid = revmap.get(source.lookuprev(s[0]))
146 revid = revmap.get(source.lookuprev(s[0]))
147 if not revid:
147 if not revid:
148 if s[0] == nodemod.nullhex:
148 if s[0] == nodemod.nullhex:
149 revid = s[0]
149 revid = s[0]
150 else:
150 else:
151 continue
151 continue
152 fp.write('%s %s\n' % (revid, s[1]))
152 fp.write('%s %s\n' % (revid, s[1]))
153 return fp.getvalue()
153 return fp.getvalue()
154
154
155 def _rewritesubstate(self, source, data):
155 def _rewritesubstate(self, source, data):
156 fp = stringio()
156 fp = stringio()
157 for line in data.splitlines():
157 for line in data.splitlines():
158 s = line.split(' ', 1)
158 s = line.split(' ', 1)
159 if len(s) != 2:
159 if len(s) != 2:
160 continue
160 continue
161
161
162 revid = s[0]
162 revid = s[0]
163 subpath = s[1]
163 subpath = s[1]
164 if revid != nodemod.nullhex:
164 if revid != nodemod.nullhex:
165 revmap = self.subrevmaps.get(subpath)
165 revmap = self.subrevmaps.get(subpath)
166 if revmap is None:
166 if revmap is None:
167 revmap = mapfile(self.ui,
167 revmap = mapfile(self.ui,
168 self.repo.wjoin(subpath, '.hg/shamap'))
168 self.repo.wjoin(subpath, '.hg/shamap'))
169 self.subrevmaps[subpath] = revmap
169 self.subrevmaps[subpath] = revmap
170
170
171 # It is reasonable that one or more of the subrepos don't
171 # It is reasonable that one or more of the subrepos don't
172 # need to be converted, in which case they can be cloned
172 # need to be converted, in which case they can be cloned
173 # into place instead of converted. Therefore, only warn
173 # into place instead of converted. Therefore, only warn
174 # once.
174 # once.
175 msg = _('no ".hgsubstate" updates will be made for "%s"\n')
175 msg = _('no ".hgsubstate" updates will be made for "%s"\n')
176 if len(revmap) == 0:
176 if len(revmap) == 0:
177 sub = self.repo.wvfs.reljoin(subpath, '.hg')
177 sub = self.repo.wvfs.reljoin(subpath, '.hg')
178
178
179 if self.repo.wvfs.exists(sub):
179 if self.repo.wvfs.exists(sub):
180 self.ui.warn(msg % subpath)
180 self.ui.warn(msg % subpath)
181
181
182 newid = revmap.get(revid)
182 newid = revmap.get(revid)
183 if not newid:
183 if not newid:
184 if len(revmap) > 0:
184 if len(revmap) > 0:
185 self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
185 self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
186 (revid, subpath))
186 (revid, subpath))
187 else:
187 else:
188 revid = newid
188 revid = newid
189
189
190 fp.write('%s %s\n' % (revid, subpath))
190 fp.write('%s %s\n' % (revid, subpath))
191
191
192 return fp.getvalue()
192 return fp.getvalue()
193
193
194 def _calculatemergedfiles(self, source, p1ctx, p2ctx):
194 def _calculatemergedfiles(self, source, p1ctx, p2ctx):
195 """Calculates the files from p2 that we need to pull in when merging p1
195 """Calculates the files from p2 that we need to pull in when merging p1
196 and p2, given that the merge is coming from the given source.
196 and p2, given that the merge is coming from the given source.
197
197
198 This prevents us from losing files that only exist in the target p2 and
198 This prevents us from losing files that only exist in the target p2 and
199 that don't come from the source repo (like if you're merging multiple
199 that don't come from the source repo (like if you're merging multiple
200 repositories together).
200 repositories together).
201 """
201 """
202 anc = [p1ctx.ancestor(p2ctx)]
202 anc = [p1ctx.ancestor(p2ctx)]
203 # Calculate what files are coming from p2
203 # Calculate what files are coming from p2
204 actions, diverge, rename = mergemod.calculateupdates(
204 actions, diverge, rename = mergemod.calculateupdates(
205 self.repo, p1ctx, p2ctx, anc,
205 self.repo, p1ctx, p2ctx, anc,
206 True, # branchmerge
206 True, # branchmerge
207 True, # force
207 True, # force
208 False, # acceptremote
208 False, # acceptremote
209 False, # followcopies
209 False, # followcopies
210 )
210 )
211
211
212 for file, (action, info, msg) in actions.iteritems():
212 for file, (action, info, msg) in actions.iteritems():
213 if source.targetfilebelongstosource(file):
213 if source.targetfilebelongstosource(file):
214 # If the file belongs to the source repo, ignore the p2
214 # If the file belongs to the source repo, ignore the p2
215 # since it will be covered by the existing fileset.
215 # since it will be covered by the existing fileset.
216 continue
216 continue
217
217
218 # If the file requires actual merging, abort. We don't have enough
218 # If the file requires actual merging, abort. We don't have enough
219 # context to resolve merges correctly.
219 # context to resolve merges correctly.
220 if action in ['m', 'dm', 'cd', 'dc']:
220 if action in ['m', 'dm', 'cd', 'dc']:
221 raise error.Abort(_("unable to convert merge commit "
221 raise error.Abort(_("unable to convert merge commit "
222 "since target parents do not merge cleanly (file "
222 "since target parents do not merge cleanly (file "
223 "%s, parents %s and %s)") % (file, p1ctx,
223 "%s, parents %s and %s)") % (file, p1ctx,
224 p2ctx))
224 p2ctx))
225 elif action == 'k':
225 elif action == 'k':
226 # 'keep' means nothing changed from p1
226 # 'keep' means nothing changed from p1
227 continue
227 continue
228 else:
228 else:
229 # Any other change means we want to take the p2 version
229 # Any other change means we want to take the p2 version
230 yield file
230 yield file
231
231
232 def putcommit(self, files, copies, parents, commit, source, revmap, full,
232 def putcommit(self, files, copies, parents, commit, source, revmap, full,
233 cleanp2):
233 cleanp2):
234 files = dict(files)
234 files = dict(files)
235
235
236 def getfilectx(repo, memctx, f):
236 def getfilectx(repo, memctx, f):
237 if p2ctx and f in p2files and f not in copies:
237 if p2ctx and f in p2files and f not in copies:
238 self.ui.debug('reusing %s from p2\n' % f)
238 self.ui.debug('reusing %s from p2\n' % f)
239 try:
239 try:
240 return p2ctx[f]
240 return p2ctx[f]
241 except error.ManifestLookupError:
241 except error.ManifestLookupError:
242 # If the file doesn't exist in p2, then we're syncing a
242 # If the file doesn't exist in p2, then we're syncing a
243 # delete, so just return None.
243 # delete, so just return None.
244 return None
244 return None
245 try:
245 try:
246 v = files[f]
246 v = files[f]
247 except KeyError:
247 except KeyError:
248 return None
248 return None
249 data, mode = source.getfile(f, v)
249 data, mode = source.getfile(f, v)
250 if data is None:
250 if data is None:
251 return None
251 return None
252 if f == '.hgtags':
252 if f == '.hgtags':
253 data = self._rewritetags(source, revmap, data)
253 data = self._rewritetags(source, revmap, data)
254 if f == '.hgsubstate':
254 if f == '.hgsubstate':
255 data = self._rewritesubstate(source, data)
255 data = self._rewritesubstate(source, data)
256 return context.memfilectx(self.repo, f, data, 'l' in mode,
256 return context.memfilectx(self.repo, f, data, 'l' in mode,
257 'x' in mode, copies.get(f))
257 'x' in mode, copies.get(f))
258
258
259 pl = []
259 pl = []
260 for p in parents:
260 for p in parents:
261 if p not in pl:
261 if p not in pl:
262 pl.append(p)
262 pl.append(p)
263 parents = pl
263 parents = pl
264 nparents = len(parents)
264 nparents = len(parents)
265 if self.filemapmode and nparents == 1:
265 if self.filemapmode and nparents == 1:
266 m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
266 m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
267 parent = parents[0]
267 parent = parents[0]
268
268
269 if len(parents) < 2:
269 if len(parents) < 2:
270 parents.append(nodemod.nullid)
270 parents.append(nodemod.nullid)
271 if len(parents) < 2:
271 if len(parents) < 2:
272 parents.append(nodemod.nullid)
272 parents.append(nodemod.nullid)
273 p2 = parents.pop(0)
273 p2 = parents.pop(0)
274
274
275 text = commit.desc
275 text = commit.desc
276
276
277 sha1s = re.findall(sha1re, text)
277 sha1s = re.findall(sha1re, text)
278 for sha1 in sha1s:
278 for sha1 in sha1s:
279 oldrev = source.lookuprev(sha1)
279 oldrev = source.lookuprev(sha1)
280 newrev = revmap.get(oldrev)
280 newrev = revmap.get(oldrev)
281 if newrev is not None:
281 if newrev is not None:
282 text = text.replace(sha1, newrev[:len(sha1)])
282 text = text.replace(sha1, newrev[:len(sha1)])
283
283
284 extra = commit.extra.copy()
284 extra = commit.extra.copy()
285
285
286 sourcename = self.repo.ui.config('convert', 'hg.sourcename')
286 sourcename = self.repo.ui.config('convert', 'hg.sourcename')
287 if sourcename:
287 if sourcename:
288 extra['convert_source'] = sourcename
288 extra['convert_source'] = sourcename
289
289
290 for label in ('source', 'transplant_source', 'rebase_source',
290 for label in ('source', 'transplant_source', 'rebase_source',
291 'intermediate-source'):
291 'intermediate-source'):
292 node = extra.get(label)
292 node = extra.get(label)
293
293
294 if node is None:
294 if node is None:
295 continue
295 continue
296
296
297 # Only transplant stores its reference in binary
297 # Only transplant stores its reference in binary
298 if label == 'transplant_source':
298 if label == 'transplant_source':
299 node = nodemod.hex(node)
299 node = nodemod.hex(node)
300
300
301 newrev = revmap.get(node)
301 newrev = revmap.get(node)
302 if newrev is not None:
302 if newrev is not None:
303 if label == 'transplant_source':
303 if label == 'transplant_source':
304 newrev = nodemod.bin(newrev)
304 newrev = nodemod.bin(newrev)
305
305
306 extra[label] = newrev
306 extra[label] = newrev
307
307
308 if self.branchnames and commit.branch:
308 if self.branchnames and commit.branch:
309 extra['branch'] = commit.branch
309 extra['branch'] = commit.branch
310 if commit.rev and commit.saverev:
310 if commit.rev and commit.saverev:
311 extra['convert_revision'] = commit.rev
311 extra['convert_revision'] = commit.rev
312
312
313 while parents:
313 while parents:
314 p1 = p2
314 p1 = p2
315 p2 = parents.pop(0)
315 p2 = parents.pop(0)
316 p1ctx = self.repo[p1]
316 p1ctx = self.repo[p1]
317 p2ctx = None
317 p2ctx = None
318 if p2 != nodemod.nullid:
318 if p2 != nodemod.nullid:
319 p2ctx = self.repo[p2]
319 p2ctx = self.repo[p2]
320 fileset = set(files)
320 fileset = set(files)
321 if full:
321 if full:
322 fileset.update(self.repo[p1])
322 fileset.update(self.repo[p1])
323 fileset.update(self.repo[p2])
323 fileset.update(self.repo[p2])
324
324
325 if p2ctx:
325 if p2ctx:
326 p2files = set(cleanp2)
326 p2files = set(cleanp2)
327 for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
327 for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
328 p2files.add(file)
328 p2files.add(file)
329 fileset.add(file)
329 fileset.add(file)
330
330
331 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
331 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
332 getfilectx, commit.author, commit.date, extra)
332 getfilectx, commit.author, commit.date, extra)
333
333
334 # We won't know if the conversion changes the node until after the
334 # We won't know if the conversion changes the node until after the
335 # commit, so copy the source's phase for now.
335 # commit, so copy the source's phase for now.
336 self.repo.ui.setconfig('phases', 'new-commit',
336 self.repo.ui.setconfig('phases', 'new-commit',
337 phases.phasenames[commit.phase], 'convert')
337 phases.phasenames[commit.phase], 'convert')
338
338
339 with self.repo.transaction("convert") as tr:
339 with self.repo.transaction("convert") as tr:
340 node = nodemod.hex(self.repo.commitctx(ctx))
340 node = nodemod.hex(self.repo.commitctx(ctx))
341
341
342 # If the node value has changed, but the phase is lower than
342 # If the node value has changed, but the phase is lower than
343 # draft, set it back to draft since it hasn't been exposed
343 # draft, set it back to draft since it hasn't been exposed
344 # anywhere.
344 # anywhere.
345 if commit.rev != node:
345 if commit.rev != node:
346 ctx = self.repo[node]
346 ctx = self.repo[node]
347 if ctx.phase() < phases.draft:
347 if ctx.phase() < phases.draft:
348 phases.retractboundary(self.repo, tr, phases.draft,
348 phases.registernew(self.repo, tr, phases.draft,
349 [ctx.node()])
349 [ctx.node()])
350
350
351 text = "(octopus merge fixup)\n"
351 text = "(octopus merge fixup)\n"
352 p2 = node
352 p2 = node
353
353
354 if self.filemapmode and nparents == 1:
354 if self.filemapmode and nparents == 1:
355 man = self.repo.manifestlog._revlog
355 man = self.repo.manifestlog._revlog
356 mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
356 mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
357 closed = 'close' in commit.extra
357 closed = 'close' in commit.extra
358 if not closed and not man.cmp(m1node, man.revision(mnode)):
358 if not closed and not man.cmp(m1node, man.revision(mnode)):
359 self.ui.status(_("filtering out empty revision\n"))
359 self.ui.status(_("filtering out empty revision\n"))
360 self.repo.rollback(force=True)
360 self.repo.rollback(force=True)
361 return parent
361 return parent
362 return p2
362 return p2
363
363
364 def puttags(self, tags):
364 def puttags(self, tags):
365 try:
365 try:
366 parentctx = self.repo[self.tagsbranch]
366 parentctx = self.repo[self.tagsbranch]
367 tagparent = parentctx.node()
367 tagparent = parentctx.node()
368 except error.RepoError:
368 except error.RepoError:
369 parentctx = None
369 parentctx = None
370 tagparent = nodemod.nullid
370 tagparent = nodemod.nullid
371
371
372 oldlines = set()
372 oldlines = set()
373 for branch, heads in self.repo.branchmap().iteritems():
373 for branch, heads in self.repo.branchmap().iteritems():
374 for h in heads:
374 for h in heads:
375 if '.hgtags' in self.repo[h]:
375 if '.hgtags' in self.repo[h]:
376 oldlines.update(
376 oldlines.update(
377 set(self.repo[h]['.hgtags'].data().splitlines(True)))
377 set(self.repo[h]['.hgtags'].data().splitlines(True)))
378 oldlines = sorted(list(oldlines))
378 oldlines = sorted(list(oldlines))
379
379
380 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
380 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
381 if newlines == oldlines:
381 if newlines == oldlines:
382 return None, None
382 return None, None
383
383
384 # if the old and new tags match, then there is nothing to update
384 # if the old and new tags match, then there is nothing to update
385 oldtags = set()
385 oldtags = set()
386 newtags = set()
386 newtags = set()
387 for line in oldlines:
387 for line in oldlines:
388 s = line.strip().split(' ', 1)
388 s = line.strip().split(' ', 1)
389 if len(s) != 2:
389 if len(s) != 2:
390 continue
390 continue
391 oldtags.add(s[1])
391 oldtags.add(s[1])
392 for line in newlines:
392 for line in newlines:
393 s = line.strip().split(' ', 1)
393 s = line.strip().split(' ', 1)
394 if len(s) != 2:
394 if len(s) != 2:
395 continue
395 continue
396 if s[1] not in oldtags:
396 if s[1] not in oldtags:
397 newtags.add(s[1].strip())
397 newtags.add(s[1].strip())
398
398
399 if not newtags:
399 if not newtags:
400 return None, None
400 return None, None
401
401
402 data = "".join(newlines)
402 data = "".join(newlines)
403 def getfilectx(repo, memctx, f):
403 def getfilectx(repo, memctx, f):
404 return context.memfilectx(repo, f, data, False, False, None)
404 return context.memfilectx(repo, f, data, False, False, None)
405
405
406 self.ui.status(_("updating tags\n"))
406 self.ui.status(_("updating tags\n"))
407 date = "%s 0" % int(time.mktime(time.gmtime()))
407 date = "%s 0" % int(time.mktime(time.gmtime()))
408 extra = {'branch': self.tagsbranch}
408 extra = {'branch': self.tagsbranch}
409 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
409 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
410 [".hgtags"], getfilectx, "convert-repo", date,
410 [".hgtags"], getfilectx, "convert-repo", date,
411 extra)
411 extra)
412 node = self.repo.commitctx(ctx)
412 node = self.repo.commitctx(ctx)
413 return nodemod.hex(node), nodemod.hex(tagparent)
413 return nodemod.hex(node), nodemod.hex(tagparent)
414
414
415 def setfilemapmode(self, active):
415 def setfilemapmode(self, active):
416 self.filemapmode = active
416 self.filemapmode = active
417
417
418 def putbookmarks(self, updatedbookmark):
418 def putbookmarks(self, updatedbookmark):
419 if not len(updatedbookmark):
419 if not len(updatedbookmark):
420 return
420 return
421 wlock = lock = tr = None
421 wlock = lock = tr = None
422 try:
422 try:
423 wlock = self.repo.wlock()
423 wlock = self.repo.wlock()
424 lock = self.repo.lock()
424 lock = self.repo.lock()
425 tr = self.repo.transaction('bookmark')
425 tr = self.repo.transaction('bookmark')
426 self.ui.status(_("updating bookmarks\n"))
426 self.ui.status(_("updating bookmarks\n"))
427 destmarks = self.repo._bookmarks
427 destmarks = self.repo._bookmarks
428 for bookmark in updatedbookmark:
428 for bookmark in updatedbookmark:
429 destmarks[bookmark] = nodemod.bin(updatedbookmark[bookmark])
429 destmarks[bookmark] = nodemod.bin(updatedbookmark[bookmark])
430 destmarks.recordchange(tr)
430 destmarks.recordchange(tr)
431 tr.close()
431 tr.close()
432 finally:
432 finally:
433 lockmod.release(lock, wlock, tr)
433 lockmod.release(lock, wlock, tr)
434
434
435 def hascommitfrommap(self, rev):
435 def hascommitfrommap(self, rev):
436 # the exact semantics of clonebranches is unclear so we can't say no
436 # the exact semantics of clonebranches is unclear so we can't say no
437 return rev in self.repo or self.clonebranches
437 return rev in self.repo or self.clonebranches
438
438
439 def hascommitforsplicemap(self, rev):
439 def hascommitforsplicemap(self, rev):
440 if rev not in self.repo and self.clonebranches:
440 if rev not in self.repo and self.clonebranches:
441 raise error.Abort(_('revision %s not found in destination '
441 raise error.Abort(_('revision %s not found in destination '
442 'repository (lookups with clonebranches=true '
442 'repository (lookups with clonebranches=true '
443 'are not implemented)') % rev)
443 'are not implemented)') % rev)
444 return rev in self.repo
444 return rev in self.repo
445
445
446 class mercurial_source(common.converter_source):
446 class mercurial_source(common.converter_source):
447 def __init__(self, ui, path, revs=None):
447 def __init__(self, ui, path, revs=None):
448 common.converter_source.__init__(self, ui, path, revs)
448 common.converter_source.__init__(self, ui, path, revs)
449 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
449 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
450 self.ignored = set()
450 self.ignored = set()
451 self.saverev = ui.configbool('convert', 'hg.saverev', False)
451 self.saverev = ui.configbool('convert', 'hg.saverev', False)
452 try:
452 try:
453 self.repo = hg.repository(self.ui, path)
453 self.repo = hg.repository(self.ui, path)
454 # try to provoke an exception if this isn't really a hg
454 # try to provoke an exception if this isn't really a hg
455 # repo, but some other bogus compatible-looking url
455 # repo, but some other bogus compatible-looking url
456 if not self.repo.local():
456 if not self.repo.local():
457 raise error.RepoError
457 raise error.RepoError
458 except error.RepoError:
458 except error.RepoError:
459 ui.traceback()
459 ui.traceback()
460 raise NoRepo(_("%s is not a local Mercurial repository") % path)
460 raise NoRepo(_("%s is not a local Mercurial repository") % path)
461 self.lastrev = None
461 self.lastrev = None
462 self.lastctx = None
462 self.lastctx = None
463 self._changescache = None, None
463 self._changescache = None, None
464 self.convertfp = None
464 self.convertfp = None
465 # Restrict converted revisions to startrev descendants
465 # Restrict converted revisions to startrev descendants
466 startnode = ui.config('convert', 'hg.startrev')
466 startnode = ui.config('convert', 'hg.startrev')
467 hgrevs = ui.config('convert', 'hg.revs')
467 hgrevs = ui.config('convert', 'hg.revs')
468 if hgrevs is None:
468 if hgrevs is None:
469 if startnode is not None:
469 if startnode is not None:
470 try:
470 try:
471 startnode = self.repo.lookup(startnode)
471 startnode = self.repo.lookup(startnode)
472 except error.RepoError:
472 except error.RepoError:
473 raise error.Abort(_('%s is not a valid start revision')
473 raise error.Abort(_('%s is not a valid start revision')
474 % startnode)
474 % startnode)
475 startrev = self.repo.changelog.rev(startnode)
475 startrev = self.repo.changelog.rev(startnode)
476 children = {startnode: 1}
476 children = {startnode: 1}
477 for r in self.repo.changelog.descendants([startrev]):
477 for r in self.repo.changelog.descendants([startrev]):
478 children[self.repo.changelog.node(r)] = 1
478 children[self.repo.changelog.node(r)] = 1
479 self.keep = children.__contains__
479 self.keep = children.__contains__
480 else:
480 else:
481 self.keep = util.always
481 self.keep = util.always
482 if revs:
482 if revs:
483 self._heads = [self.repo[r].node() for r in revs]
483 self._heads = [self.repo[r].node() for r in revs]
484 else:
484 else:
485 self._heads = self.repo.heads()
485 self._heads = self.repo.heads()
486 else:
486 else:
487 if revs or startnode is not None:
487 if revs or startnode is not None:
488 raise error.Abort(_('hg.revs cannot be combined with '
488 raise error.Abort(_('hg.revs cannot be combined with '
489 'hg.startrev or --rev'))
489 'hg.startrev or --rev'))
490 nodes = set()
490 nodes = set()
491 parents = set()
491 parents = set()
492 for r in scmutil.revrange(self.repo, [hgrevs]):
492 for r in scmutil.revrange(self.repo, [hgrevs]):
493 ctx = self.repo[r]
493 ctx = self.repo[r]
494 nodes.add(ctx.node())
494 nodes.add(ctx.node())
495 parents.update(p.node() for p in ctx.parents())
495 parents.update(p.node() for p in ctx.parents())
496 self.keep = nodes.__contains__
496 self.keep = nodes.__contains__
497 self._heads = nodes - parents
497 self._heads = nodes - parents
498
498
499 def _changectx(self, rev):
499 def _changectx(self, rev):
500 if self.lastrev != rev:
500 if self.lastrev != rev:
501 self.lastctx = self.repo[rev]
501 self.lastctx = self.repo[rev]
502 self.lastrev = rev
502 self.lastrev = rev
503 return self.lastctx
503 return self.lastctx
504
504
505 def _parents(self, ctx):
505 def _parents(self, ctx):
506 return [p for p in ctx.parents() if p and self.keep(p.node())]
506 return [p for p in ctx.parents() if p and self.keep(p.node())]
507
507
508 def getheads(self):
508 def getheads(self):
509 return [nodemod.hex(h) for h in self._heads if self.keep(h)]
509 return [nodemod.hex(h) for h in self._heads if self.keep(h)]
510
510
511 def getfile(self, name, rev):
511 def getfile(self, name, rev):
512 try:
512 try:
513 fctx = self._changectx(rev)[name]
513 fctx = self._changectx(rev)[name]
514 return fctx.data(), fctx.flags()
514 return fctx.data(), fctx.flags()
515 except error.LookupError:
515 except error.LookupError:
516 return None, None
516 return None, None
517
517
518 def _changedfiles(self, ctx1, ctx2):
518 def _changedfiles(self, ctx1, ctx2):
519 ma, r = [], []
519 ma, r = [], []
520 maappend = ma.append
520 maappend = ma.append
521 rappend = r.append
521 rappend = r.append
522 d = ctx1.manifest().diff(ctx2.manifest())
522 d = ctx1.manifest().diff(ctx2.manifest())
523 for f, ((node1, flag1), (node2, flag2)) in d.iteritems():
523 for f, ((node1, flag1), (node2, flag2)) in d.iteritems():
524 if node2 is None:
524 if node2 is None:
525 rappend(f)
525 rappend(f)
526 else:
526 else:
527 maappend(f)
527 maappend(f)
528 return ma, r
528 return ma, r
529
529
530 def getchanges(self, rev, full):
530 def getchanges(self, rev, full):
531 ctx = self._changectx(rev)
531 ctx = self._changectx(rev)
532 parents = self._parents(ctx)
532 parents = self._parents(ctx)
533 if full or not parents:
533 if full or not parents:
534 files = copyfiles = ctx.manifest()
534 files = copyfiles = ctx.manifest()
535 if parents:
535 if parents:
536 if self._changescache[0] == rev:
536 if self._changescache[0] == rev:
537 ma, r = self._changescache[1]
537 ma, r = self._changescache[1]
538 else:
538 else:
539 ma, r = self._changedfiles(parents[0], ctx)
539 ma, r = self._changedfiles(parents[0], ctx)
540 if not full:
540 if not full:
541 files = ma + r
541 files = ma + r
542 copyfiles = ma
542 copyfiles = ma
543 # _getcopies() is also run for roots and before filtering so missing
543 # _getcopies() is also run for roots and before filtering so missing
544 # revlogs are detected early
544 # revlogs are detected early
545 copies = self._getcopies(ctx, parents, copyfiles)
545 copies = self._getcopies(ctx, parents, copyfiles)
546 cleanp2 = set()
546 cleanp2 = set()
547 if len(parents) == 2:
547 if len(parents) == 2:
548 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
548 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
549 for f, value in d.iteritems():
549 for f, value in d.iteritems():
550 if value is None:
550 if value is None:
551 cleanp2.add(f)
551 cleanp2.add(f)
552 changes = [(f, rev) for f in files if f not in self.ignored]
552 changes = [(f, rev) for f in files if f not in self.ignored]
553 changes.sort()
553 changes.sort()
554 return changes, copies, cleanp2
554 return changes, copies, cleanp2
555
555
556 def _getcopies(self, ctx, parents, files):
556 def _getcopies(self, ctx, parents, files):
557 copies = {}
557 copies = {}
558 for name in files:
558 for name in files:
559 if name in self.ignored:
559 if name in self.ignored:
560 continue
560 continue
561 try:
561 try:
562 copysource, _copynode = ctx.filectx(name).renamed()
562 copysource, _copynode = ctx.filectx(name).renamed()
563 if copysource in self.ignored:
563 if copysource in self.ignored:
564 continue
564 continue
565 # Ignore copy sources not in parent revisions
565 # Ignore copy sources not in parent revisions
566 found = False
566 found = False
567 for p in parents:
567 for p in parents:
568 if copysource in p:
568 if copysource in p:
569 found = True
569 found = True
570 break
570 break
571 if not found:
571 if not found:
572 continue
572 continue
573 copies[name] = copysource
573 copies[name] = copysource
574 except TypeError:
574 except TypeError:
575 pass
575 pass
576 except error.LookupError as e:
576 except error.LookupError as e:
577 if not self.ignoreerrors:
577 if not self.ignoreerrors:
578 raise
578 raise
579 self.ignored.add(name)
579 self.ignored.add(name)
580 self.ui.warn(_('ignoring: %s\n') % e)
580 self.ui.warn(_('ignoring: %s\n') % e)
581 return copies
581 return copies
582
582
583 def getcommit(self, rev):
583 def getcommit(self, rev):
584 ctx = self._changectx(rev)
584 ctx = self._changectx(rev)
585 _parents = self._parents(ctx)
585 _parents = self._parents(ctx)
586 parents = [p.hex() for p in _parents]
586 parents = [p.hex() for p in _parents]
587 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
587 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
588 crev = rev
588 crev = rev
589
589
590 return common.commit(author=ctx.user(),
590 return common.commit(author=ctx.user(),
591 date=util.datestr(ctx.date(),
591 date=util.datestr(ctx.date(),
592 '%Y-%m-%d %H:%M:%S %1%2'),
592 '%Y-%m-%d %H:%M:%S %1%2'),
593 desc=ctx.description(),
593 desc=ctx.description(),
594 rev=crev,
594 rev=crev,
595 parents=parents,
595 parents=parents,
596 optparents=optparents,
596 optparents=optparents,
597 branch=ctx.branch(),
597 branch=ctx.branch(),
598 extra=ctx.extra(),
598 extra=ctx.extra(),
599 sortkey=ctx.rev(),
599 sortkey=ctx.rev(),
600 saverev=self.saverev,
600 saverev=self.saverev,
601 phase=ctx.phase())
601 phase=ctx.phase())
602
602
603 def gettags(self):
603 def gettags(self):
604 # This will get written to .hgtags, filter non global tags out.
604 # This will get written to .hgtags, filter non global tags out.
605 tags = [t for t in self.repo.tagslist()
605 tags = [t for t in self.repo.tagslist()
606 if self.repo.tagtype(t[0]) == 'global']
606 if self.repo.tagtype(t[0]) == 'global']
607 return dict([(name, nodemod.hex(node)) for name, node in tags
607 return dict([(name, nodemod.hex(node)) for name, node in tags
608 if self.keep(node)])
608 if self.keep(node)])
609
609
610 def getchangedfiles(self, rev, i):
610 def getchangedfiles(self, rev, i):
611 ctx = self._changectx(rev)
611 ctx = self._changectx(rev)
612 parents = self._parents(ctx)
612 parents = self._parents(ctx)
613 if not parents and i is None:
613 if not parents and i is None:
614 i = 0
614 i = 0
615 ma, r = ctx.manifest().keys(), []
615 ma, r = ctx.manifest().keys(), []
616 else:
616 else:
617 i = i or 0
617 i = i or 0
618 ma, r = self._changedfiles(parents[i], ctx)
618 ma, r = self._changedfiles(parents[i], ctx)
619 ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
619 ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
620
620
621 if i == 0:
621 if i == 0:
622 self._changescache = (rev, (ma, r))
622 self._changescache = (rev, (ma, r))
623
623
624 return ma + r
624 return ma + r
625
625
626 def converted(self, rev, destrev):
626 def converted(self, rev, destrev):
627 if self.convertfp is None:
627 if self.convertfp is None:
628 self.convertfp = open(self.repo.vfs.join('shamap'), 'a')
628 self.convertfp = open(self.repo.vfs.join('shamap'), 'a')
629 self.convertfp.write('%s %s\n' % (destrev, rev))
629 self.convertfp.write('%s %s\n' % (destrev, rev))
630 self.convertfp.flush()
630 self.convertfp.flush()
631
631
632 def before(self):
632 def before(self):
633 self.ui.debug('run hg source pre-conversion action\n')
633 self.ui.debug('run hg source pre-conversion action\n')
634
634
635 def after(self):
635 def after(self):
636 self.ui.debug('run hg source post-conversion action\n')
636 self.ui.debug('run hg source post-conversion action\n')
637
637
638 def hasnativeorder(self):
638 def hasnativeorder(self):
639 return True
639 return True
640
640
641 def hasnativeclose(self):
641 def hasnativeclose(self):
642 return True
642 return True
643
643
644 def lookuprev(self, rev):
644 def lookuprev(self, rev):
645 try:
645 try:
646 return nodemod.hex(self.repo.lookup(rev))
646 return nodemod.hex(self.repo.lookup(rev))
647 except (error.RepoError, error.LookupError):
647 except (error.RepoError, error.LookupError):
648 return None
648 return None
649
649
650 def getbookmarks(self):
650 def getbookmarks(self):
651 return bookmarks.listbookmarks(self.repo)
651 return bookmarks.listbookmarks(self.repo)
652
652
653 def checkrevformat(self, revstr, mapname='splicemap'):
653 def checkrevformat(self, revstr, mapname='splicemap'):
654 """ Mercurial, revision string is a 40 byte hex """
654 """ Mercurial, revision string is a 40 byte hex """
655 self.checkhexformat(revstr, mapname)
655 self.checkhexformat(revstr, mapname)
General Comments 0
You need to be logged in to leave comments. Login now