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