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