##// END OF EJS Templates
convert: remove unused/unnecessary variable "parentctx"...
Martin von Zweigbergk -
r37412:65705fbc default
parent child Browse files
Show More
@@ -1,651 +1,649 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 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 tagparent = self.repo[self.tagsbranch].node()
368 tagparent = parentctx.node()
369 except error.RepoError:
368 except error.RepoError:
370 parentctx = None
371 tagparent = nodemod.nullid
369 tagparent = nodemod.nullid
372
370
373 oldlines = set()
371 oldlines = set()
374 for branch, heads in self.repo.branchmap().iteritems():
372 for branch, heads in self.repo.branchmap().iteritems():
375 for h in heads:
373 for h in heads:
376 if '.hgtags' in self.repo[h]:
374 if '.hgtags' in self.repo[h]:
377 oldlines.update(
375 oldlines.update(
378 set(self.repo[h]['.hgtags'].data().splitlines(True)))
376 set(self.repo[h]['.hgtags'].data().splitlines(True)))
379 oldlines = sorted(list(oldlines))
377 oldlines = sorted(list(oldlines))
380
378
381 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
379 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
382 if newlines == oldlines:
380 if newlines == oldlines:
383 return None, None
381 return None, None
384
382
385 # if the old and new tags match, then there is nothing to update
383 # if the old and new tags match, then there is nothing to update
386 oldtags = set()
384 oldtags = set()
387 newtags = set()
385 newtags = set()
388 for line in oldlines:
386 for line in oldlines:
389 s = line.strip().split(' ', 1)
387 s = line.strip().split(' ', 1)
390 if len(s) != 2:
388 if len(s) != 2:
391 continue
389 continue
392 oldtags.add(s[1])
390 oldtags.add(s[1])
393 for line in newlines:
391 for line in newlines:
394 s = line.strip().split(' ', 1)
392 s = line.strip().split(' ', 1)
395 if len(s) != 2:
393 if len(s) != 2:
396 continue
394 continue
397 if s[1] not in oldtags:
395 if s[1] not in oldtags:
398 newtags.add(s[1].strip())
396 newtags.add(s[1].strip())
399
397
400 if not newtags:
398 if not newtags:
401 return None, None
399 return None, None
402
400
403 data = "".join(newlines)
401 data = "".join(newlines)
404 def getfilectx(repo, memctx, f):
402 def getfilectx(repo, memctx, f):
405 return context.memfilectx(repo, memctx, f, data, False, False, None)
403 return context.memfilectx(repo, memctx, f, data, False, False, None)
406
404
407 self.ui.status(_("updating tags\n"))
405 self.ui.status(_("updating tags\n"))
408 date = "%s 0" % int(time.mktime(time.gmtime()))
406 date = "%s 0" % int(time.mktime(time.gmtime()))
409 extra = {'branch': self.tagsbranch}
407 extra = {'branch': self.tagsbranch}
410 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
408 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
411 [".hgtags"], getfilectx, "convert-repo", date,
409 [".hgtags"], getfilectx, "convert-repo", date,
412 extra)
410 extra)
413 node = self.repo.commitctx(ctx)
411 node = self.repo.commitctx(ctx)
414 return nodemod.hex(node), nodemod.hex(tagparent)
412 return nodemod.hex(node), nodemod.hex(tagparent)
415
413
416 def setfilemapmode(self, active):
414 def setfilemapmode(self, active):
417 self.filemapmode = active
415 self.filemapmode = active
418
416
419 def putbookmarks(self, updatedbookmark):
417 def putbookmarks(self, updatedbookmark):
420 if not len(updatedbookmark):
418 if not len(updatedbookmark):
421 return
419 return
422 wlock = lock = tr = None
420 wlock = lock = tr = None
423 try:
421 try:
424 wlock = self.repo.wlock()
422 wlock = self.repo.wlock()
425 lock = self.repo.lock()
423 lock = self.repo.lock()
426 tr = self.repo.transaction('bookmark')
424 tr = self.repo.transaction('bookmark')
427 self.ui.status(_("updating bookmarks\n"))
425 self.ui.status(_("updating bookmarks\n"))
428 destmarks = self.repo._bookmarks
426 destmarks = self.repo._bookmarks
429 changes = [(bookmark, nodemod.bin(updatedbookmark[bookmark]))
427 changes = [(bookmark, nodemod.bin(updatedbookmark[bookmark]))
430 for bookmark in updatedbookmark]
428 for bookmark in updatedbookmark]
431 destmarks.applychanges(self.repo, tr, changes)
429 destmarks.applychanges(self.repo, tr, changes)
432 tr.close()
430 tr.close()
433 finally:
431 finally:
434 lockmod.release(lock, wlock, tr)
432 lockmod.release(lock, wlock, tr)
435
433
436 def hascommitfrommap(self, rev):
434 def hascommitfrommap(self, rev):
437 # the exact semantics of clonebranches is unclear so we can't say no
435 # the exact semantics of clonebranches is unclear so we can't say no
438 return rev in self.repo or self.clonebranches
436 return rev in self.repo or self.clonebranches
439
437
440 def hascommitforsplicemap(self, rev):
438 def hascommitforsplicemap(self, rev):
441 if rev not in self.repo and self.clonebranches:
439 if rev not in self.repo and self.clonebranches:
442 raise error.Abort(_('revision %s not found in destination '
440 raise error.Abort(_('revision %s not found in destination '
443 'repository (lookups with clonebranches=true '
441 'repository (lookups with clonebranches=true '
444 'are not implemented)') % rev)
442 'are not implemented)') % rev)
445 return rev in self.repo
443 return rev in self.repo
446
444
447 class mercurial_source(common.converter_source):
445 class mercurial_source(common.converter_source):
448 def __init__(self, ui, repotype, path, revs=None):
446 def __init__(self, ui, repotype, path, revs=None):
449 common.converter_source.__init__(self, ui, repotype, path, revs)
447 common.converter_source.__init__(self, ui, repotype, path, revs)
450 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors')
448 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors')
451 self.ignored = set()
449 self.ignored = set()
452 self.saverev = ui.configbool('convert', 'hg.saverev')
450 self.saverev = ui.configbool('convert', 'hg.saverev')
453 try:
451 try:
454 self.repo = hg.repository(self.ui, path)
452 self.repo = hg.repository(self.ui, path)
455 # try to provoke an exception if this isn't really a hg
453 # try to provoke an exception if this isn't really a hg
456 # repo, but some other bogus compatible-looking url
454 # repo, but some other bogus compatible-looking url
457 if not self.repo.local():
455 if not self.repo.local():
458 raise error.RepoError
456 raise error.RepoError
459 except error.RepoError:
457 except error.RepoError:
460 ui.traceback()
458 ui.traceback()
461 raise NoRepo(_("%s is not a local Mercurial repository") % path)
459 raise NoRepo(_("%s is not a local Mercurial repository") % path)
462 self.lastrev = None
460 self.lastrev = None
463 self.lastctx = None
461 self.lastctx = None
464 self._changescache = None, None
462 self._changescache = None, None
465 self.convertfp = None
463 self.convertfp = None
466 # Restrict converted revisions to startrev descendants
464 # Restrict converted revisions to startrev descendants
467 startnode = ui.config('convert', 'hg.startrev')
465 startnode = ui.config('convert', 'hg.startrev')
468 hgrevs = ui.config('convert', 'hg.revs')
466 hgrevs = ui.config('convert', 'hg.revs')
469 if hgrevs is None:
467 if hgrevs is None:
470 if startnode is not None:
468 if startnode is not None:
471 try:
469 try:
472 startnode = self.repo.lookup(startnode)
470 startnode = self.repo.lookup(startnode)
473 except error.RepoError:
471 except error.RepoError:
474 raise error.Abort(_('%s is not a valid start revision')
472 raise error.Abort(_('%s is not a valid start revision')
475 % startnode)
473 % startnode)
476 startrev = self.repo.changelog.rev(startnode)
474 startrev = self.repo.changelog.rev(startnode)
477 children = {startnode: 1}
475 children = {startnode: 1}
478 for r in self.repo.changelog.descendants([startrev]):
476 for r in self.repo.changelog.descendants([startrev]):
479 children[self.repo.changelog.node(r)] = 1
477 children[self.repo.changelog.node(r)] = 1
480 self.keep = children.__contains__
478 self.keep = children.__contains__
481 else:
479 else:
482 self.keep = util.always
480 self.keep = util.always
483 if revs:
481 if revs:
484 self._heads = [self.repo.lookup(r) for r in revs]
482 self._heads = [self.repo.lookup(r) for r in revs]
485 else:
483 else:
486 self._heads = self.repo.heads()
484 self._heads = self.repo.heads()
487 else:
485 else:
488 if revs or startnode is not None:
486 if revs or startnode is not None:
489 raise error.Abort(_('hg.revs cannot be combined with '
487 raise error.Abort(_('hg.revs cannot be combined with '
490 'hg.startrev or --rev'))
488 'hg.startrev or --rev'))
491 nodes = set()
489 nodes = set()
492 parents = set()
490 parents = set()
493 for r in scmutil.revrange(self.repo, [hgrevs]):
491 for r in scmutil.revrange(self.repo, [hgrevs]):
494 ctx = self.repo[r]
492 ctx = self.repo[r]
495 nodes.add(ctx.node())
493 nodes.add(ctx.node())
496 parents.update(p.node() for p in ctx.parents())
494 parents.update(p.node() for p in ctx.parents())
497 self.keep = nodes.__contains__
495 self.keep = nodes.__contains__
498 self._heads = nodes - parents
496 self._heads = nodes - parents
499
497
500 def _changectx(self, rev):
498 def _changectx(self, rev):
501 if self.lastrev != rev:
499 if self.lastrev != rev:
502 self.lastctx = self.repo[rev]
500 self.lastctx = self.repo[rev]
503 self.lastrev = rev
501 self.lastrev = rev
504 return self.lastctx
502 return self.lastctx
505
503
506 def _parents(self, ctx):
504 def _parents(self, ctx):
507 return [p for p in ctx.parents() if p and self.keep(p.node())]
505 return [p for p in ctx.parents() if p and self.keep(p.node())]
508
506
509 def getheads(self):
507 def getheads(self):
510 return [nodemod.hex(h) for h in self._heads if self.keep(h)]
508 return [nodemod.hex(h) for h in self._heads if self.keep(h)]
511
509
512 def getfile(self, name, rev):
510 def getfile(self, name, rev):
513 try:
511 try:
514 fctx = self._changectx(rev)[name]
512 fctx = self._changectx(rev)[name]
515 return fctx.data(), fctx.flags()
513 return fctx.data(), fctx.flags()
516 except error.LookupError:
514 except error.LookupError:
517 return None, None
515 return None, None
518
516
519 def _changedfiles(self, ctx1, ctx2):
517 def _changedfiles(self, ctx1, ctx2):
520 ma, r = [], []
518 ma, r = [], []
521 maappend = ma.append
519 maappend = ma.append
522 rappend = r.append
520 rappend = r.append
523 d = ctx1.manifest().diff(ctx2.manifest())
521 d = ctx1.manifest().diff(ctx2.manifest())
524 for f, ((node1, flag1), (node2, flag2)) in d.iteritems():
522 for f, ((node1, flag1), (node2, flag2)) in d.iteritems():
525 if node2 is None:
523 if node2 is None:
526 rappend(f)
524 rappend(f)
527 else:
525 else:
528 maappend(f)
526 maappend(f)
529 return ma, r
527 return ma, r
530
528
531 def getchanges(self, rev, full):
529 def getchanges(self, rev, full):
532 ctx = self._changectx(rev)
530 ctx = self._changectx(rev)
533 parents = self._parents(ctx)
531 parents = self._parents(ctx)
534 if full or not parents:
532 if full or not parents:
535 files = copyfiles = ctx.manifest()
533 files = copyfiles = ctx.manifest()
536 if parents:
534 if parents:
537 if self._changescache[0] == rev:
535 if self._changescache[0] == rev:
538 ma, r = self._changescache[1]
536 ma, r = self._changescache[1]
539 else:
537 else:
540 ma, r = self._changedfiles(parents[0], ctx)
538 ma, r = self._changedfiles(parents[0], ctx)
541 if not full:
539 if not full:
542 files = ma + r
540 files = ma + r
543 copyfiles = ma
541 copyfiles = ma
544 # _getcopies() is also run for roots and before filtering so missing
542 # _getcopies() is also run for roots and before filtering so missing
545 # revlogs are detected early
543 # revlogs are detected early
546 copies = self._getcopies(ctx, parents, copyfiles)
544 copies = self._getcopies(ctx, parents, copyfiles)
547 cleanp2 = set()
545 cleanp2 = set()
548 if len(parents) == 2:
546 if len(parents) == 2:
549 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
547 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
550 for f, value in d.iteritems():
548 for f, value in d.iteritems():
551 if value is None:
549 if value is None:
552 cleanp2.add(f)
550 cleanp2.add(f)
553 changes = [(f, rev) for f in files if f not in self.ignored]
551 changes = [(f, rev) for f in files if f not in self.ignored]
554 changes.sort()
552 changes.sort()
555 return changes, copies, cleanp2
553 return changes, copies, cleanp2
556
554
557 def _getcopies(self, ctx, parents, files):
555 def _getcopies(self, ctx, parents, files):
558 copies = {}
556 copies = {}
559 for name in files:
557 for name in files:
560 if name in self.ignored:
558 if name in self.ignored:
561 continue
559 continue
562 try:
560 try:
563 copysource, _copynode = ctx.filectx(name).renamed()
561 copysource, _copynode = ctx.filectx(name).renamed()
564 if copysource in self.ignored:
562 if copysource in self.ignored:
565 continue
563 continue
566 # Ignore copy sources not in parent revisions
564 # Ignore copy sources not in parent revisions
567 if not any(copysource in p for p in parents):
565 if not any(copysource in p for p in parents):
568 continue
566 continue
569 copies[name] = copysource
567 copies[name] = copysource
570 except TypeError:
568 except TypeError:
571 pass
569 pass
572 except error.LookupError as e:
570 except error.LookupError as e:
573 if not self.ignoreerrors:
571 if not self.ignoreerrors:
574 raise
572 raise
575 self.ignored.add(name)
573 self.ignored.add(name)
576 self.ui.warn(_('ignoring: %s\n') % e)
574 self.ui.warn(_('ignoring: %s\n') % e)
577 return copies
575 return copies
578
576
579 def getcommit(self, rev):
577 def getcommit(self, rev):
580 ctx = self._changectx(rev)
578 ctx = self._changectx(rev)
581 _parents = self._parents(ctx)
579 _parents = self._parents(ctx)
582 parents = [p.hex() for p in _parents]
580 parents = [p.hex() for p in _parents]
583 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
581 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
584 crev = rev
582 crev = rev
585
583
586 return common.commit(author=ctx.user(),
584 return common.commit(author=ctx.user(),
587 date=dateutil.datestr(ctx.date(),
585 date=dateutil.datestr(ctx.date(),
588 '%Y-%m-%d %H:%M:%S %1%2'),
586 '%Y-%m-%d %H:%M:%S %1%2'),
589 desc=ctx.description(),
587 desc=ctx.description(),
590 rev=crev,
588 rev=crev,
591 parents=parents,
589 parents=parents,
592 optparents=optparents,
590 optparents=optparents,
593 branch=ctx.branch(),
591 branch=ctx.branch(),
594 extra=ctx.extra(),
592 extra=ctx.extra(),
595 sortkey=ctx.rev(),
593 sortkey=ctx.rev(),
596 saverev=self.saverev,
594 saverev=self.saverev,
597 phase=ctx.phase())
595 phase=ctx.phase())
598
596
599 def gettags(self):
597 def gettags(self):
600 # This will get written to .hgtags, filter non global tags out.
598 # This will get written to .hgtags, filter non global tags out.
601 tags = [t for t in self.repo.tagslist()
599 tags = [t for t in self.repo.tagslist()
602 if self.repo.tagtype(t[0]) == 'global']
600 if self.repo.tagtype(t[0]) == 'global']
603 return dict([(name, nodemod.hex(node)) for name, node in tags
601 return dict([(name, nodemod.hex(node)) for name, node in tags
604 if self.keep(node)])
602 if self.keep(node)])
605
603
606 def getchangedfiles(self, rev, i):
604 def getchangedfiles(self, rev, i):
607 ctx = self._changectx(rev)
605 ctx = self._changectx(rev)
608 parents = self._parents(ctx)
606 parents = self._parents(ctx)
609 if not parents and i is None:
607 if not parents and i is None:
610 i = 0
608 i = 0
611 ma, r = ctx.manifest().keys(), []
609 ma, r = ctx.manifest().keys(), []
612 else:
610 else:
613 i = i or 0
611 i = i or 0
614 ma, r = self._changedfiles(parents[i], ctx)
612 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)]
613 ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
616
614
617 if i == 0:
615 if i == 0:
618 self._changescache = (rev, (ma, r))
616 self._changescache = (rev, (ma, r))
619
617
620 return ma + r
618 return ma + r
621
619
622 def converted(self, rev, destrev):
620 def converted(self, rev, destrev):
623 if self.convertfp is None:
621 if self.convertfp is None:
624 self.convertfp = open(self.repo.vfs.join('shamap'), 'ab')
622 self.convertfp = open(self.repo.vfs.join('shamap'), 'ab')
625 self.convertfp.write(util.tonativeeol('%s %s\n' % (destrev, rev)))
623 self.convertfp.write(util.tonativeeol('%s %s\n' % (destrev, rev)))
626 self.convertfp.flush()
624 self.convertfp.flush()
627
625
628 def before(self):
626 def before(self):
629 self.ui.debug('run hg source pre-conversion action\n')
627 self.ui.debug('run hg source pre-conversion action\n')
630
628
631 def after(self):
629 def after(self):
632 self.ui.debug('run hg source post-conversion action\n')
630 self.ui.debug('run hg source post-conversion action\n')
633
631
634 def hasnativeorder(self):
632 def hasnativeorder(self):
635 return True
633 return True
636
634
637 def hasnativeclose(self):
635 def hasnativeclose(self):
638 return True
636 return True
639
637
640 def lookuprev(self, rev):
638 def lookuprev(self, rev):
641 try:
639 try:
642 return nodemod.hex(self.repo.lookup(rev))
640 return nodemod.hex(self.repo.lookup(rev))
643 except (error.RepoError, error.LookupError):
641 except (error.RepoError, error.LookupError):
644 return None
642 return None
645
643
646 def getbookmarks(self):
644 def getbookmarks(self):
647 return bookmarks.listbookmarks(self.repo)
645 return bookmarks.listbookmarks(self.repo)
648
646
649 def checkrevformat(self, revstr, mapname='splicemap'):
647 def checkrevformat(self, revstr, mapname='splicemap'):
650 """ Mercurial, revision string is a 40 byte hex """
648 """ Mercurial, revision string is a 40 byte hex """
651 self.checkhexformat(revstr, mapname)
649 self.checkhexformat(revstr, mapname)
General Comments 0
You need to be logged in to leave comments. Login now