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