##// END OF EJS Templates
convert: tolerate spaces between splicemap parent ids (issue3203)...
Patrick Mezard -
r16118:d554a3dc stable
parent child Browse files
Show More
@@ -1,435 +1,435 b''
1 # common.py - common code for the convert extension
1 # common.py - common code for the 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 import base64, errno
8 import base64, errno
9 import os
9 import os
10 import cPickle as pickle
10 import cPickle as pickle
11 from mercurial import util
11 from mercurial import util
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 propertycache = util.propertycache
14 propertycache = util.propertycache
15
15
16 def encodeargs(args):
16 def encodeargs(args):
17 def encodearg(s):
17 def encodearg(s):
18 lines = base64.encodestring(s)
18 lines = base64.encodestring(s)
19 lines = [l.splitlines()[0] for l in lines]
19 lines = [l.splitlines()[0] for l in lines]
20 return ''.join(lines)
20 return ''.join(lines)
21
21
22 s = pickle.dumps(args)
22 s = pickle.dumps(args)
23 return encodearg(s)
23 return encodearg(s)
24
24
25 def decodeargs(s):
25 def decodeargs(s):
26 s = base64.decodestring(s)
26 s = base64.decodestring(s)
27 return pickle.loads(s)
27 return pickle.loads(s)
28
28
29 class MissingTool(Exception):
29 class MissingTool(Exception):
30 pass
30 pass
31
31
32 def checktool(exe, name=None, abort=True):
32 def checktool(exe, name=None, abort=True):
33 name = name or exe
33 name = name or exe
34 if not util.findexe(exe):
34 if not util.findexe(exe):
35 exc = abort and util.Abort or MissingTool
35 exc = abort and util.Abort or MissingTool
36 raise exc(_('cannot find required "%s" tool') % name)
36 raise exc(_('cannot find required "%s" tool') % name)
37
37
38 class NoRepo(Exception):
38 class NoRepo(Exception):
39 pass
39 pass
40
40
41 SKIPREV = 'SKIP'
41 SKIPREV = 'SKIP'
42
42
43 class commit(object):
43 class commit(object):
44 def __init__(self, author, date, desc, parents, branch=None, rev=None,
44 def __init__(self, author, date, desc, parents, branch=None, rev=None,
45 extra={}, sortkey=None):
45 extra={}, sortkey=None):
46 self.author = author or 'unknown'
46 self.author = author or 'unknown'
47 self.date = date or '0 0'
47 self.date = date or '0 0'
48 self.desc = desc
48 self.desc = desc
49 self.parents = parents
49 self.parents = parents
50 self.branch = branch
50 self.branch = branch
51 self.rev = rev
51 self.rev = rev
52 self.extra = extra
52 self.extra = extra
53 self.sortkey = sortkey
53 self.sortkey = sortkey
54
54
55 class converter_source(object):
55 class converter_source(object):
56 """Conversion source interface"""
56 """Conversion source interface"""
57
57
58 def __init__(self, ui, path=None, rev=None):
58 def __init__(self, ui, path=None, rev=None):
59 """Initialize conversion source (or raise NoRepo("message")
59 """Initialize conversion source (or raise NoRepo("message")
60 exception if path is not a valid repository)"""
60 exception if path is not a valid repository)"""
61 self.ui = ui
61 self.ui = ui
62 self.path = path
62 self.path = path
63 self.rev = rev
63 self.rev = rev
64
64
65 self.encoding = 'utf-8'
65 self.encoding = 'utf-8'
66
66
67 def before(self):
67 def before(self):
68 pass
68 pass
69
69
70 def after(self):
70 def after(self):
71 pass
71 pass
72
72
73 def setrevmap(self, revmap):
73 def setrevmap(self, revmap):
74 """set the map of already-converted revisions"""
74 """set the map of already-converted revisions"""
75 pass
75 pass
76
76
77 def getheads(self):
77 def getheads(self):
78 """Return a list of this repository's heads"""
78 """Return a list of this repository's heads"""
79 raise NotImplementedError()
79 raise NotImplementedError()
80
80
81 def getfile(self, name, rev):
81 def getfile(self, name, rev):
82 """Return a pair (data, mode) where data is the file content
82 """Return a pair (data, mode) where data is the file content
83 as a string and mode one of '', 'x' or 'l'. rev is the
83 as a string and mode one of '', 'x' or 'l'. rev is the
84 identifier returned by a previous call to getchanges(). Raise
84 identifier returned by a previous call to getchanges(). Raise
85 IOError to indicate that name was deleted in rev.
85 IOError to indicate that name was deleted in rev.
86 """
86 """
87 raise NotImplementedError()
87 raise NotImplementedError()
88
88
89 def getchanges(self, version):
89 def getchanges(self, version):
90 """Returns a tuple of (files, copies).
90 """Returns a tuple of (files, copies).
91
91
92 files is a sorted list of (filename, id) tuples for all files
92 files is a sorted list of (filename, id) tuples for all files
93 changed between version and its first parent returned by
93 changed between version and its first parent returned by
94 getcommit(). id is the source revision id of the file.
94 getcommit(). id is the source revision id of the file.
95
95
96 copies is a dictionary of dest: source
96 copies is a dictionary of dest: source
97 """
97 """
98 raise NotImplementedError()
98 raise NotImplementedError()
99
99
100 def getcommit(self, version):
100 def getcommit(self, version):
101 """Return the commit object for version"""
101 """Return the commit object for version"""
102 raise NotImplementedError()
102 raise NotImplementedError()
103
103
104 def gettags(self):
104 def gettags(self):
105 """Return the tags as a dictionary of name: revision
105 """Return the tags as a dictionary of name: revision
106
106
107 Tag names must be UTF-8 strings.
107 Tag names must be UTF-8 strings.
108 """
108 """
109 raise NotImplementedError()
109 raise NotImplementedError()
110
110
111 def recode(self, s, encoding=None):
111 def recode(self, s, encoding=None):
112 if not encoding:
112 if not encoding:
113 encoding = self.encoding or 'utf-8'
113 encoding = self.encoding or 'utf-8'
114
114
115 if isinstance(s, unicode):
115 if isinstance(s, unicode):
116 return s.encode("utf-8")
116 return s.encode("utf-8")
117 try:
117 try:
118 return s.decode(encoding).encode("utf-8")
118 return s.decode(encoding).encode("utf-8")
119 except:
119 except:
120 try:
120 try:
121 return s.decode("latin-1").encode("utf-8")
121 return s.decode("latin-1").encode("utf-8")
122 except:
122 except:
123 return s.decode(encoding, "replace").encode("utf-8")
123 return s.decode(encoding, "replace").encode("utf-8")
124
124
125 def getchangedfiles(self, rev, i):
125 def getchangedfiles(self, rev, i):
126 """Return the files changed by rev compared to parent[i].
126 """Return the files changed by rev compared to parent[i].
127
127
128 i is an index selecting one of the parents of rev. The return
128 i is an index selecting one of the parents of rev. The return
129 value should be the list of files that are different in rev and
129 value should be the list of files that are different in rev and
130 this parent.
130 this parent.
131
131
132 If rev has no parents, i is None.
132 If rev has no parents, i is None.
133
133
134 This function is only needed to support --filemap
134 This function is only needed to support --filemap
135 """
135 """
136 raise NotImplementedError()
136 raise NotImplementedError()
137
137
138 def converted(self, rev, sinkrev):
138 def converted(self, rev, sinkrev):
139 '''Notify the source that a revision has been converted.'''
139 '''Notify the source that a revision has been converted.'''
140 pass
140 pass
141
141
142 def hasnativeorder(self):
142 def hasnativeorder(self):
143 """Return true if this source has a meaningful, native revision
143 """Return true if this source has a meaningful, native revision
144 order. For instance, Mercurial revisions are store sequentially
144 order. For instance, Mercurial revisions are store sequentially
145 while there is no such global ordering with Darcs.
145 while there is no such global ordering with Darcs.
146 """
146 """
147 return False
147 return False
148
148
149 def lookuprev(self, rev):
149 def lookuprev(self, rev):
150 """If rev is a meaningful revision reference in source, return
150 """If rev is a meaningful revision reference in source, return
151 the referenced identifier in the same format used by getcommit().
151 the referenced identifier in the same format used by getcommit().
152 return None otherwise.
152 return None otherwise.
153 """
153 """
154 return None
154 return None
155
155
156 def getbookmarks(self):
156 def getbookmarks(self):
157 """Return the bookmarks as a dictionary of name: revision
157 """Return the bookmarks as a dictionary of name: revision
158
158
159 Bookmark names are to be UTF-8 strings.
159 Bookmark names are to be UTF-8 strings.
160 """
160 """
161 return {}
161 return {}
162
162
163 class converter_sink(object):
163 class converter_sink(object):
164 """Conversion sink (target) interface"""
164 """Conversion sink (target) interface"""
165
165
166 def __init__(self, ui, path):
166 def __init__(self, ui, path):
167 """Initialize conversion sink (or raise NoRepo("message")
167 """Initialize conversion sink (or raise NoRepo("message")
168 exception if path is not a valid repository)
168 exception if path is not a valid repository)
169
169
170 created is a list of paths to remove if a fatal error occurs
170 created is a list of paths to remove if a fatal error occurs
171 later"""
171 later"""
172 self.ui = ui
172 self.ui = ui
173 self.path = path
173 self.path = path
174 self.created = []
174 self.created = []
175
175
176 def getheads(self):
176 def getheads(self):
177 """Return a list of this repository's heads"""
177 """Return a list of this repository's heads"""
178 raise NotImplementedError()
178 raise NotImplementedError()
179
179
180 def revmapfile(self):
180 def revmapfile(self):
181 """Path to a file that will contain lines
181 """Path to a file that will contain lines
182 source_rev_id sink_rev_id
182 source_rev_id sink_rev_id
183 mapping equivalent revision identifiers for each system."""
183 mapping equivalent revision identifiers for each system."""
184 raise NotImplementedError()
184 raise NotImplementedError()
185
185
186 def authorfile(self):
186 def authorfile(self):
187 """Path to a file that will contain lines
187 """Path to a file that will contain lines
188 srcauthor=dstauthor
188 srcauthor=dstauthor
189 mapping equivalent authors identifiers for each system."""
189 mapping equivalent authors identifiers for each system."""
190 return None
190 return None
191
191
192 def putcommit(self, files, copies, parents, commit, source, revmap):
192 def putcommit(self, files, copies, parents, commit, source, revmap):
193 """Create a revision with all changed files listed in 'files'
193 """Create a revision with all changed files listed in 'files'
194 and having listed parents. 'commit' is a commit object
194 and having listed parents. 'commit' is a commit object
195 containing at a minimum the author, date, and message for this
195 containing at a minimum the author, date, and message for this
196 changeset. 'files' is a list of (path, version) tuples,
196 changeset. 'files' is a list of (path, version) tuples,
197 'copies' is a dictionary mapping destinations to sources,
197 'copies' is a dictionary mapping destinations to sources,
198 'source' is the source repository, and 'revmap' is a mapfile
198 'source' is the source repository, and 'revmap' is a mapfile
199 of source revisions to converted revisions. Only getfile() and
199 of source revisions to converted revisions. Only getfile() and
200 lookuprev() should be called on 'source'.
200 lookuprev() should be called on 'source'.
201
201
202 Note that the sink repository is not told to update itself to
202 Note that the sink repository is not told to update itself to
203 a particular revision (or even what that revision would be)
203 a particular revision (or even what that revision would be)
204 before it receives the file data.
204 before it receives the file data.
205 """
205 """
206 raise NotImplementedError()
206 raise NotImplementedError()
207
207
208 def puttags(self, tags):
208 def puttags(self, tags):
209 """Put tags into sink.
209 """Put tags into sink.
210
210
211 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
211 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
212 Return a pair (tag_revision, tag_parent_revision), or (None, None)
212 Return a pair (tag_revision, tag_parent_revision), or (None, None)
213 if nothing was changed.
213 if nothing was changed.
214 """
214 """
215 raise NotImplementedError()
215 raise NotImplementedError()
216
216
217 def setbranch(self, branch, pbranches):
217 def setbranch(self, branch, pbranches):
218 """Set the current branch name. Called before the first putcommit
218 """Set the current branch name. Called before the first putcommit
219 on the branch.
219 on the branch.
220 branch: branch name for subsequent commits
220 branch: branch name for subsequent commits
221 pbranches: (converted parent revision, parent branch) tuples"""
221 pbranches: (converted parent revision, parent branch) tuples"""
222 pass
222 pass
223
223
224 def setfilemapmode(self, active):
224 def setfilemapmode(self, active):
225 """Tell the destination that we're using a filemap
225 """Tell the destination that we're using a filemap
226
226
227 Some converter_sources (svn in particular) can claim that a file
227 Some converter_sources (svn in particular) can claim that a file
228 was changed in a revision, even if there was no change. This method
228 was changed in a revision, even if there was no change. This method
229 tells the destination that we're using a filemap and that it should
229 tells the destination that we're using a filemap and that it should
230 filter empty revisions.
230 filter empty revisions.
231 """
231 """
232 pass
232 pass
233
233
234 def before(self):
234 def before(self):
235 pass
235 pass
236
236
237 def after(self):
237 def after(self):
238 pass
238 pass
239
239
240 def putbookmarks(self, bookmarks):
240 def putbookmarks(self, bookmarks):
241 """Put bookmarks into sink.
241 """Put bookmarks into sink.
242
242
243 bookmarks: {bookmarkname: sink_rev_id, ...}
243 bookmarks: {bookmarkname: sink_rev_id, ...}
244 where bookmarkname is an UTF-8 string.
244 where bookmarkname is an UTF-8 string.
245 """
245 """
246 pass
246 pass
247
247
248 def hascommit(self, rev):
248 def hascommit(self, rev):
249 """Return True if the sink contains rev"""
249 """Return True if the sink contains rev"""
250 raise NotImplementedError()
250 raise NotImplementedError()
251
251
252 class commandline(object):
252 class commandline(object):
253 def __init__(self, ui, command):
253 def __init__(self, ui, command):
254 self.ui = ui
254 self.ui = ui
255 self.command = command
255 self.command = command
256
256
257 def prerun(self):
257 def prerun(self):
258 pass
258 pass
259
259
260 def postrun(self):
260 def postrun(self):
261 pass
261 pass
262
262
263 def _cmdline(self, cmd, closestdin, *args, **kwargs):
263 def _cmdline(self, cmd, closestdin, *args, **kwargs):
264 cmdline = [self.command, cmd] + list(args)
264 cmdline = [self.command, cmd] + list(args)
265 for k, v in kwargs.iteritems():
265 for k, v in kwargs.iteritems():
266 if len(k) == 1:
266 if len(k) == 1:
267 cmdline.append('-' + k)
267 cmdline.append('-' + k)
268 else:
268 else:
269 cmdline.append('--' + k.replace('_', '-'))
269 cmdline.append('--' + k.replace('_', '-'))
270 try:
270 try:
271 if len(k) == 1:
271 if len(k) == 1:
272 cmdline.append('' + v)
272 cmdline.append('' + v)
273 else:
273 else:
274 cmdline[-1] += '=' + v
274 cmdline[-1] += '=' + v
275 except TypeError:
275 except TypeError:
276 pass
276 pass
277 cmdline = [util.shellquote(arg) for arg in cmdline]
277 cmdline = [util.shellquote(arg) for arg in cmdline]
278 if not self.ui.debugflag:
278 if not self.ui.debugflag:
279 cmdline += ['2>', util.nulldev]
279 cmdline += ['2>', util.nulldev]
280 if closestdin:
280 if closestdin:
281 cmdline += ['<', util.nulldev]
281 cmdline += ['<', util.nulldev]
282 cmdline = ' '.join(cmdline)
282 cmdline = ' '.join(cmdline)
283 return cmdline
283 return cmdline
284
284
285 def _run(self, cmd, *args, **kwargs):
285 def _run(self, cmd, *args, **kwargs):
286 return self._dorun(util.popen, cmd, True, *args, **kwargs)
286 return self._dorun(util.popen, cmd, True, *args, **kwargs)
287
287
288 def _run2(self, cmd, *args, **kwargs):
288 def _run2(self, cmd, *args, **kwargs):
289 return self._dorun(util.popen2, cmd, False, *args, **kwargs)
289 return self._dorun(util.popen2, cmd, False, *args, **kwargs)
290
290
291 def _dorun(self, openfunc, cmd, closestdin, *args, **kwargs):
291 def _dorun(self, openfunc, cmd, closestdin, *args, **kwargs):
292 cmdline = self._cmdline(cmd, closestdin, *args, **kwargs)
292 cmdline = self._cmdline(cmd, closestdin, *args, **kwargs)
293 self.ui.debug('running: %s\n' % (cmdline,))
293 self.ui.debug('running: %s\n' % (cmdline,))
294 self.prerun()
294 self.prerun()
295 try:
295 try:
296 return openfunc(cmdline)
296 return openfunc(cmdline)
297 finally:
297 finally:
298 self.postrun()
298 self.postrun()
299
299
300 def run(self, cmd, *args, **kwargs):
300 def run(self, cmd, *args, **kwargs):
301 fp = self._run(cmd, *args, **kwargs)
301 fp = self._run(cmd, *args, **kwargs)
302 output = fp.read()
302 output = fp.read()
303 self.ui.debug(output)
303 self.ui.debug(output)
304 return output, fp.close()
304 return output, fp.close()
305
305
306 def runlines(self, cmd, *args, **kwargs):
306 def runlines(self, cmd, *args, **kwargs):
307 fp = self._run(cmd, *args, **kwargs)
307 fp = self._run(cmd, *args, **kwargs)
308 output = fp.readlines()
308 output = fp.readlines()
309 self.ui.debug(''.join(output))
309 self.ui.debug(''.join(output))
310 return output, fp.close()
310 return output, fp.close()
311
311
312 def checkexit(self, status, output=''):
312 def checkexit(self, status, output=''):
313 if status:
313 if status:
314 if output:
314 if output:
315 self.ui.warn(_('%s error:\n') % self.command)
315 self.ui.warn(_('%s error:\n') % self.command)
316 self.ui.warn(output)
316 self.ui.warn(output)
317 msg = util.explainexit(status)[0]
317 msg = util.explainexit(status)[0]
318 raise util.Abort('%s %s' % (self.command, msg))
318 raise util.Abort('%s %s' % (self.command, msg))
319
319
320 def run0(self, cmd, *args, **kwargs):
320 def run0(self, cmd, *args, **kwargs):
321 output, status = self.run(cmd, *args, **kwargs)
321 output, status = self.run(cmd, *args, **kwargs)
322 self.checkexit(status, output)
322 self.checkexit(status, output)
323 return output
323 return output
324
324
325 def runlines0(self, cmd, *args, **kwargs):
325 def runlines0(self, cmd, *args, **kwargs):
326 output, status = self.runlines(cmd, *args, **kwargs)
326 output, status = self.runlines(cmd, *args, **kwargs)
327 self.checkexit(status, ''.join(output))
327 self.checkexit(status, ''.join(output))
328 return output
328 return output
329
329
330 @propertycache
330 @propertycache
331 def argmax(self):
331 def argmax(self):
332 # POSIX requires at least 4096 bytes for ARG_MAX
332 # POSIX requires at least 4096 bytes for ARG_MAX
333 argmax = 4096
333 argmax = 4096
334 try:
334 try:
335 argmax = os.sysconf("SC_ARG_MAX")
335 argmax = os.sysconf("SC_ARG_MAX")
336 except:
336 except:
337 pass
337 pass
338
338
339 # Windows shells impose their own limits on command line length,
339 # Windows shells impose their own limits on command line length,
340 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
340 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
341 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
341 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
342 # details about cmd.exe limitations.
342 # details about cmd.exe limitations.
343
343
344 # Since ARG_MAX is for command line _and_ environment, lower our limit
344 # Since ARG_MAX is for command line _and_ environment, lower our limit
345 # (and make happy Windows shells while doing this).
345 # (and make happy Windows shells while doing this).
346 return argmax // 2 - 1
346 return argmax // 2 - 1
347
347
348 def limit_arglist(self, arglist, cmd, closestdin, *args, **kwargs):
348 def limit_arglist(self, arglist, cmd, closestdin, *args, **kwargs):
349 cmdlen = len(self._cmdline(cmd, closestdin, *args, **kwargs))
349 cmdlen = len(self._cmdline(cmd, closestdin, *args, **kwargs))
350 limit = self.argmax - cmdlen
350 limit = self.argmax - cmdlen
351 bytes = 0
351 bytes = 0
352 fl = []
352 fl = []
353 for fn in arglist:
353 for fn in arglist:
354 b = len(fn) + 3
354 b = len(fn) + 3
355 if bytes + b < limit or len(fl) == 0:
355 if bytes + b < limit or len(fl) == 0:
356 fl.append(fn)
356 fl.append(fn)
357 bytes += b
357 bytes += b
358 else:
358 else:
359 yield fl
359 yield fl
360 fl = [fn]
360 fl = [fn]
361 bytes = b
361 bytes = b
362 if fl:
362 if fl:
363 yield fl
363 yield fl
364
364
365 def xargs(self, arglist, cmd, *args, **kwargs):
365 def xargs(self, arglist, cmd, *args, **kwargs):
366 for l in self.limit_arglist(arglist, cmd, True, *args, **kwargs):
366 for l in self.limit_arglist(arglist, cmd, True, *args, **kwargs):
367 self.run0(cmd, *(list(args) + l), **kwargs)
367 self.run0(cmd, *(list(args) + l), **kwargs)
368
368
369 class mapfile(dict):
369 class mapfile(dict):
370 def __init__(self, ui, path):
370 def __init__(self, ui, path):
371 super(mapfile, self).__init__()
371 super(mapfile, self).__init__()
372 self.ui = ui
372 self.ui = ui
373 self.path = path
373 self.path = path
374 self.fp = None
374 self.fp = None
375 self.order = []
375 self.order = []
376 self._read()
376 self._read()
377
377
378 def _read(self):
378 def _read(self):
379 if not self.path:
379 if not self.path:
380 return
380 return
381 try:
381 try:
382 fp = open(self.path, 'r')
382 fp = open(self.path, 'r')
383 except IOError, err:
383 except IOError, err:
384 if err.errno != errno.ENOENT:
384 if err.errno != errno.ENOENT:
385 raise
385 raise
386 return
386 return
387 for i, line in enumerate(fp):
387 for i, line in enumerate(fp):
388 try:
388 try:
389 key, value = line.splitlines()[0].rstrip().rsplit(' ', 1)
389 key, value = line.splitlines()[0].rstrip().rsplit(' ', 1)
390 except ValueError:
390 except ValueError:
391 raise util.Abort(
391 raise util.Abort(
392 _('syntax error in %s(%d): key/value pair expected')
392 _('syntax error in %s(%d): key/value pair expected')
393 % (self.path, i + 1))
393 % (self.path, i + 1))
394 if key not in self:
394 if key not in self:
395 self.order.append(key)
395 self.order.append(key)
396 super(mapfile, self).__setitem__(key, value)
396 super(mapfile, self).__setitem__(key, value)
397 fp.close()
397 fp.close()
398
398
399 def __setitem__(self, key, value):
399 def __setitem__(self, key, value):
400 if self.fp is None:
400 if self.fp is None:
401 try:
401 try:
402 self.fp = open(self.path, 'a')
402 self.fp = open(self.path, 'a')
403 except IOError, err:
403 except IOError, err:
404 raise util.Abort(_('could not open map file %r: %s') %
404 raise util.Abort(_('could not open map file %r: %s') %
405 (self.path, err.strerror))
405 (self.path, err.strerror))
406 self.fp.write('%s %s\n' % (key, value))
406 self.fp.write('%s %s\n' % (key, value))
407 self.fp.flush()
407 self.fp.flush()
408 super(mapfile, self).__setitem__(key, value)
408 super(mapfile, self).__setitem__(key, value)
409
409
410 def close(self):
410 def close(self):
411 if self.fp:
411 if self.fp:
412 self.fp.close()
412 self.fp.close()
413 self.fp = None
413 self.fp = None
414
414
415 def parsesplicemap(path):
415 def parsesplicemap(path):
416 """Parse a splicemap, return a child/parents dictionary."""
416 """Parse a splicemap, return a child/parents dictionary."""
417 m = {}
417 m = {}
418 try:
418 try:
419 fp = open(path, 'r')
419 fp = open(path, 'r')
420 for i, line in enumerate(fp):
420 for i, line in enumerate(fp):
421 try:
421 try:
422 child, parents = line.splitlines()[0].rstrip().rsplit(' ', 1)
422 child, parents = line.splitlines()[0].rstrip().split(' ', 1)
423 parents = parents.replace(',', ' ').split()
423 parents = parents.replace(',', ' ').split()
424 except ValueError:
424 except ValueError:
425 raise util.Abort(_('syntax error in %s(%d): child parent1'
425 raise util.Abort(_('syntax error in %s(%d): child parent1'
426 '[,parent2] expected') % (path, i + 1))
426 '[,parent2] expected') % (path, i + 1))
427 pp = []
427 pp = []
428 for p in parents:
428 for p in parents:
429 if p not in pp:
429 if p not in pp:
430 pp.append(p)
430 pp.append(p)
431 m[child] = pp
431 m[child] = pp
432 except IOError, e:
432 except IOError, e:
433 if e.errno != errno.ENOENT:
433 if e.errno != errno.ENOENT:
434 raise
434 raise
435 return m
435 return m
@@ -1,220 +1,220 b''
1
1
2 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "convert=" >> $HGRCPATH
3 $ echo "convert=" >> $HGRCPATH
4 $ echo 'graphlog =' >> $HGRCPATH
4 $ echo 'graphlog =' >> $HGRCPATH
5 $ glog()
5 $ glog()
6 > {
6 > {
7 > hg glog --template '{rev}:{node|short} "{desc|firstline}"\
7 > hg glog --template '{rev}:{node|short} "{desc|firstline}"\
8 > files: {files}\n' "$@"
8 > files: {files}\n' "$@"
9 > }
9 > }
10 $ hg init repo1
10 $ hg init repo1
11 $ cd repo1
11 $ cd repo1
12 $ echo a > a
12 $ echo a > a
13 $ hg ci -Am adda
13 $ hg ci -Am adda
14 adding a
14 adding a
15 $ echo b > b
15 $ echo b > b
16 $ echo a >> a
16 $ echo a >> a
17 $ hg ci -Am addb
17 $ hg ci -Am addb
18 adding b
18 adding b
19 $ PARENTID1=`hg id --debug -i`
19 $ PARENTID1=`hg id --debug -i`
20 $ echo c > c
20 $ echo c > c
21 $ hg ci -Am addc
21 $ hg ci -Am addc
22 adding c
22 adding c
23 $ PARENTID2=`hg id --debug -i`
23 $ PARENTID2=`hg id --debug -i`
24 $ cd ..
24 $ cd ..
25 $ glog -R repo1
25 $ glog -R repo1
26 @ 2:e55c719b85b6 "addc" files: c
26 @ 2:e55c719b85b6 "addc" files: c
27 |
27 |
28 o 1:6d4c2037ddc2 "addb" files: a b
28 o 1:6d4c2037ddc2 "addb" files: a b
29 |
29 |
30 o 0:07f494440405 "adda" files: a
30 o 0:07f494440405 "adda" files: a
31
31
32
32
33 $ hg init repo2
33 $ hg init repo2
34 $ cd repo2
34 $ cd repo2
35 $ echo b > a
35 $ echo b > a
36 $ echo d > d
36 $ echo d > d
37 $ hg ci -Am addaandd
37 $ hg ci -Am addaandd
38 adding a
38 adding a
39 adding d
39 adding d
40 $ CHILDID1=`hg id --debug -i`
40 $ CHILDID1=`hg id --debug -i`
41 $ echo d >> d
41 $ echo d >> d
42 $ hg ci -Am changed
42 $ hg ci -Am changed
43 $ CHILDID2=`hg id --debug -i`
43 $ CHILDID2=`hg id --debug -i`
44 $ echo e > e
44 $ echo e > e
45 $ hg ci -Am adde
45 $ hg ci -Am adde
46 adding e
46 adding e
47 $ cd ..
47 $ cd ..
48 $ glog -R repo2
48 $ glog -R repo2
49 @ 2:a39b65753b0a "adde" files: e
49 @ 2:a39b65753b0a "adde" files: e
50 |
50 |
51 o 1:e4ea00df9189 "changed" files: d
51 o 1:e4ea00df9189 "changed" files: d
52 |
52 |
53 o 0:527cdedf31fb "addaandd" files: a d
53 o 0:527cdedf31fb "addaandd" files: a d
54
54
55
55
56 test invalid splicemap
56 test invalid splicemap
57
57
58 $ cat > splicemap <<EOF
58 $ cat > splicemap <<EOF
59 > $CHILDID2
59 > $CHILDID2
60 > EOF
60 > EOF
61 $ hg convert --splicemap splicemap repo2 repo1
61 $ hg convert --splicemap splicemap repo2 repo1
62 abort: syntax error in splicemap(1): child parent1[,parent2] expected
62 abort: syntax error in splicemap(1): child parent1[,parent2] expected
63 [255]
63 [255]
64
64
65 splice repo2 on repo1
65 splice repo2 on repo1
66
66
67 $ cat > splicemap <<EOF
67 $ cat > splicemap <<EOF
68 > $CHILDID1 $PARENTID1
68 > $CHILDID1 $PARENTID1
69 > $CHILDID2 $PARENTID2,$CHILDID1
69 > $CHILDID2 $PARENTID2,$CHILDID1
70 > EOF
70 > EOF
71 $ cat splicemap
71 $ cat splicemap
72 527cdedf31fbd5ea708aa14eeecf53d4676f38db 6d4c2037ddc2cb2627ac3a244ecce35283268f8e
72 527cdedf31fbd5ea708aa14eeecf53d4676f38db 6d4c2037ddc2cb2627ac3a244ecce35283268f8e
73 e4ea00df91897da3079a10fab658c1eddba6617b e55c719b85b60e5102fac26110ba626e7cb6b7dc,527cdedf31fbd5ea708aa14eeecf53d4676f38db
73 e4ea00df91897da3079a10fab658c1eddba6617b e55c719b85b60e5102fac26110ba626e7cb6b7dc,527cdedf31fbd5ea708aa14eeecf53d4676f38db
74 $ hg clone repo1 target1
74 $ hg clone repo1 target1
75 updating to branch default
75 updating to branch default
76 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 $ hg convert --splicemap splicemap repo2 target1
77 $ hg convert --splicemap splicemap repo2 target1
78 scanning source...
78 scanning source...
79 sorting...
79 sorting...
80 converting...
80 converting...
81 2 addaandd
81 2 addaandd
82 spliced in ['6d4c2037ddc2cb2627ac3a244ecce35283268f8e'] as parents of 527cdedf31fbd5ea708aa14eeecf53d4676f38db
82 spliced in ['6d4c2037ddc2cb2627ac3a244ecce35283268f8e'] as parents of 527cdedf31fbd5ea708aa14eeecf53d4676f38db
83 1 changed
83 1 changed
84 spliced in ['e55c719b85b60e5102fac26110ba626e7cb6b7dc', '527cdedf31fbd5ea708aa14eeecf53d4676f38db'] as parents of e4ea00df91897da3079a10fab658c1eddba6617b
84 spliced in ['e55c719b85b60e5102fac26110ba626e7cb6b7dc', '527cdedf31fbd5ea708aa14eeecf53d4676f38db'] as parents of e4ea00df91897da3079a10fab658c1eddba6617b
85 0 adde
85 0 adde
86 $ glog -R target1
86 $ glog -R target1
87 o 5:16bc847b02aa "adde" files: e
87 o 5:16bc847b02aa "adde" files: e
88 |
88 |
89 o 4:e30e4fee3418 "changed" files: d
89 o 4:e30e4fee3418 "changed" files: d
90 |\
90 |\
91 | o 3:e673348c3a3c "addaandd" files: a d
91 | o 3:e673348c3a3c "addaandd" files: a d
92 | |
92 | |
93 @ | 2:e55c719b85b6 "addc" files: c
93 @ | 2:e55c719b85b6 "addc" files: c
94 |/
94 |/
95 o 1:6d4c2037ddc2 "addb" files: a b
95 o 1:6d4c2037ddc2 "addb" files: a b
96 |
96 |
97 o 0:07f494440405 "adda" files: a
97 o 0:07f494440405 "adda" files: a
98
98
99
99
100
100
101
101
102 Test splicemap and conversion order
102 Test splicemap and conversion order
103
103
104 $ hg init ordered
104 $ hg init ordered
105 $ cd ordered
105 $ cd ordered
106 $ echo a > a
106 $ echo a > a
107 $ hg ci -Am adda
107 $ hg ci -Am adda
108 adding a
108 adding a
109 $ hg branch branch
109 $ hg branch branch
110 marked working directory as branch branch
110 marked working directory as branch branch
111 (branches are permanent and global, did you want a bookmark?)
111 (branches are permanent and global, did you want a bookmark?)
112 $ echo a >> a
112 $ echo a >> a
113 $ hg ci -Am changea
113 $ hg ci -Am changea
114 $ echo a >> a
114 $ echo a >> a
115 $ hg ci -Am changeaagain
115 $ hg ci -Am changeaagain
116 $ hg up 0
116 $ hg up 0
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ echo b > b
118 $ echo b > b
119 $ hg ci -Am addb
119 $ hg ci -Am addb
120 adding b
120 adding b
121
121
122 We want 2 to depend on 1 and 3. Since 3 is always converted after 2,
122 We want 2 to depend on 1 and 3. Since 3 is always converted after 2,
123 the bug should be exhibited with all conversion orders.
123 the bug should be exhibited with all conversion orders.
124
124
125 $ cat > ../splicemap <<EOF
125 $ cat > ../splicemap <<EOF
126 > $(hg id -r 2 -i --debug) $(hg id -r 1 -i --debug),$(hg id -r 3 -i --debug)
126 > $(hg id -r 2 -i --debug) $(hg id -r 1 -i --debug), $(hg id -r 3 -i --debug)
127 > EOF
127 > EOF
128 $ cd ..
128 $ cd ..
129 $ cat splicemap
129 $ cat splicemap
130 7c364e7fa7d70ae525610c016317ed717b519d97 717d54d67e6c31fd75ffef2ff3042bdd98418437,102a90ea7b4a3361e4082ed620918c261189a36a
130 7c364e7fa7d70ae525610c016317ed717b519d97 717d54d67e6c31fd75ffef2ff3042bdd98418437, 102a90ea7b4a3361e4082ed620918c261189a36a
131
131
132 Test regular conversion
132 Test regular conversion
133
133
134 $ hg convert --splicemap splicemap ordered ordered-hg1
134 $ hg convert --splicemap splicemap ordered ordered-hg1
135 initializing destination ordered-hg1 repository
135 initializing destination ordered-hg1 repository
136 scanning source...
136 scanning source...
137 sorting...
137 sorting...
138 converting...
138 converting...
139 3 adda
139 3 adda
140 2 changea
140 2 changea
141 1 addb
141 1 addb
142 0 changeaagain
142 0 changeaagain
143 spliced in ['717d54d67e6c31fd75ffef2ff3042bdd98418437', '102a90ea7b4a3361e4082ed620918c261189a36a'] as parents of 7c364e7fa7d70ae525610c016317ed717b519d97
143 spliced in ['717d54d67e6c31fd75ffef2ff3042bdd98418437', '102a90ea7b4a3361e4082ed620918c261189a36a'] as parents of 7c364e7fa7d70ae525610c016317ed717b519d97
144 $ glog -R ordered-hg1
144 $ glog -R ordered-hg1
145 o 3:4cb04b9afbf2 "changeaagain" files: a
145 o 3:4cb04b9afbf2 "changeaagain" files: a
146 |\
146 |\
147 | o 2:102a90ea7b4a "addb" files: b
147 | o 2:102a90ea7b4a "addb" files: b
148 | |
148 | |
149 o | 1:717d54d67e6c "changea" files: a
149 o | 1:717d54d67e6c "changea" files: a
150 |/
150 |/
151 o 0:07f494440405 "adda" files: a
151 o 0:07f494440405 "adda" files: a
152
152
153
153
154 Test conversion with parent revisions already in dest, using source
154 Test conversion with parent revisions already in dest, using source
155 and destination identifiers. Test unknown splicemap target.
155 and destination identifiers. Test unknown splicemap target.
156
156
157 $ hg convert -r1 ordered ordered-hg2
157 $ hg convert -r1 ordered ordered-hg2
158 initializing destination ordered-hg2 repository
158 initializing destination ordered-hg2 repository
159 scanning source...
159 scanning source...
160 sorting...
160 sorting...
161 converting...
161 converting...
162 1 adda
162 1 adda
163 0 changea
163 0 changea
164 $ hg convert -r3 ordered ordered-hg2
164 $ hg convert -r3 ordered ordered-hg2
165 scanning source...
165 scanning source...
166 sorting...
166 sorting...
167 converting...
167 converting...
168 0 addb
168 0 addb
169 $ cat > splicemap <<EOF
169 $ cat > splicemap <<EOF
170 > $(hg -R ordered id -r 2 -i --debug) \
170 > $(hg -R ordered id -r 2 -i --debug) \
171 > $(hg -R ordered-hg2 id -r 1 -i --debug),\
171 > $(hg -R ordered-hg2 id -r 1 -i --debug),\
172 > $(hg -R ordered-hg2 id -r 2 -i --debug)
172 > $(hg -R ordered-hg2 id -r 2 -i --debug)
173 > deadbeef102a90ea7b4a3361e4082ed620918c26 deadbeef102a90ea7b4a3361e4082ed620918c27
173 > deadbeef102a90ea7b4a3361e4082ed620918c26 deadbeef102a90ea7b4a3361e4082ed620918c27
174 > EOF
174 > EOF
175 $ hg convert --splicemap splicemap ordered ordered-hg2
175 $ hg convert --splicemap splicemap ordered ordered-hg2
176 scanning source...
176 scanning source...
177 splice map revision deadbeef102a90ea7b4a3361e4082ed620918c26 is not being converted, ignoring
177 splice map revision deadbeef102a90ea7b4a3361e4082ed620918c26 is not being converted, ignoring
178 sorting...
178 sorting...
179 converting...
179 converting...
180 0 changeaagain
180 0 changeaagain
181 spliced in ['717d54d67e6c31fd75ffef2ff3042bdd98418437', '102a90ea7b4a3361e4082ed620918c261189a36a'] as parents of 7c364e7fa7d70ae525610c016317ed717b519d97
181 spliced in ['717d54d67e6c31fd75ffef2ff3042bdd98418437', '102a90ea7b4a3361e4082ed620918c261189a36a'] as parents of 7c364e7fa7d70ae525610c016317ed717b519d97
182 $ glog -R ordered-hg2
182 $ glog -R ordered-hg2
183 o 3:4cb04b9afbf2 "changeaagain" files: a
183 o 3:4cb04b9afbf2 "changeaagain" files: a
184 |\
184 |\
185 | o 2:102a90ea7b4a "addb" files: b
185 | o 2:102a90ea7b4a "addb" files: b
186 | |
186 | |
187 o | 1:717d54d67e6c "changea" files: a
187 o | 1:717d54d67e6c "changea" files: a
188 |/
188 |/
189 o 0:07f494440405 "adda" files: a
189 o 0:07f494440405 "adda" files: a
190
190
191
191
192 Test empty conversion
192 Test empty conversion
193
193
194 $ hg convert --splicemap splicemap ordered ordered-hg2
194 $ hg convert --splicemap splicemap ordered ordered-hg2
195 scanning source...
195 scanning source...
196 splice map revision deadbeef102a90ea7b4a3361e4082ed620918c26 is not being converted, ignoring
196 splice map revision deadbeef102a90ea7b4a3361e4082ed620918c26 is not being converted, ignoring
197 sorting...
197 sorting...
198 converting...
198 converting...
199
199
200 Test clonebranches
200 Test clonebranches
201
201
202 $ hg --config convert.hg.clonebranches=true convert \
202 $ hg --config convert.hg.clonebranches=true convert \
203 > --splicemap splicemap ordered ordered-hg3
203 > --splicemap splicemap ordered ordered-hg3
204 initializing destination ordered-hg3 repository
204 initializing destination ordered-hg3 repository
205 scanning source...
205 scanning source...
206 abort: revision 717d54d67e6c31fd75ffef2ff3042bdd98418437 not be found in destination repository (lookups with clonebranches=true are not implemented)
206 abort: revision 717d54d67e6c31fd75ffef2ff3042bdd98418437 not be found in destination repository (lookups with clonebranches=true are not implemented)
207 [255]
207 [255]
208
208
209 Test invalid dependency
209 Test invalid dependency
210
210
211 $ cat > splicemap <<EOF
211 $ cat > splicemap <<EOF
212 > $(hg -R ordered id -r 2 -i --debug) \
212 > $(hg -R ordered id -r 2 -i --debug) \
213 > deadbeef102a90ea7b4a3361e4082ed620918c26,\
213 > deadbeef102a90ea7b4a3361e4082ed620918c26,\
214 > $(hg -R ordered-hg2 id -r 2 -i --debug)
214 > $(hg -R ordered-hg2 id -r 2 -i --debug)
215 > EOF
215 > EOF
216 $ hg convert --splicemap splicemap ordered ordered-hg4
216 $ hg convert --splicemap splicemap ordered ordered-hg4
217 initializing destination ordered-hg4 repository
217 initializing destination ordered-hg4 repository
218 scanning source...
218 scanning source...
219 abort: unknown splice map parent: deadbeef102a90ea7b4a3361e4082ed620918c26
219 abort: unknown splice map parent: deadbeef102a90ea7b4a3361e4082ed620918c26
220 [255]
220 [255]
General Comments 0
You need to be logged in to leave comments. Login now