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