##// END OF EJS Templates
convert: apply the appropriate phases to the destination (issue4165)...
Matt Harbison -
r25571:1abfe639 default
parent child Browse files
Show More
@@ -1,470 +1,471
1 1 # common.py - common code for the convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import base64, errno, subprocess, os, datetime, re
9 9 import cPickle as pickle
10 from mercurial import util
10 from mercurial import phases, util
11 11 from mercurial.i18n import _
12 12
13 13 propertycache = util.propertycache
14 14
15 15 def encodeargs(args):
16 16 def encodearg(s):
17 17 lines = base64.encodestring(s)
18 18 lines = [l.splitlines()[0] for l in lines]
19 19 return ''.join(lines)
20 20
21 21 s = pickle.dumps(args)
22 22 return encodearg(s)
23 23
24 24 def decodeargs(s):
25 25 s = base64.decodestring(s)
26 26 return pickle.loads(s)
27 27
28 28 class MissingTool(Exception):
29 29 pass
30 30
31 31 def checktool(exe, name=None, abort=True):
32 32 name = name or exe
33 33 if not util.findexe(exe):
34 34 if abort:
35 35 exc = util.Abort
36 36 else:
37 37 exc = MissingTool
38 38 raise exc(_('cannot find required "%s" tool') % name)
39 39
40 40 class NoRepo(Exception):
41 41 pass
42 42
43 43 SKIPREV = 'SKIP'
44 44
45 45 class commit(object):
46 46 def __init__(self, author, date, desc, parents, branch=None, rev=None,
47 extra={}, sortkey=None, saverev=True):
47 extra={}, sortkey=None, saverev=True, phase=phases.draft):
48 48 self.author = author or 'unknown'
49 49 self.date = date or '0 0'
50 50 self.desc = desc
51 51 self.parents = parents
52 52 self.branch = branch
53 53 self.rev = rev
54 54 self.extra = extra
55 55 self.sortkey = sortkey
56 56 self.saverev = saverev
57 self.phase = phase
57 58
58 59 class converter_source(object):
59 60 """Conversion source interface"""
60 61
61 62 def __init__(self, ui, path=None, rev=None):
62 63 """Initialize conversion source (or raise NoRepo("message")
63 64 exception if path is not a valid repository)"""
64 65 self.ui = ui
65 66 self.path = path
66 67 self.rev = rev
67 68
68 69 self.encoding = 'utf-8'
69 70
70 71 def checkhexformat(self, revstr, mapname='splicemap'):
71 72 """ fails if revstr is not a 40 byte hex. mercurial and git both uses
72 73 such format for their revision numbering
73 74 """
74 75 if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
75 76 raise util.Abort(_('%s entry %s is not a valid revision'
76 77 ' identifier') % (mapname, revstr))
77 78
78 79 def before(self):
79 80 pass
80 81
81 82 def after(self):
82 83 pass
83 84
84 85 def setrevmap(self, revmap):
85 86 """set the map of already-converted revisions"""
86 87 pass
87 88
88 89 def getheads(self):
89 90 """Return a list of this repository's heads"""
90 91 raise NotImplementedError
91 92
92 93 def getfile(self, name, rev):
93 94 """Return a pair (data, mode) where data is the file content
94 95 as a string and mode one of '', 'x' or 'l'. rev is the
95 96 identifier returned by a previous call to getchanges().
96 97 Data is None if file is missing/deleted in rev.
97 98 """
98 99 raise NotImplementedError
99 100
100 101 def getchanges(self, version, full):
101 102 """Returns a tuple of (files, copies, cleanp2).
102 103
103 104 files is a sorted list of (filename, id) tuples for all files
104 105 changed between version and its first parent returned by
105 106 getcommit(). If full, all files in that revision is returned.
106 107 id is the source revision id of the file.
107 108
108 109 copies is a dictionary of dest: source
109 110
110 111 cleanp2 is the set of files filenames that are clean against p2.
111 112 (Files that are clean against p1 are already not in files (unless
112 113 full). This makes it possible to handle p2 clean files similarly.)
113 114 """
114 115 raise NotImplementedError
115 116
116 117 def getcommit(self, version):
117 118 """Return the commit object for version"""
118 119 raise NotImplementedError
119 120
120 121 def numcommits(self):
121 122 """Return the number of commits in this source.
122 123
123 124 If unknown, return None.
124 125 """
125 126 return None
126 127
127 128 def gettags(self):
128 129 """Return the tags as a dictionary of name: revision
129 130
130 131 Tag names must be UTF-8 strings.
131 132 """
132 133 raise NotImplementedError
133 134
134 135 def recode(self, s, encoding=None):
135 136 if not encoding:
136 137 encoding = self.encoding or 'utf-8'
137 138
138 139 if isinstance(s, unicode):
139 140 return s.encode("utf-8")
140 141 try:
141 142 return s.decode(encoding).encode("utf-8")
142 143 except UnicodeError:
143 144 try:
144 145 return s.decode("latin-1").encode("utf-8")
145 146 except UnicodeError:
146 147 return s.decode(encoding, "replace").encode("utf-8")
147 148
148 149 def getchangedfiles(self, rev, i):
149 150 """Return the files changed by rev compared to parent[i].
150 151
151 152 i is an index selecting one of the parents of rev. The return
152 153 value should be the list of files that are different in rev and
153 154 this parent.
154 155
155 156 If rev has no parents, i is None.
156 157
157 158 This function is only needed to support --filemap
158 159 """
159 160 raise NotImplementedError
160 161
161 162 def converted(self, rev, sinkrev):
162 163 '''Notify the source that a revision has been converted.'''
163 164 pass
164 165
165 166 def hasnativeorder(self):
166 167 """Return true if this source has a meaningful, native revision
167 168 order. For instance, Mercurial revisions are store sequentially
168 169 while there is no such global ordering with Darcs.
169 170 """
170 171 return False
171 172
172 173 def hasnativeclose(self):
173 174 """Return true if this source has ability to close branch.
174 175 """
175 176 return False
176 177
177 178 def lookuprev(self, rev):
178 179 """If rev is a meaningful revision reference in source, return
179 180 the referenced identifier in the same format used by getcommit().
180 181 return None otherwise.
181 182 """
182 183 return None
183 184
184 185 def getbookmarks(self):
185 186 """Return the bookmarks as a dictionary of name: revision
186 187
187 188 Bookmark names are to be UTF-8 strings.
188 189 """
189 190 return {}
190 191
191 192 def checkrevformat(self, revstr, mapname='splicemap'):
192 193 """revstr is a string that describes a revision in the given
193 194 source control system. Return true if revstr has correct
194 195 format.
195 196 """
196 197 return True
197 198
198 199 class converter_sink(object):
199 200 """Conversion sink (target) interface"""
200 201
201 202 def __init__(self, ui, path):
202 203 """Initialize conversion sink (or raise NoRepo("message")
203 204 exception if path is not a valid repository)
204 205
205 206 created is a list of paths to remove if a fatal error occurs
206 207 later"""
207 208 self.ui = ui
208 209 self.path = path
209 210 self.created = []
210 211
211 212 def revmapfile(self):
212 213 """Path to a file that will contain lines
213 214 source_rev_id sink_rev_id
214 215 mapping equivalent revision identifiers for each system."""
215 216 raise NotImplementedError
216 217
217 218 def authorfile(self):
218 219 """Path to a file that will contain lines
219 220 srcauthor=dstauthor
220 221 mapping equivalent authors identifiers for each system."""
221 222 return None
222 223
223 224 def putcommit(self, files, copies, parents, commit, source, revmap, full,
224 225 cleanp2):
225 226 """Create a revision with all changed files listed in 'files'
226 227 and having listed parents. 'commit' is a commit object
227 228 containing at a minimum the author, date, and message for this
228 229 changeset. 'files' is a list of (path, version) tuples,
229 230 'copies' is a dictionary mapping destinations to sources,
230 231 'source' is the source repository, and 'revmap' is a mapfile
231 232 of source revisions to converted revisions. Only getfile() and
232 233 lookuprev() should be called on 'source'. 'full' means that 'files'
233 234 is complete and all other files should be removed.
234 235 'cleanp2' is a set of the filenames that are unchanged from p2
235 236 (only in the common merge case where there two parents).
236 237
237 238 Note that the sink repository is not told to update itself to
238 239 a particular revision (or even what that revision would be)
239 240 before it receives the file data.
240 241 """
241 242 raise NotImplementedError
242 243
243 244 def puttags(self, tags):
244 245 """Put tags into sink.
245 246
246 247 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
247 248 Return a pair (tag_revision, tag_parent_revision), or (None, None)
248 249 if nothing was changed.
249 250 """
250 251 raise NotImplementedError
251 252
252 253 def setbranch(self, branch, pbranches):
253 254 """Set the current branch name. Called before the first putcommit
254 255 on the branch.
255 256 branch: branch name for subsequent commits
256 257 pbranches: (converted parent revision, parent branch) tuples"""
257 258 pass
258 259
259 260 def setfilemapmode(self, active):
260 261 """Tell the destination that we're using a filemap
261 262
262 263 Some converter_sources (svn in particular) can claim that a file
263 264 was changed in a revision, even if there was no change. This method
264 265 tells the destination that we're using a filemap and that it should
265 266 filter empty revisions.
266 267 """
267 268 pass
268 269
269 270 def before(self):
270 271 pass
271 272
272 273 def after(self):
273 274 pass
274 275
275 276 def putbookmarks(self, bookmarks):
276 277 """Put bookmarks into sink.
277 278
278 279 bookmarks: {bookmarkname: sink_rev_id, ...}
279 280 where bookmarkname is an UTF-8 string.
280 281 """
281 282 pass
282 283
283 284 def hascommitfrommap(self, rev):
284 285 """Return False if a rev mentioned in a filemap is known to not be
285 286 present."""
286 287 raise NotImplementedError
287 288
288 289 def hascommitforsplicemap(self, rev):
289 290 """This method is for the special needs for splicemap handling and not
290 291 for general use. Returns True if the sink contains rev, aborts on some
291 292 special cases."""
292 293 raise NotImplementedError
293 294
294 295 class commandline(object):
295 296 def __init__(self, ui, command):
296 297 self.ui = ui
297 298 self.command = command
298 299
299 300 def prerun(self):
300 301 pass
301 302
302 303 def postrun(self):
303 304 pass
304 305
305 306 def _cmdline(self, cmd, *args, **kwargs):
306 307 cmdline = [self.command, cmd] + list(args)
307 308 for k, v in kwargs.iteritems():
308 309 if len(k) == 1:
309 310 cmdline.append('-' + k)
310 311 else:
311 312 cmdline.append('--' + k.replace('_', '-'))
312 313 try:
313 314 if len(k) == 1:
314 315 cmdline.append('' + v)
315 316 else:
316 317 cmdline[-1] += '=' + v
317 318 except TypeError:
318 319 pass
319 320 cmdline = [util.shellquote(arg) for arg in cmdline]
320 321 if not self.ui.debugflag:
321 322 cmdline += ['2>', os.devnull]
322 323 cmdline = ' '.join(cmdline)
323 324 return cmdline
324 325
325 326 def _run(self, cmd, *args, **kwargs):
326 327 def popen(cmdline):
327 328 p = subprocess.Popen(cmdline, shell=True, bufsize=-1,
328 329 close_fds=util.closefds,
329 330 stdout=subprocess.PIPE)
330 331 return p
331 332 return self._dorun(popen, cmd, *args, **kwargs)
332 333
333 334 def _run2(self, cmd, *args, **kwargs):
334 335 return self._dorun(util.popen2, cmd, *args, **kwargs)
335 336
336 337 def _dorun(self, openfunc, cmd, *args, **kwargs):
337 338 cmdline = self._cmdline(cmd, *args, **kwargs)
338 339 self.ui.debug('running: %s\n' % (cmdline,))
339 340 self.prerun()
340 341 try:
341 342 return openfunc(cmdline)
342 343 finally:
343 344 self.postrun()
344 345
345 346 def run(self, cmd, *args, **kwargs):
346 347 p = self._run(cmd, *args, **kwargs)
347 348 output = p.communicate()[0]
348 349 self.ui.debug(output)
349 350 return output, p.returncode
350 351
351 352 def runlines(self, cmd, *args, **kwargs):
352 353 p = self._run(cmd, *args, **kwargs)
353 354 output = p.stdout.readlines()
354 355 p.wait()
355 356 self.ui.debug(''.join(output))
356 357 return output, p.returncode
357 358
358 359 def checkexit(self, status, output=''):
359 360 if status:
360 361 if output:
361 362 self.ui.warn(_('%s error:\n') % self.command)
362 363 self.ui.warn(output)
363 364 msg = util.explainexit(status)[0]
364 365 raise util.Abort('%s %s' % (self.command, msg))
365 366
366 367 def run0(self, cmd, *args, **kwargs):
367 368 output, status = self.run(cmd, *args, **kwargs)
368 369 self.checkexit(status, output)
369 370 return output
370 371
371 372 def runlines0(self, cmd, *args, **kwargs):
372 373 output, status = self.runlines(cmd, *args, **kwargs)
373 374 self.checkexit(status, ''.join(output))
374 375 return output
375 376
376 377 @propertycache
377 378 def argmax(self):
378 379 # POSIX requires at least 4096 bytes for ARG_MAX
379 380 argmax = 4096
380 381 try:
381 382 argmax = os.sysconf("SC_ARG_MAX")
382 383 except (AttributeError, ValueError):
383 384 pass
384 385
385 386 # Windows shells impose their own limits on command line length,
386 387 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
387 388 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
388 389 # details about cmd.exe limitations.
389 390
390 391 # Since ARG_MAX is for command line _and_ environment, lower our limit
391 392 # (and make happy Windows shells while doing this).
392 393 return argmax // 2 - 1
393 394
394 395 def _limit_arglist(self, arglist, cmd, *args, **kwargs):
395 396 cmdlen = len(self._cmdline(cmd, *args, **kwargs))
396 397 limit = self.argmax - cmdlen
397 398 bytes = 0
398 399 fl = []
399 400 for fn in arglist:
400 401 b = len(fn) + 3
401 402 if bytes + b < limit or len(fl) == 0:
402 403 fl.append(fn)
403 404 bytes += b
404 405 else:
405 406 yield fl
406 407 fl = [fn]
407 408 bytes = b
408 409 if fl:
409 410 yield fl
410 411
411 412 def xargs(self, arglist, cmd, *args, **kwargs):
412 413 for l in self._limit_arglist(arglist, cmd, *args, **kwargs):
413 414 self.run0(cmd, *(list(args) + l), **kwargs)
414 415
415 416 class mapfile(dict):
416 417 def __init__(self, ui, path):
417 418 super(mapfile, self).__init__()
418 419 self.ui = ui
419 420 self.path = path
420 421 self.fp = None
421 422 self.order = []
422 423 self._read()
423 424
424 425 def _read(self):
425 426 if not self.path:
426 427 return
427 428 try:
428 429 fp = open(self.path, 'r')
429 430 except IOError, err:
430 431 if err.errno != errno.ENOENT:
431 432 raise
432 433 return
433 434 for i, line in enumerate(fp):
434 435 line = line.splitlines()[0].rstrip()
435 436 if not line:
436 437 # Ignore blank lines
437 438 continue
438 439 try:
439 440 key, value = line.rsplit(' ', 1)
440 441 except ValueError:
441 442 raise util.Abort(
442 443 _('syntax error in %s(%d): key/value pair expected')
443 444 % (self.path, i + 1))
444 445 if key not in self:
445 446 self.order.append(key)
446 447 super(mapfile, self).__setitem__(key, value)
447 448 fp.close()
448 449
449 450 def __setitem__(self, key, value):
450 451 if self.fp is None:
451 452 try:
452 453 self.fp = open(self.path, 'a')
453 454 except IOError, err:
454 455 raise util.Abort(_('could not open map file %r: %s') %
455 456 (self.path, err.strerror))
456 457 self.fp.write('%s %s\n' % (key, value))
457 458 self.fp.flush()
458 459 super(mapfile, self).__setitem__(key, value)
459 460
460 461 def close(self):
461 462 if self.fp:
462 463 self.fp.close()
463 464 self.fp = None
464 465
465 466 def makedatetimestamp(t):
466 467 """Like util.makedate() but for time t instead of current time"""
467 468 delta = (datetime.datetime.utcfromtimestamp(t) -
468 469 datetime.datetime.fromtimestamp(t))
469 470 tz = delta.days * 86400 + delta.seconds
470 471 return t, tz
@@ -1,536 +1,560
1 1 # hg.py - hg backend for convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # Notes for hg->hg conversion:
9 9 #
10 10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 11 # of commit messages, but new versions do. Changesets created by
12 12 # those older versions, then converted, may thus have different
13 13 # hashes for changesets that are otherwise identical.
14 14 #
15 15 # * Using "--config convert.hg.saverev=true" will make the source
16 16 # identifier to be stored in the converted revision. This will cause
17 17 # the converted revision to have a different identity than the
18 18 # source.
19 19
20 20
21 21 import os, time, cStringIO
22 22 from mercurial.i18n import _
23 23 from mercurial.node import bin, hex, nullid
24 24 from mercurial import hg, util, context, bookmarks, error, scmutil, exchange
25 from mercurial import phases
25 26
26 27 from common import NoRepo, commit, converter_source, converter_sink, mapfile
27 28
28 29 import re
29 30 sha1re = re.compile(r'\b[0-9a-f]{12,40}\b')
30 31
31 32 class mercurial_sink(converter_sink):
32 33 def __init__(self, ui, path):
33 34 converter_sink.__init__(self, ui, path)
34 35 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
35 36 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
36 37 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
37 38 self.lastbranch = None
38 39 if os.path.isdir(path) and len(os.listdir(path)) > 0:
39 40 try:
40 41 self.repo = hg.repository(self.ui, path)
41 42 if not self.repo.local():
42 43 raise NoRepo(_('%s is not a local Mercurial repository')
43 44 % path)
44 45 except error.RepoError, err:
45 46 ui.traceback()
46 47 raise NoRepo(err.args[0])
47 48 else:
48 49 try:
49 50 ui.status(_('initializing destination %s repository\n') % path)
50 51 self.repo = hg.repository(self.ui, path, create=True)
51 52 if not self.repo.local():
52 53 raise NoRepo(_('%s is not a local Mercurial repository')
53 54 % path)
54 55 self.created.append(path)
55 56 except error.RepoError:
56 57 ui.traceback()
57 58 raise NoRepo(_("could not create hg repository %s as sink")
58 59 % path)
59 60 self.lock = None
60 61 self.wlock = None
61 62 self.filemapmode = False
62 63 self.subrevmaps = {}
63 64
64 65 def before(self):
65 66 self.ui.debug('run hg sink pre-conversion action\n')
66 67 self.wlock = self.repo.wlock()
67 68 self.lock = self.repo.lock()
68 69
69 70 def after(self):
70 71 self.ui.debug('run hg sink post-conversion action\n')
71 72 if self.lock:
72 73 self.lock.release()
73 74 if self.wlock:
74 75 self.wlock.release()
75 76
76 77 def revmapfile(self):
77 78 return self.repo.join("shamap")
78 79
79 80 def authorfile(self):
80 81 return self.repo.join("authormap")
81 82
82 83 def setbranch(self, branch, pbranches):
83 84 if not self.clonebranches:
84 85 return
85 86
86 87 setbranch = (branch != self.lastbranch)
87 88 self.lastbranch = branch
88 89 if not branch:
89 90 branch = 'default'
90 91 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
91 92 if pbranches:
92 93 pbranch = pbranches[0][1]
93 94 else:
94 95 pbranch = 'default'
95 96
96 97 branchpath = os.path.join(self.path, branch)
97 98 if setbranch:
98 99 self.after()
99 100 try:
100 101 self.repo = hg.repository(self.ui, branchpath)
101 102 except Exception:
102 103 self.repo = hg.repository(self.ui, branchpath, create=True)
103 104 self.before()
104 105
105 106 # pbranches may bring revisions from other branches (merge parents)
106 107 # Make sure we have them, or pull them.
107 108 missings = {}
108 109 for b in pbranches:
109 110 try:
110 111 self.repo.lookup(b[0])
111 112 except Exception:
112 113 missings.setdefault(b[1], []).append(b[0])
113 114
114 115 if missings:
115 116 self.after()
116 117 for pbranch, heads in sorted(missings.iteritems()):
117 118 pbranchpath = os.path.join(self.path, pbranch)
118 119 prepo = hg.peer(self.ui, {}, pbranchpath)
119 120 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
120 121 exchange.pull(self.repo, prepo,
121 122 [prepo.lookup(h) for h in heads])
122 123 self.before()
123 124
124 125 def _rewritetags(self, source, revmap, data):
125 126 fp = cStringIO.StringIO()
126 127 for line in data.splitlines():
127 128 s = line.split(' ', 1)
128 129 if len(s) != 2:
129 130 continue
130 131 revid = revmap.get(source.lookuprev(s[0]))
131 132 if not revid:
132 133 if s[0] == hex(nullid):
133 134 revid = s[0]
134 135 else:
135 136 continue
136 137 fp.write('%s %s\n' % (revid, s[1]))
137 138 return fp.getvalue()
138 139
139 140 def _rewritesubstate(self, source, data):
140 141 fp = cStringIO.StringIO()
141 142 for line in data.splitlines():
142 143 s = line.split(' ', 1)
143 144 if len(s) != 2:
144 145 continue
145 146
146 147 revid = s[0]
147 148 subpath = s[1]
148 149 if revid != hex(nullid):
149 150 revmap = self.subrevmaps.get(subpath)
150 151 if revmap is None:
151 152 revmap = mapfile(self.ui,
152 153 self.repo.wjoin(subpath, '.hg/shamap'))
153 154 self.subrevmaps[subpath] = revmap
154 155
155 156 # It is reasonable that one or more of the subrepos don't
156 157 # need to be converted, in which case they can be cloned
157 158 # into place instead of converted. Therefore, only warn
158 159 # once.
159 160 msg = _('no ".hgsubstate" updates will be made for "%s"\n')
160 161 if len(revmap) == 0:
161 162 sub = self.repo.wvfs.reljoin(subpath, '.hg')
162 163
163 164 if self.repo.wvfs.exists(sub):
164 165 self.ui.warn(msg % subpath)
165 166
166 167 newid = revmap.get(revid)
167 168 if not newid:
168 169 if len(revmap) > 0:
169 170 self.ui.warn(_("%s is missing from %s/.hg/shamap\n") %
170 171 (revid, subpath))
171 172 else:
172 173 revid = newid
173 174
174 175 fp.write('%s %s\n' % (revid, subpath))
175 176
176 177 return fp.getvalue()
177 178
178 179 def putcommit(self, files, copies, parents, commit, source, revmap, full,
179 180 cleanp2):
180 181 files = dict(files)
181 182
182 183 def getfilectx(repo, memctx, f):
183 184 if p2ctx and f in cleanp2 and f not in copies:
184 185 self.ui.debug('reusing %s from p2\n' % f)
185 186 return p2ctx[f]
186 187 try:
187 188 v = files[f]
188 189 except KeyError:
189 190 return None
190 191 data, mode = source.getfile(f, v)
191 192 if data is None:
192 193 return None
193 194 if f == '.hgtags':
194 195 data = self._rewritetags(source, revmap, data)
195 196 if f == '.hgsubstate':
196 197 data = self._rewritesubstate(source, data)
197 198 return context.memfilectx(self.repo, f, data, 'l' in mode,
198 199 'x' in mode, copies.get(f))
199 200
200 201 pl = []
201 202 for p in parents:
202 203 if p not in pl:
203 204 pl.append(p)
204 205 parents = pl
205 206 nparents = len(parents)
206 207 if self.filemapmode and nparents == 1:
207 208 m1node = self.repo.changelog.read(bin(parents[0]))[0]
208 209 parent = parents[0]
209 210
210 211 if len(parents) < 2:
211 212 parents.append(nullid)
212 213 if len(parents) < 2:
213 214 parents.append(nullid)
214 215 p2 = parents.pop(0)
215 216
216 217 text = commit.desc
217 218
218 219 sha1s = re.findall(sha1re, text)
219 220 for sha1 in sha1s:
220 221 oldrev = source.lookuprev(sha1)
221 222 newrev = revmap.get(oldrev)
222 223 if newrev is not None:
223 224 text = text.replace(sha1, newrev[:len(sha1)])
224 225
225 226 extra = commit.extra.copy()
226 227
227 228 for label in ('source', 'transplant_source', 'rebase_source'):
228 229 node = extra.get(label)
229 230
230 231 if node is None:
231 232 continue
232 233
233 234 # Only transplant stores its reference in binary
234 235 if label == 'transplant_source':
235 236 node = hex(node)
236 237
237 238 newrev = revmap.get(node)
238 239 if newrev is not None:
239 240 if label == 'transplant_source':
240 241 newrev = bin(newrev)
241 242
242 243 extra[label] = newrev
243 244
244 245 if self.branchnames and commit.branch:
245 246 extra['branch'] = commit.branch
246 247 if commit.rev and commit.saverev:
247 248 extra['convert_revision'] = commit.rev
248 249
249 250 while parents:
250 251 p1 = p2
251 252 p2 = parents.pop(0)
252 253 p2ctx = None
253 254 if p2 != nullid:
254 255 p2ctx = self.repo[p2]
255 256 fileset = set(files)
256 257 if full:
257 258 fileset.update(self.repo[p1])
258 259 fileset.update(self.repo[p2])
259 260 ctx = context.memctx(self.repo, (p1, p2), text, fileset,
260 261 getfilectx, commit.author, commit.date, extra)
261 self.repo.commitctx(ctx)
262
263 # We won't know if the conversion changes the node until after the
264 # commit, so copy the source's phase for now.
265 self.repo.ui.setconfig('phases', 'new-commit',
266 phases.phasenames[commit.phase], 'convert')
267
268 tr = self.repo.transaction("convert")
269
270 try:
271 node = hex(self.repo.commitctx(ctx))
272
273 # If the node value has changed, but the phase is lower than
274 # draft, set it back to draft since it hasn't been exposed
275 # anywhere.
276 if commit.rev != node:
277 ctx = self.repo[node]
278 if ctx.phase() < phases.draft:
279 phases.retractboundary(self.repo, tr, phases.draft,
280 [ctx.node()])
281 tr.close()
282 finally:
283 tr.release()
284
262 285 text = "(octopus merge fixup)\n"
263 286 p2 = hex(self.repo.changelog.tip())
264 287
265 288 if self.filemapmode and nparents == 1:
266 289 man = self.repo.manifest
267 290 mnode = self.repo.changelog.read(bin(p2))[0]
268 291 closed = 'close' in commit.extra
269 292 if not closed and not man.cmp(m1node, man.revision(mnode)):
270 293 self.ui.status(_("filtering out empty revision\n"))
271 294 self.repo.rollback(force=True)
272 295 return parent
273 296 return p2
274 297
275 298 def puttags(self, tags):
276 299 try:
277 300 parentctx = self.repo[self.tagsbranch]
278 301 tagparent = parentctx.node()
279 302 except error.RepoError:
280 303 parentctx = None
281 304 tagparent = nullid
282 305
283 306 oldlines = set()
284 307 for branch, heads in self.repo.branchmap().iteritems():
285 308 for h in heads:
286 309 if '.hgtags' in self.repo[h]:
287 310 oldlines.update(
288 311 set(self.repo[h]['.hgtags'].data().splitlines(True)))
289 312 oldlines = sorted(list(oldlines))
290 313
291 314 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
292 315 if newlines == oldlines:
293 316 return None, None
294 317
295 318 # if the old and new tags match, then there is nothing to update
296 319 oldtags = set()
297 320 newtags = set()
298 321 for line in oldlines:
299 322 s = line.strip().split(' ', 1)
300 323 if len(s) != 2:
301 324 continue
302 325 oldtags.add(s[1])
303 326 for line in newlines:
304 327 s = line.strip().split(' ', 1)
305 328 if len(s) != 2:
306 329 continue
307 330 if s[1] not in oldtags:
308 331 newtags.add(s[1].strip())
309 332
310 333 if not newtags:
311 334 return None, None
312 335
313 336 data = "".join(newlines)
314 337 def getfilectx(repo, memctx, f):
315 338 return context.memfilectx(repo, f, data, False, False, None)
316 339
317 340 self.ui.status(_("updating tags\n"))
318 341 date = "%s 0" % int(time.mktime(time.gmtime()))
319 342 extra = {'branch': self.tagsbranch}
320 343 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
321 344 [".hgtags"], getfilectx, "convert-repo", date,
322 345 extra)
323 346 self.repo.commitctx(ctx)
324 347 return hex(self.repo.changelog.tip()), hex(tagparent)
325 348
326 349 def setfilemapmode(self, active):
327 350 self.filemapmode = active
328 351
329 352 def putbookmarks(self, updatedbookmark):
330 353 if not len(updatedbookmark):
331 354 return
332 355
333 356 self.ui.status(_("updating bookmarks\n"))
334 357 destmarks = self.repo._bookmarks
335 358 for bookmark in updatedbookmark:
336 359 destmarks[bookmark] = bin(updatedbookmark[bookmark])
337 360 destmarks.write()
338 361
339 362 def hascommitfrommap(self, rev):
340 363 # the exact semantics of clonebranches is unclear so we can't say no
341 364 return rev in self.repo or self.clonebranches
342 365
343 366 def hascommitforsplicemap(self, rev):
344 367 if rev not in self.repo and self.clonebranches:
345 368 raise util.Abort(_('revision %s not found in destination '
346 369 'repository (lookups with clonebranches=true '
347 370 'are not implemented)') % rev)
348 371 return rev in self.repo
349 372
350 373 class mercurial_source(converter_source):
351 374 def __init__(self, ui, path, rev=None):
352 375 converter_source.__init__(self, ui, path, rev)
353 376 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
354 377 self.ignored = set()
355 378 self.saverev = ui.configbool('convert', 'hg.saverev', False)
356 379 try:
357 380 self.repo = hg.repository(self.ui, path)
358 381 # try to provoke an exception if this isn't really a hg
359 382 # repo, but some other bogus compatible-looking url
360 383 if not self.repo.local():
361 384 raise error.RepoError
362 385 except error.RepoError:
363 386 ui.traceback()
364 387 raise NoRepo(_("%s is not a local Mercurial repository") % path)
365 388 self.lastrev = None
366 389 self.lastctx = None
367 390 self._changescache = None, None
368 391 self.convertfp = None
369 392 # Restrict converted revisions to startrev descendants
370 393 startnode = ui.config('convert', 'hg.startrev')
371 394 hgrevs = ui.config('convert', 'hg.revs')
372 395 if hgrevs is None:
373 396 if startnode is not None:
374 397 try:
375 398 startnode = self.repo.lookup(startnode)
376 399 except error.RepoError:
377 400 raise util.Abort(_('%s is not a valid start revision')
378 401 % startnode)
379 402 startrev = self.repo.changelog.rev(startnode)
380 403 children = {startnode: 1}
381 404 for r in self.repo.changelog.descendants([startrev]):
382 405 children[self.repo.changelog.node(r)] = 1
383 406 self.keep = children.__contains__
384 407 else:
385 408 self.keep = util.always
386 409 if rev:
387 410 self._heads = [self.repo[rev].node()]
388 411 else:
389 412 self._heads = self.repo.heads()
390 413 else:
391 414 if rev or startnode is not None:
392 415 raise util.Abort(_('hg.revs cannot be combined with '
393 416 'hg.startrev or --rev'))
394 417 nodes = set()
395 418 parents = set()
396 419 for r in scmutil.revrange(self.repo, [hgrevs]):
397 420 ctx = self.repo[r]
398 421 nodes.add(ctx.node())
399 422 parents.update(p.node() for p in ctx.parents())
400 423 self.keep = nodes.__contains__
401 424 self._heads = nodes - parents
402 425
403 426 def changectx(self, rev):
404 427 if self.lastrev != rev:
405 428 self.lastctx = self.repo[rev]
406 429 self.lastrev = rev
407 430 return self.lastctx
408 431
409 432 def parents(self, ctx):
410 433 return [p for p in ctx.parents() if p and self.keep(p.node())]
411 434
412 435 def getheads(self):
413 436 return [hex(h) for h in self._heads if self.keep(h)]
414 437
415 438 def getfile(self, name, rev):
416 439 try:
417 440 fctx = self.changectx(rev)[name]
418 441 return fctx.data(), fctx.flags()
419 442 except error.LookupError:
420 443 return None, None
421 444
422 445 def getchanges(self, rev, full):
423 446 ctx = self.changectx(rev)
424 447 parents = self.parents(ctx)
425 448 if full or not parents:
426 449 files = copyfiles = ctx.manifest()
427 450 if parents:
428 451 if self._changescache[0] == rev:
429 452 m, a, r = self._changescache[1]
430 453 else:
431 454 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
432 455 if not full:
433 456 files = m + a + r
434 457 copyfiles = m + a
435 458 # getcopies() is also run for roots and before filtering so missing
436 459 # revlogs are detected early
437 460 copies = self.getcopies(ctx, parents, copyfiles)
438 461 cleanp2 = set()
439 462 if len(parents) == 2:
440 463 cleanp2.update(self.repo.status(parents[1].node(), ctx.node(),
441 464 clean=True).clean)
442 465 changes = [(f, rev) for f in files if f not in self.ignored]
443 466 changes.sort()
444 467 return changes, copies, cleanp2
445 468
446 469 def getcopies(self, ctx, parents, files):
447 470 copies = {}
448 471 for name in files:
449 472 if name in self.ignored:
450 473 continue
451 474 try:
452 475 copysource, _copynode = ctx.filectx(name).renamed()
453 476 if copysource in self.ignored:
454 477 continue
455 478 # Ignore copy sources not in parent revisions
456 479 found = False
457 480 for p in parents:
458 481 if copysource in p:
459 482 found = True
460 483 break
461 484 if not found:
462 485 continue
463 486 copies[name] = copysource
464 487 except TypeError:
465 488 pass
466 489 except error.LookupError, e:
467 490 if not self.ignoreerrors:
468 491 raise
469 492 self.ignored.add(name)
470 493 self.ui.warn(_('ignoring: %s\n') % e)
471 494 return copies
472 495
473 496 def getcommit(self, rev):
474 497 ctx = self.changectx(rev)
475 498 parents = [p.hex() for p in self.parents(ctx)]
476 499 crev = rev
477 500
478 501 return commit(author=ctx.user(),
479 502 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
480 503 desc=ctx.description(), rev=crev, parents=parents,
481 504 branch=ctx.branch(), extra=ctx.extra(),
482 sortkey=ctx.rev(), saverev=self.saverev)
505 sortkey=ctx.rev(), saverev=self.saverev,
506 phase=ctx.phase())
483 507
484 508 def gettags(self):
485 509 # This will get written to .hgtags, filter non global tags out.
486 510 tags = [t for t in self.repo.tagslist()
487 511 if self.repo.tagtype(t[0]) == 'global']
488 512 return dict([(name, hex(node)) for name, node in tags
489 513 if self.keep(node)])
490 514
491 515 def getchangedfiles(self, rev, i):
492 516 ctx = self.changectx(rev)
493 517 parents = self.parents(ctx)
494 518 if not parents and i is None:
495 519 i = 0
496 520 changes = [], ctx.manifest().keys(), []
497 521 else:
498 522 i = i or 0
499 523 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
500 524 changes = [[f for f in l if f not in self.ignored] for l in changes]
501 525
502 526 if i == 0:
503 527 self._changescache = (rev, changes)
504 528
505 529 return changes[0] + changes[1] + changes[2]
506 530
507 531 def converted(self, rev, destrev):
508 532 if self.convertfp is None:
509 533 self.convertfp = open(self.repo.join('shamap'), 'a')
510 534 self.convertfp.write('%s %s\n' % (destrev, rev))
511 535 self.convertfp.flush()
512 536
513 537 def before(self):
514 538 self.ui.debug('run hg source pre-conversion action\n')
515 539
516 540 def after(self):
517 541 self.ui.debug('run hg source post-conversion action\n')
518 542
519 543 def hasnativeorder(self):
520 544 return True
521 545
522 546 def hasnativeclose(self):
523 547 return True
524 548
525 549 def lookuprev(self, rev):
526 550 try:
527 551 return hex(self.repo.lookup(rev))
528 552 except (error.RepoError, error.LookupError):
529 553 return None
530 554
531 555 def getbookmarks(self):
532 556 return bookmarks.listbookmarks(self.repo)
533 557
534 558 def checkrevformat(self, revstr, mapname='splicemap'):
535 559 """ Mercurial, revision string is a 40 byte hex """
536 560 self.checkhexformat(revstr, mapname)
@@ -1,470 +1,470
1 1 #require cvs
2 2
3 3 $ cvscall()
4 4 > {
5 5 > cvs -f "$@"
6 6 > }
7 7 $ hgcat()
8 8 > {
9 9 > hg --cwd src-hg cat -r tip "$1"
10 10 > }
11 11 $ echo "[extensions]" >> $HGRCPATH
12 12 $ echo "convert = " >> $HGRCPATH
13 13 $ cat > cvshooks.py <<EOF
14 14 > def cvslog(ui,repo,hooktype,log):
15 15 > print "%s hook: %d entries"%(hooktype,len(log))
16 16 >
17 17 > def cvschangesets(ui,repo,hooktype,changesets):
18 18 > print "%s hook: %d changesets"%(hooktype,len(changesets))
19 19 > EOF
20 20 $ hookpath=`pwd`
21 21 $ cat <<EOF >> $HGRCPATH
22 22 > [hooks]
23 23 > cvslog = python:$hookpath/cvshooks.py:cvslog
24 24 > cvschangesets = python:$hookpath/cvshooks.py:cvschangesets
25 25 > EOF
26 26
27 27 create cvs repository
28 28
29 29 $ mkdir cvsrepo
30 30 $ cd cvsrepo
31 31 $ CVSROOT=`pwd`
32 32 $ export CVSROOT
33 33 $ CVS_OPTIONS=-f
34 34 $ export CVS_OPTIONS
35 35 $ cd ..
36 36 $ rmdir cvsrepo
37 37 $ cvscall -q -d "$CVSROOT" init
38 38
39 39 create source directory
40 40
41 41 $ mkdir src-temp
42 42 $ cd src-temp
43 43 $ echo a > a
44 44 $ mkdir b
45 45 $ cd b
46 46 $ echo c > c
47 47 $ cd ..
48 48
49 49 import source directory
50 50
51 51 $ cvscall -q import -m import src INITIAL start
52 52 N src/a
53 53 N src/b/c
54 54
55 55 No conflicts created by this import
56 56
57 57 $ cd ..
58 58
59 59 checkout source directory
60 60
61 61 $ cvscall -q checkout src
62 62 U src/a
63 63 U src/b/c
64 64
65 65 commit a new revision changing b/c
66 66
67 67 $ cd src
68 68 $ sleep 1
69 69 $ echo c >> b/c
70 70 $ cvscall -q commit -mci0 . | grep '<--'
71 71 $TESTTMP/cvsrepo/src/b/c,v <-- *c (glob)
72 72 $ cd ..
73 73
74 74 convert fresh repo and also check localtimezone option
75 75
76 76 NOTE: This doesn't check all time zones -- it merely determines that
77 77 the configuration option is taking effect.
78 78
79 79 An arbitrary (U.S.) time zone is used here. TZ=US/Hawaii is selected
80 80 since it does not use DST (unlike other U.S. time zones) and is always
81 81 a fixed difference from UTC.
82 82
83 83 $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
84 84 initializing destination src-hg repository
85 85 connecting to $TESTTMP/cvsrepo
86 86 scanning source...
87 87 collecting CVS rlog
88 88 5 log entries
89 89 cvslog hook: 5 entries
90 90 creating changesets
91 91 3 changeset entries
92 92 cvschangesets hook: 3 changesets
93 93 sorting...
94 94 converting...
95 95 2 Initial revision
96 96 1 ci0
97 97 0 import
98 98 updating tags
99 99 $ hgcat a
100 100 a
101 101 $ hgcat b/c
102 102 c
103 103 c
104 104
105 105 convert fresh repo with --filemap
106 106
107 107 $ echo include b/c > filemap
108 108 $ hg convert --filemap filemap src src-filemap
109 109 initializing destination src-filemap repository
110 110 connecting to $TESTTMP/cvsrepo
111 111 scanning source...
112 112 collecting CVS rlog
113 113 5 log entries
114 114 cvslog hook: 5 entries
115 115 creating changesets
116 116 3 changeset entries
117 117 cvschangesets hook: 3 changesets
118 118 sorting...
119 119 converting...
120 120 2 Initial revision
121 121 1 ci0
122 122 0 import
123 123 filtering out empty revision
124 repository tip rolled back to revision 1 (undo commit)
124 repository tip rolled back to revision 1 (undo convert)
125 125 updating tags
126 126 $ hgcat b/c
127 127 c
128 128 c
129 129 $ hg -R src-filemap log --template '{rev} {desc} files: {files}\n'
130 130 2 update tags files: .hgtags
131 131 1 ci0 files: b/c
132 132 0 Initial revision files: b/c
133 133
134 134 convert full repository (issue1649)
135 135
136 136 $ cvscall -q -d "$CVSROOT" checkout -d srcfull "." | grep -v CVSROOT
137 137 U srcfull/src/a
138 138 U srcfull/src/b/c
139 139 $ ls srcfull
140 140 CVS
141 141 CVSROOT
142 142 src
143 143 $ hg convert srcfull srcfull-hg \
144 144 > | grep -v 'log entries' | grep -v 'hook:' \
145 145 > | grep -v '^[0-3] .*' # filter instable changeset order
146 146 initializing destination srcfull-hg repository
147 147 connecting to $TESTTMP/cvsrepo
148 148 scanning source...
149 149 collecting CVS rlog
150 150 creating changesets
151 151 4 changeset entries
152 152 sorting...
153 153 converting...
154 154 updating tags
155 155 $ hg cat -r tip --cwd srcfull-hg src/a
156 156 a
157 157 $ hg cat -r tip --cwd srcfull-hg src/b/c
158 158 c
159 159 c
160 160
161 161 commit new file revisions
162 162
163 163 $ cd src
164 164 $ echo a >> a
165 165 $ echo c >> b/c
166 166 $ cvscall -q commit -mci1 . | grep '<--'
167 167 $TESTTMP/cvsrepo/src/a,v <-- a
168 168 $TESTTMP/cvsrepo/src/b/c,v <-- *c (glob)
169 169 $ cd ..
170 170
171 171 convert again
172 172
173 173 $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
174 174 connecting to $TESTTMP/cvsrepo
175 175 scanning source...
176 176 collecting CVS rlog
177 177 7 log entries
178 178 cvslog hook: 7 entries
179 179 creating changesets
180 180 4 changeset entries
181 181 cvschangesets hook: 4 changesets
182 182 sorting...
183 183 converting...
184 184 0 ci1
185 185 $ hgcat a
186 186 a
187 187 a
188 188 $ hgcat b/c
189 189 c
190 190 c
191 191 c
192 192
193 193 convert again with --filemap
194 194
195 195 $ hg convert --filemap filemap src src-filemap
196 196 connecting to $TESTTMP/cvsrepo
197 197 scanning source...
198 198 collecting CVS rlog
199 199 7 log entries
200 200 cvslog hook: 7 entries
201 201 creating changesets
202 202 4 changeset entries
203 203 cvschangesets hook: 4 changesets
204 204 sorting...
205 205 converting...
206 206 0 ci1
207 207 $ hgcat b/c
208 208 c
209 209 c
210 210 c
211 211 $ hg -R src-filemap log --template '{rev} {desc} files: {files}\n'
212 212 3 ci1 files: b/c
213 213 2 update tags files: .hgtags
214 214 1 ci0 files: b/c
215 215 0 Initial revision files: b/c
216 216
217 217 commit branch
218 218
219 219 $ cd src
220 220 $ cvs -q update -r1.1 b/c
221 221 U b/c
222 222 $ cvs -q tag -b branch
223 223 T a
224 224 T b/c
225 225 $ cvs -q update -r branch > /dev/null
226 226 $ echo d >> b/c
227 227 $ cvs -q commit -mci2 . | grep '<--'
228 228 $TESTTMP/cvsrepo/src/b/c,v <-- *c (glob)
229 229 $ cd ..
230 230
231 231 convert again
232 232
233 233 $ TZ=US/Hawaii hg convert --config convert.localtimezone=True src src-hg
234 234 connecting to $TESTTMP/cvsrepo
235 235 scanning source...
236 236 collecting CVS rlog
237 237 8 log entries
238 238 cvslog hook: 8 entries
239 239 creating changesets
240 240 5 changeset entries
241 241 cvschangesets hook: 5 changesets
242 242 sorting...
243 243 converting...
244 244 0 ci2
245 245 $ hgcat b/c
246 246 c
247 247 d
248 248
249 249 convert again with --filemap
250 250
251 251 $ TZ=US/Hawaii hg convert --config convert.localtimezone=True --filemap filemap src src-filemap
252 252 connecting to $TESTTMP/cvsrepo
253 253 scanning source...
254 254 collecting CVS rlog
255 255 8 log entries
256 256 cvslog hook: 8 entries
257 257 creating changesets
258 258 5 changeset entries
259 259 cvschangesets hook: 5 changesets
260 260 sorting...
261 261 converting...
262 262 0 ci2
263 263 $ hgcat b/c
264 264 c
265 265 d
266 266 $ hg -R src-filemap log --template '{rev} {desc} files: {files}\n'
267 267 4 ci2 files: b/c
268 268 3 ci1 files: b/c
269 269 2 update tags files: .hgtags
270 270 1 ci0 files: b/c
271 271 0 Initial revision files: b/c
272 272
273 273 commit a new revision with funny log message
274 274
275 275 $ cd src
276 276 $ sleep 1
277 277 $ echo e >> a
278 278 $ cvscall -q commit -m'funny
279 279 > ----------------------------
280 280 > log message' . | grep '<--' |\
281 281 > sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g'
282 282 checking in src/a,v
283 283
284 284 commit new file revisions with some fuzz
285 285
286 286 $ sleep 1
287 287 $ echo f >> a
288 288 $ cvscall -q commit -mfuzzy . | grep '<--'
289 289 $TESTTMP/cvsrepo/src/a,v <-- a
290 290 $ sleep 4 # the two changes will be split if fuzz < 4
291 291 $ echo g >> b/c
292 292 $ cvscall -q commit -mfuzzy . | grep '<--'
293 293 $TESTTMP/cvsrepo/src/b/c,v <-- *c (glob)
294 294 $ cd ..
295 295
296 296 convert again
297 297
298 298 $ TZ=US/Hawaii hg convert --config convert.cvsps.fuzz=2 --config convert.localtimezone=True src src-hg
299 299 connecting to $TESTTMP/cvsrepo
300 300 scanning source...
301 301 collecting CVS rlog
302 302 11 log entries
303 303 cvslog hook: 11 entries
304 304 creating changesets
305 305 8 changeset entries
306 306 cvschangesets hook: 8 changesets
307 307 sorting...
308 308 converting...
309 309 2 funny
310 310 1 fuzzy
311 311 0 fuzzy
312 312 $ hg -R src-hg log -G --template '{rev} ({branches}) {desc} date: {date|date} files: {files}\n'
313 313 o 8 (branch) fuzzy date: * -1000 files: b/c (glob)
314 314 |
315 315 o 7 (branch) fuzzy date: * -1000 files: a (glob)
316 316 |
317 317 o 6 (branch) funny
318 318 | ----------------------------
319 319 | log message date: * -1000 files: a (glob)
320 320 o 5 (branch) ci2 date: * -1000 files: b/c (glob)
321 321
322 322 o 4 () ci1 date: * -1000 files: a b/c (glob)
323 323 |
324 324 o 3 () update tags date: * +0000 files: .hgtags (glob)
325 325 |
326 326 | o 2 (INITIAL) import date: * -1000 files: (glob)
327 327 | |
328 328 o | 1 () ci0 date: * -1000 files: b/c (glob)
329 329 |/
330 330 o 0 () Initial revision date: * -1000 files: a b/c (glob)
331 331
332 332
333 333 testing debugcvsps
334 334
335 335 $ cd src
336 336 $ hg debugcvsps --fuzz=2
337 337 collecting CVS rlog
338 338 11 log entries
339 339 cvslog hook: 11 entries
340 340 creating changesets
341 341 10 changeset entries
342 342 cvschangesets hook: 10 changesets
343 343 ---------------------
344 344 PatchSet 1
345 345 Date: * (glob)
346 346 Author: * (glob)
347 347 Branch: HEAD
348 348 Tag: (none)
349 349 Branchpoints: INITIAL
350 350 Log:
351 351 Initial revision
352 352
353 353 Members:
354 354 a:INITIAL->1.1
355 355
356 356 ---------------------
357 357 PatchSet 2
358 358 Date: * (glob)
359 359 Author: * (glob)
360 360 Branch: HEAD
361 361 Tag: (none)
362 362 Branchpoints: INITIAL, branch
363 363 Log:
364 364 Initial revision
365 365
366 366 Members:
367 367 b/c:INITIAL->1.1
368 368
369 369 ---------------------
370 370 PatchSet 3
371 371 Date: * (glob)
372 372 Author: * (glob)
373 373 Branch: INITIAL
374 374 Tag: start
375 375 Log:
376 376 import
377 377
378 378 Members:
379 379 a:1.1->1.1.1.1
380 380 b/c:1.1->1.1.1.1
381 381
382 382 ---------------------
383 383 PatchSet 4
384 384 Date: * (glob)
385 385 Author: * (glob)
386 386 Branch: HEAD
387 387 Tag: (none)
388 388 Log:
389 389 ci0
390 390
391 391 Members:
392 392 b/c:1.1->1.2
393 393
394 394 ---------------------
395 395 PatchSet 5
396 396 Date: * (glob)
397 397 Author: * (glob)
398 398 Branch: HEAD
399 399 Tag: (none)
400 400 Branchpoints: branch
401 401 Log:
402 402 ci1
403 403
404 404 Members:
405 405 a:1.1->1.2
406 406
407 407 ---------------------
408 408 PatchSet 6
409 409 Date: * (glob)
410 410 Author: * (glob)
411 411 Branch: HEAD
412 412 Tag: (none)
413 413 Log:
414 414 ci1
415 415
416 416 Members:
417 417 b/c:1.2->1.3
418 418
419 419 ---------------------
420 420 PatchSet 7
421 421 Date: * (glob)
422 422 Author: * (glob)
423 423 Branch: branch
424 424 Tag: (none)
425 425 Log:
426 426 ci2
427 427
428 428 Members:
429 429 b/c:1.1->1.1.2.1
430 430
431 431 ---------------------
432 432 PatchSet 8
433 433 Date: * (glob)
434 434 Author: * (glob)
435 435 Branch: branch
436 436 Tag: (none)
437 437 Log:
438 438 funny
439 439 ----------------------------
440 440 log message
441 441
442 442 Members:
443 443 a:1.2->1.2.2.1
444 444
445 445 ---------------------
446 446 PatchSet 9
447 447 Date: * (glob)
448 448 Author: * (glob)
449 449 Branch: branch
450 450 Tag: (none)
451 451 Log:
452 452 fuzzy
453 453
454 454 Members:
455 455 a:1.2.2.1->1.2.2.2
456 456
457 457 ---------------------
458 458 PatchSet 10
459 459 Date: * (glob)
460 460 Author: * (glob)
461 461 Branch: branch
462 462 Tag: (none)
463 463 Log:
464 464 fuzzy
465 465
466 466 Members:
467 467 b/c:1.1.2.1->1.1.2.2
468 468
469 469
470 470 $ cd ..
@@ -1,673 +1,673
1 1
2 2 $ HGMERGE=true; export HGMERGE
3 3 $ echo '[extensions]' >> $HGRCPATH
4 4 $ echo 'convert =' >> $HGRCPATH
5 5 $ glog()
6 6 > {
7 7 > hg log -G --template '{rev} "{desc}" files: {files}\n' "$@"
8 8 > }
9 9 $ hg init source
10 10 $ cd source
11 11 $ echo foo > foo
12 12 $ echo baz > baz
13 13 $ mkdir -p dir/subdir
14 14 $ echo dir/file >> dir/file
15 15 $ echo dir/file2 >> dir/file2
16 16 $ echo dir/file3 >> dir/file3 # to be corrupted in rev 0
17 17 $ echo dir/subdir/file3 >> dir/subdir/file3
18 18 $ echo dir/subdir/file4 >> dir/subdir/file4
19 19 $ hg ci -d '0 0' -qAm '0: add foo baz dir/'
20 20 $ echo bar > bar
21 21 $ echo quux > quux
22 22 $ echo dir/file4 >> dir/file4 # to be corrupted in rev 1
23 23 $ hg copy foo copied
24 24 $ hg ci -d '1 0' -qAm '1: add bar quux; copy foo to copied'
25 25 $ echo >> foo
26 26 $ hg ci -d '2 0' -m '2: change foo'
27 27 $ hg up -qC 1
28 28 $ echo >> bar
29 29 $ echo >> quux
30 30 $ hg ci -d '3 0' -m '3: change bar quux'
31 31 created new head
32 32 $ hg up -qC 2
33 33 $ hg merge -qr 3
34 34 $ echo >> bar
35 35 $ echo >> baz
36 36 $ hg ci -d '4 0' -m '4: first merge; change bar baz'
37 37 $ echo >> bar
38 38 $ echo 1 >> baz
39 39 $ echo >> quux
40 40 $ hg ci -d '5 0' -m '5: change bar baz quux'
41 41 $ hg up -qC 4
42 42 $ echo >> foo
43 43 $ echo 2 >> baz
44 44 $ hg ci -d '6 0' -m '6: change foo baz'
45 45 created new head
46 46 $ hg up -qC 5
47 47 $ hg merge -qr 6
48 48 $ echo >> bar
49 49 $ hg ci -d '7 0' -m '7: second merge; change bar'
50 50 $ echo >> foo
51 51 $ hg ci -m '8: change foo'
52 52 $ glog
53 53 @ 8 "8: change foo" files: foo
54 54 |
55 55 o 7 "7: second merge; change bar" files: bar baz
56 56 |\
57 57 | o 6 "6: change foo baz" files: baz foo
58 58 | |
59 59 o | 5 "5: change bar baz quux" files: bar baz quux
60 60 |/
61 61 o 4 "4: first merge; change bar baz" files: bar baz
62 62 |\
63 63 | o 3 "3: change bar quux" files: bar quux
64 64 | |
65 65 o | 2 "2: change foo" files: foo
66 66 |/
67 67 o 1 "1: add bar quux; copy foo to copied" files: bar copied dir/file4 quux
68 68 |
69 69 o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 dir/file3 dir/subdir/file3 dir/subdir/file4 foo
70 70
71 71
72 72 final file versions in this repo:
73 73
74 74 $ hg manifest --debug
75 75 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
76 76 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
77 77 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
78 78 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir/file
79 79 75e6d3f8328f5f6ace6bf10b98df793416a09dca 644 dir/file2
80 80 e96dce0bc6a217656a3a410e5e6bec2c4f42bf7c 644 dir/file3
81 81 6edd55f559cdce67132b12ca09e09cee08b60442 644 dir/file4
82 82 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir/subdir/file3
83 83 57a1c1511590f3de52874adfa04effe8a77d64af 644 dir/subdir/file4
84 84 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
85 85 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
86 86 $ hg debugrename copied
87 87 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
88 88
89 89 $ cd ..
90 90
91 91
92 92 Test interaction with startrev and verify that changing it is handled properly:
93 93
94 94 $ > empty
95 95 $ hg convert --filemap empty source movingstart --config convert.hg.startrev=3 -r4
96 96 initializing destination movingstart repository
97 97 scanning source...
98 98 sorting...
99 99 converting...
100 100 1 3: change bar quux
101 101 0 4: first merge; change bar baz
102 102 $ hg convert --filemap empty source movingstart
103 103 scanning source...
104 104 sorting...
105 105 converting...
106 106 3 5: change bar baz quux
107 107 2 6: change foo baz
108 108 1 7: second merge; change bar
109 109 warning: af455ce4166b3c9c88e6309c2b9332171dcea595 parent 61e22ca76c3b3e93df20338c4e02ce286898e825 is missing
110 110 warning: cf908b3eeedc301c9272ebae931da966d5b326c7 parent 59e1ab45c888289513b7354484dac8a88217beab is missing
111 111 0 8: change foo
112 112
113 113
114 114 splitrepo tests
115 115
116 116 $ splitrepo()
117 117 > {
118 118 > msg="$1"
119 119 > files="$2"
120 120 > opts=$3
121 121 > echo "% $files: $msg"
122 122 > prefix=`echo "$files" | sed -e 's/ /-/g'`
123 123 > fmap="$prefix.fmap"
124 124 > repo="$prefix.repo"
125 125 > for i in $files; do
126 126 > echo "include $i" >> "$fmap"
127 127 > done
128 128 > hg -q convert $opts --filemap "$fmap" --datesort source "$repo"
129 129 > hg up -q -R "$repo"
130 130 > glog -R "$repo"
131 131 > hg -R "$repo" manifest --debug
132 132 > }
133 133 $ splitrepo 'skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd' foo
134 134 % foo: skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd
135 135 @ 3 "8: change foo" files: foo
136 136 |
137 137 o 2 "6: change foo baz" files: foo
138 138 |
139 139 o 1 "2: change foo" files: foo
140 140 |
141 141 o 0 "0: add foo baz dir/" files: foo
142 142
143 143 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
144 144 $ splitrepo 'merges are not merges anymore' bar
145 145 % bar: merges are not merges anymore
146 146 @ 4 "7: second merge; change bar" files: bar
147 147 |
148 148 o 3 "5: change bar baz quux" files: bar
149 149 |
150 150 o 2 "4: first merge; change bar baz" files: bar
151 151 |
152 152 o 1 "3: change bar quux" files: bar
153 153 |
154 154 o 0 "1: add bar quux; copy foo to copied" files: bar
155 155
156 156 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
157 157 $ splitrepo '1st merge is not a merge anymore; 2nd still is' baz
158 158 % baz: 1st merge is not a merge anymore; 2nd still is
159 159 @ 4 "7: second merge; change bar" files: baz
160 160 |\
161 161 | o 3 "6: change foo baz" files: baz
162 162 | |
163 163 o | 2 "5: change bar baz quux" files: baz
164 164 |/
165 165 o 1 "4: first merge; change bar baz" files: baz
166 166 |
167 167 o 0 "0: add foo baz dir/" files: baz
168 168
169 169 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
170 170 $ splitrepo 'we add additional merges when they are interesting' 'foo quux'
171 171 % foo quux: we add additional merges when they are interesting
172 172 @ 8 "8: change foo" files: foo
173 173 |
174 174 o 7 "7: second merge; change bar" files:
175 175 |\
176 176 | o 6 "6: change foo baz" files: foo
177 177 | |
178 178 o | 5 "5: change bar baz quux" files: quux
179 179 |/
180 180 o 4 "4: first merge; change bar baz" files:
181 181 |\
182 182 | o 3 "3: change bar quux" files: quux
183 183 | |
184 184 o | 2 "2: change foo" files: foo
185 185 |/
186 186 o 1 "1: add bar quux; copy foo to copied" files: quux
187 187 |
188 188 o 0 "0: add foo baz dir/" files: foo
189 189
190 190 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
191 191 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
192 192 $ splitrepo 'partial conversion' 'bar quux' '-r 3'
193 193 % bar quux: partial conversion
194 194 @ 1 "3: change bar quux" files: bar quux
195 195 |
196 196 o 0 "1: add bar quux; copy foo to copied" files: bar quux
197 197
198 198 b79105bedc55102f394e90a789c9c380117c1b4a 644 bar
199 199 db0421cc6b685a458c8d86c7d5c004f94429ea23 644 quux
200 200 $ splitrepo 'complete the partial conversion' 'bar quux'
201 201 % bar quux: complete the partial conversion
202 202 @ 4 "7: second merge; change bar" files: bar
203 203 |
204 204 o 3 "5: change bar baz quux" files: bar quux
205 205 |
206 206 o 2 "4: first merge; change bar baz" files: bar
207 207 |
208 208 o 1 "3: change bar quux" files: bar quux
209 209 |
210 210 o 0 "1: add bar quux; copy foo to copied" files: bar quux
211 211
212 212 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
213 213 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
214 214 $ rm -r foo.repo
215 215 $ splitrepo 'partial conversion' 'foo' '-r 3'
216 216 % foo: partial conversion
217 217 @ 0 "0: add foo baz dir/" files: foo
218 218
219 219 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
220 220 $ splitrepo 'complete the partial conversion' 'foo'
221 221 % foo: complete the partial conversion
222 222 @ 3 "8: change foo" files: foo
223 223 |
224 224 o 2 "6: change foo baz" files: foo
225 225 |
226 226 o 1 "2: change foo" files: foo
227 227 |
228 228 o 0 "0: add foo baz dir/" files: foo
229 229
230 230 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
231 231 $ splitrepo 'copied file; source not included in new repo' copied
232 232 % copied: copied file; source not included in new repo
233 233 @ 0 "1: add bar quux; copy foo to copied" files: copied
234 234
235 235 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 copied
236 236 $ hg --cwd copied.repo debugrename copied
237 237 copied not renamed
238 238 $ splitrepo 'copied file; source included in new repo' 'foo copied'
239 239 % foo copied: copied file; source included in new repo
240 240 @ 4 "8: change foo" files: foo
241 241 |
242 242 o 3 "6: change foo baz" files: foo
243 243 |
244 244 o 2 "2: change foo" files: foo
245 245 |
246 246 o 1 "1: add bar quux; copy foo to copied" files: copied
247 247 |
248 248 o 0 "0: add foo baz dir/" files: foo
249 249
250 250 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
251 251 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
252 252 $ hg --cwd foo-copied.repo debugrename copied
253 253 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
254 254
255 255 verify the top level 'include .' if there is no other includes:
256 256
257 257 $ echo "exclude something" > default.fmap
258 258 $ hg convert -q --filemap default.fmap -r1 source dummydest2
259 259 $ hg -R dummydest2 log --template '{rev} {node|short} {desc|firstline}\n'
260 260 1 61e22ca76c3b 1: add bar quux; copy foo to copied
261 261 0 c085cf2ee7fe 0: add foo baz dir/
262 262
263 263 $ echo "include somethingelse" >> default.fmap
264 264 $ hg convert -q --filemap default.fmap -r1 source dummydest3
265 265 $ hg -R dummydest3 log --template '{rev} {node|short} {desc|firstline}\n'
266 266
267 267 $ echo "include ." >> default.fmap
268 268 $ hg convert -q --filemap default.fmap -r1 source dummydest4
269 269 $ hg -R dummydest4 log --template '{rev} {node|short} {desc|firstline}\n'
270 270 1 61e22ca76c3b 1: add bar quux; copy foo to copied
271 271 0 c085cf2ee7fe 0: add foo baz dir/
272 272
273 273 ensure that the filemap contains duplicated slashes (issue3612)
274 274
275 275 $ cat > renames.fmap <<EOF
276 276 > include dir
277 277 > exclude dir/file2
278 278 > rename dir dir2//dir3
279 279 > include foo
280 280 > include copied
281 281 > rename foo foo2/
282 282 > rename copied ./copied2
283 283 > exclude dir/subdir
284 284 > include dir/subdir/file3
285 285 > EOF
286 286 $ rm source/.hg/store/data/dir/file3.i
287 287 $ rm source/.hg/store/data/dir/file4.i
288 288 $ hg -q convert --filemap renames.fmap --datesort source dummydest
289 289 abort: data/dir/file3.i@e96dce0bc6a2: no match found!
290 290 [255]
291 291 $ hg -q convert --filemap renames.fmap --datesort --config convert.hg.ignoreerrors=1 source renames.repo
292 292 ignoring: data/dir/file3.i@e96dce0bc6a2: no match found
293 293 ignoring: data/dir/file4.i@6edd55f559cd: no match found
294 294 $ hg up -q -R renames.repo
295 295 $ glog -R renames.repo
296 296 @ 4 "8: change foo" files: foo2
297 297 |
298 298 o 3 "6: change foo baz" files: foo2
299 299 |
300 300 o 2 "2: change foo" files: foo2
301 301 |
302 302 o 1 "1: add bar quux; copy foo to copied" files: copied2
303 303 |
304 304 o 0 "0: add foo baz dir/" files: dir2/dir3/file dir2/dir3/subdir/file3 foo2
305 305
306 306 $ hg -R renames.repo verify
307 307 checking changesets
308 308 checking manifests
309 309 crosschecking files in changesets and manifests
310 310 checking files
311 311 4 files, 5 changesets, 7 total revisions
312 312
313 313 $ hg -R renames.repo manifest --debug
314 314 d43feacba7a4f1f2080dde4a4b985bd8a0236d46 644 copied2
315 315 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir2/dir3/file
316 316 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir2/dir3/subdir/file3
317 317 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo2
318 318 $ hg --cwd renames.repo debugrename copied2
319 319 copied2 renamed from foo2:2ed2a3912a0b24502043eae84ee4b279c18b90dd
320 320
321 321 copied:
322 322
323 323 $ hg --cwd source cat copied
324 324 foo
325 325
326 326 copied2:
327 327
328 328 $ hg --cwd renames.repo cat copied2
329 329 foo
330 330
331 331 filemap errors
332 332
333 333 $ cat > errors.fmap <<EOF
334 334 > include dir/ # beware that comments changes error line numbers!
335 335 > exclude /dir
336 336 > rename dir//dir /dir//dir/ "out of sync"
337 337 > include
338 338 > EOF
339 339 $ hg -q convert --filemap errors.fmap source errors.repo
340 340 errors.fmap:3: superfluous / in include '/dir'
341 341 errors.fmap:3: superfluous / in rename '/dir'
342 342 errors.fmap:4: unknown directive 'out of sync'
343 343 errors.fmap:5: path to exclude is missing
344 344 abort: errors in filemap
345 345 [255]
346 346
347 347 test branch closing revision pruning if branch is pruned
348 348
349 349 $ hg init branchpruning
350 350 $ cd branchpruning
351 351 $ hg branch foo
352 352 marked working directory as branch foo
353 353 (branches are permanent and global, did you want a bookmark?)
354 354 $ echo a > a
355 355 $ hg ci -Am adda
356 356 adding a
357 357 $ hg ci --close-branch -m closefoo
358 358 $ hg up 0
359 359 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
360 360 $ hg branch empty
361 361 marked working directory as branch empty
362 362 (branches are permanent and global, did you want a bookmark?)
363 363 $ hg ci -m emptybranch
364 364 $ hg ci --close-branch -m closeempty
365 365 $ hg up 0
366 366 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 367 $ hg branch default
368 368 marked working directory as branch default
369 369 (branches are permanent and global, did you want a bookmark?)
370 370 $ echo b > b
371 371 $ hg ci -Am addb
372 372 adding b
373 373 $ hg ci --close-branch -m closedefault
374 374 $ cat > filemap <<EOF
375 375 > include b
376 376 > EOF
377 377 $ cd ..
378 378 $ hg convert branchpruning branchpruning-hg1
379 379 initializing destination branchpruning-hg1 repository
380 380 scanning source...
381 381 sorting...
382 382 converting...
383 383 5 adda
384 384 4 closefoo
385 385 3 emptybranch
386 386 2 closeempty
387 387 1 addb
388 388 0 closedefault
389 389 $ glog -R branchpruning-hg1
390 390 _ 5 "closedefault" files:
391 391 |
392 392 o 4 "addb" files: b
393 393 |
394 394 | _ 3 "closeempty" files:
395 395 | |
396 396 | o 2 "emptybranch" files:
397 397 |/
398 398 | _ 1 "closefoo" files:
399 399 |/
400 400 o 0 "adda" files: a
401 401
402 402
403 403 exercise incremental conversion at the same time
404 404
405 405 $ hg convert -r0 --filemap branchpruning/filemap branchpruning branchpruning-hg2
406 406 initializing destination branchpruning-hg2 repository
407 407 scanning source...
408 408 sorting...
409 409 converting...
410 410 0 adda
411 411 $ hg convert -r4 --filemap branchpruning/filemap branchpruning branchpruning-hg2
412 412 scanning source...
413 413 sorting...
414 414 converting...
415 415 0 addb
416 416 $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2
417 417 scanning source...
418 418 sorting...
419 419 converting...
420 420 3 closefoo
421 421 2 emptybranch
422 422 1 closeempty
423 423 0 closedefault
424 424 $ glog -R branchpruning-hg2
425 425 _ 1 "closedefault" files:
426 426 |
427 427 o 0 "addb" files: b
428 428
429 429
430 430 Test rebuilding of map with unknown revisions in shamap - it used to crash
431 431
432 432 $ cd branchpruning
433 433 $ hg up -r 2
434 434 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
435 435 $ hg merge 4
436 436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 437 (branch merge, don't forget to commit)
438 438 $ hg ci -m 'merging something'
439 439 $ cd ..
440 440 $ echo "53792d18237d2b64971fa571936869156655338d 6d955580116e82c4b029bd30f321323bae71a7f0" >> branchpruning-hg2/.hg/shamap
441 441 $ hg convert --filemap branchpruning/filemap branchpruning branchpruning-hg2 --debug --config progress.debug=true
442 442 run hg source pre-conversion action
443 443 run hg sink pre-conversion action
444 444 scanning source...
445 445 scanning: 1 revisions
446 446 sorting...
447 447 converting...
448 448 0 merging something
449 449 source: 2503605b178fe50e8fbbb0e77b97939540aa8c87
450 450 converting: 0/1 revisions (0.00%)
451 451 unknown revmap source: 53792d18237d2b64971fa571936869156655338d
452 452 run hg sink post-conversion action
453 453 run hg source post-conversion action
454 454
455 455
456 456 filemap rename undoing revision rename
457 457
458 458 $ hg init renameundo
459 459 $ cd renameundo
460 460 $ echo 1 > a
461 461 $ echo 1 > c
462 462 $ hg ci -qAm add
463 463 $ hg mv -q a b/a
464 464 $ hg mv -q c b/c
465 465 $ hg ci -qm rename
466 466 $ echo 2 > b/a
467 467 $ echo 2 > b/c
468 468 $ hg ci -qm modify
469 469 $ cd ..
470 470
471 471 $ echo "rename b ." > renameundo.fmap
472 472 $ hg convert --filemap renameundo.fmap renameundo renameundo2
473 473 initializing destination renameundo2 repository
474 474 scanning source...
475 475 sorting...
476 476 converting...
477 477 2 add
478 478 1 rename
479 479 filtering out empty revision
480 repository tip rolled back to revision 0 (undo commit)
480 repository tip rolled back to revision 0 (undo convert)
481 481 0 modify
482 482 $ glog -R renameundo2
483 483 o 1 "modify" files: a c
484 484 |
485 485 o 0 "add" files: a c
486 486
487 487
488 488
489 489 test merge parents/empty merges pruning
490 490
491 491 $ glog()
492 492 > {
493 493 > hg log -G --template '{rev}:{node|short}@{branch} "{desc}" files: {files}\n' "$@"
494 494 > }
495 495
496 496 test anonymous branch pruning
497 497
498 498 $ hg init anonymousbranch
499 499 $ cd anonymousbranch
500 500 $ echo a > a
501 501 $ echo b > b
502 502 $ hg ci -Am add
503 503 adding a
504 504 adding b
505 505 $ echo a >> a
506 506 $ hg ci -m changea
507 507 $ hg up 0
508 508 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 509 $ echo b >> b
510 510 $ hg ci -m changeb
511 511 created new head
512 512 $ hg up 1
513 513 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 514 $ hg merge
515 515 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 516 (branch merge, don't forget to commit)
517 517 $ hg ci -m merge
518 518 $ cd ..
519 519
520 520 $ cat > filemap <<EOF
521 521 > include a
522 522 > EOF
523 523 $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg
524 524 initializing destination anonymousbranch-hg repository
525 525 scanning source...
526 526 sorting...
527 527 converting...
528 528 3 add
529 529 2 changea
530 530 1 changeb
531 531 0 merge
532 532 $ glog -R anonymousbranch
533 533 @ 3:c71d5201a498@default "merge" files:
534 534 |\
535 535 | o 2:607eb44b17f9@default "changeb" files: b
536 536 | |
537 537 o | 1:1f60ea617824@default "changea" files: a
538 538 |/
539 539 o 0:0146e6129113@default "add" files: a b
540 540
541 541 $ glog -R anonymousbranch-hg
542 542 o 1:cda818e7219b@default "changea" files: a
543 543 |
544 544 o 0:c334dc3be0da@default "add" files: a
545 545
546 546 $ cat anonymousbranch-hg/.hg/shamap
547 547 0146e6129113dba9ac90207cfdf2d7ed35257ae5 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916
548 548 1f60ea61782421edf8d051ff4fcb61b330f26a4a cda818e7219b5f7f3fb9f49780054ed6a1905ec3
549 549 607eb44b17f9348cd5cbd26e16af87ba77b0b037 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916
550 550 c71d5201a498b2658d105a6bf69d7a0df2649aea cda818e7219b5f7f3fb9f49780054ed6a1905ec3
551 551
552 552 $ cat > filemap <<EOF
553 553 > include b
554 554 > EOF
555 555 $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg2
556 556 initializing destination anonymousbranch-hg2 repository
557 557 scanning source...
558 558 sorting...
559 559 converting...
560 560 3 add
561 561 2 changea
562 562 1 changeb
563 563 0 merge
564 564 $ glog -R anonymousbranch
565 565 @ 3:c71d5201a498@default "merge" files:
566 566 |\
567 567 | o 2:607eb44b17f9@default "changeb" files: b
568 568 | |
569 569 o | 1:1f60ea617824@default "changea" files: a
570 570 |/
571 571 o 0:0146e6129113@default "add" files: a b
572 572
573 573 $ glog -R anonymousbranch-hg2
574 574 o 1:62dd350b0df6@default "changeb" files: b
575 575 |
576 576 o 0:4b9ced861657@default "add" files: b
577 577
578 578 $ cat anonymousbranch-hg2/.hg/shamap
579 579 0146e6129113dba9ac90207cfdf2d7ed35257ae5 4b9ced86165703791653059a1db6ed864630a523
580 580 1f60ea61782421edf8d051ff4fcb61b330f26a4a 4b9ced86165703791653059a1db6ed864630a523
581 581 607eb44b17f9348cd5cbd26e16af87ba77b0b037 62dd350b0df695f7d2c82a02e0499b16fd790f22
582 582 c71d5201a498b2658d105a6bf69d7a0df2649aea 62dd350b0df695f7d2c82a02e0499b16fd790f22
583 583
584 584 test named branch pruning
585 585
586 586 $ hg init namedbranch
587 587 $ cd namedbranch
588 588 $ echo a > a
589 589 $ echo b > b
590 590 $ hg ci -Am add
591 591 adding a
592 592 adding b
593 593 $ echo a >> a
594 594 $ hg ci -m changea
595 595 $ hg up 0
596 596 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 597 $ hg branch foo
598 598 marked working directory as branch foo
599 599 (branches are permanent and global, did you want a bookmark?)
600 600 $ echo b >> b
601 601 $ hg ci -m changeb
602 602 $ hg up default
603 603 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
604 604 $ hg merge foo
605 605 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
606 606 (branch merge, don't forget to commit)
607 607 $ hg ci -m merge
608 608 $ cd ..
609 609
610 610 $ cat > filemap <<EOF
611 611 > include a
612 612 > EOF
613 613 $ hg convert --filemap filemap namedbranch namedbranch-hg
614 614 initializing destination namedbranch-hg repository
615 615 scanning source...
616 616 sorting...
617 617 converting...
618 618 3 add
619 619 2 changea
620 620 1 changeb
621 621 0 merge
622 622 $ glog -R namedbranch
623 623 @ 3:73899bcbe45c@default "merge" files:
624 624 |\
625 625 | o 2:8097982d19fc@foo "changeb" files: b
626 626 | |
627 627 o | 1:1f60ea617824@default "changea" files: a
628 628 |/
629 629 o 0:0146e6129113@default "add" files: a b
630 630
631 631 $ glog -R namedbranch-hg
632 632 o 1:cda818e7219b@default "changea" files: a
633 633 |
634 634 o 0:c334dc3be0da@default "add" files: a
635 635
636 636
637 637 $ cd namedbranch
638 638 $ hg --config extensions.mq= strip tip
639 639 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
640 640 saved backup bundle to $TESTTMP/namedbranch/.hg/strip-backup/73899bcbe45c-92adf160-backup.hg (glob)
641 641 $ hg up foo
642 642 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
643 643 $ hg merge default
644 644 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 645 (branch merge, don't forget to commit)
646 646 $ hg ci -m merge
647 647 $ cd ..
648 648
649 649 $ hg convert --filemap filemap namedbranch namedbranch-hg2
650 650 initializing destination namedbranch-hg2 repository
651 651 scanning source...
652 652 sorting...
653 653 converting...
654 654 3 add
655 655 2 changea
656 656 1 changeb
657 657 0 merge
658 658 $ glog -R namedbranch
659 659 @ 3:e1959de76e1b@foo "merge" files:
660 660 |\
661 661 | o 2:8097982d19fc@foo "changeb" files: b
662 662 | |
663 663 o | 1:1f60ea617824@default "changea" files: a
664 664 |/
665 665 o 0:0146e6129113@default "add" files: a b
666 666
667 667 $ glog -R namedbranch-hg2
668 668 o 2:dcf314454667@foo "merge" files:
669 669 |\
670 670 | o 1:cda818e7219b@default "changea" files: a
671 671 |/
672 672 o 0:c334dc3be0da@default "add" files: a
673 673
@@ -1,552 +1,564
1 1
2 2 $ cat >> $HGRCPATH <<EOF
3 3 > [extensions]
4 4 > convert=
5 5 > [convert]
6 6 > hg.saverev=False
7 7 > EOF
8 8 $ hg init orig
9 9 $ cd orig
10 10 $ echo foo > foo
11 11 $ echo bar > bar
12 12 $ hg ci -qAm 'add foo and bar'
13 13 $ hg rm foo
14 14 $ hg ci -m 'remove foo'
15 15 $ mkdir foo
16 16 $ echo file > foo/file
17 17 $ hg ci -qAm 'add foo/file'
18 18 $ hg tag some-tag
19 19 $ hg tag -l local-tag
20 20 $ hg log
21 21 changeset: 3:593cbf6fb2b4
22 22 tag: local-tag
23 23 tag: tip
24 24 user: test
25 25 date: Thu Jan 01 00:00:00 1970 +0000
26 26 summary: Added tag some-tag for changeset ad681a868e44
27 27
28 28 changeset: 2:ad681a868e44
29 29 tag: some-tag
30 30 user: test
31 31 date: Thu Jan 01 00:00:00 1970 +0000
32 32 summary: add foo/file
33 33
34 34 changeset: 1:cbba8ecc03b7
35 35 user: test
36 36 date: Thu Jan 01 00:00:00 1970 +0000
37 37 summary: remove foo
38 38
39 39 changeset: 0:327daa9251fa
40 40 user: test
41 41 date: Thu Jan 01 00:00:00 1970 +0000
42 42 summary: add foo and bar
43 43
44 $ hg phase --public -r tip
44 45 $ cd ..
45 46 $ hg convert orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
46 47 initializing destination new repository
47 48 scanning source...
48 49 sorting...
49 50 converting...
50 51 3 add foo and bar
51 52 2 remove foo
52 53 1 add foo/file
53 54 0 Added tag some-tag for changeset ad681a868e44
54 55 $ cd new
56 $ hg log -G --template '{rev} {node|short} ({phase}) "{desc}"\n'
57 o 3 593cbf6fb2b4 (public) "Added tag some-tag for changeset ad681a868e44"
58 |
59 o 2 ad681a868e44 (public) "add foo/file"
60 |
61 o 1 cbba8ecc03b7 (public) "remove foo"
62 |
63 o 0 327daa9251fa (public) "add foo and bar"
64
65
55 66 $ hg out ../orig
56 67 comparing with ../orig
57 68 searching for changes
58 69 no changes found
59 70 [1]
60 71
61 72 dirstate should be empty:
62 73
63 74 $ hg debugstate
64 75 $ hg parents -q
65 76 $ hg up -C
66 77 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 78 $ hg copy bar baz
68 79
69 80 put something in the dirstate:
70 81
71 82 $ hg debugstate > debugstate
72 83 $ grep baz debugstate
73 84 a 0 -1 unset baz
74 85 copy: bar -> baz
75 86
76 87 add a new revision in the original repo
77 88
78 89 $ cd ../orig
79 90 $ echo baz > baz
80 91 $ hg ci -qAm 'add baz'
81 92 $ cd ..
82 93 $ hg convert orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
83 94 scanning source...
84 95 sorting...
85 96 converting...
86 97 0 add baz
87 98 $ cd new
88 99 $ hg out ../orig
89 100 comparing with ../orig
90 101 searching for changes
91 102 no changes found
92 103 [1]
93 104
94 105 dirstate should be the same (no output below):
95 106
96 107 $ hg debugstate > new-debugstate
97 108 $ diff debugstate new-debugstate
98 109
99 110 no copies
100 111
101 112 $ hg up -C
102 113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
103 114 $ hg debugrename baz
104 115 baz not renamed
105 116 $ cd ..
106 117
107 118 test tag rewriting
108 119
109 120 $ cat > filemap <<EOF
110 121 > exclude foo
111 122 > EOF
112 123 $ hg convert --filemap filemap orig new-filemap 2>&1 | grep -v 'subversion python bindings could not be loaded'
113 124 initializing destination new-filemap repository
114 125 scanning source...
115 126 sorting...
116 127 converting...
117 128 4 add foo and bar
118 129 3 remove foo
119 130 2 add foo/file
120 131 1 Added tag some-tag for changeset ad681a868e44
121 132 0 add baz
122 133 $ cd new-filemap
123 134 $ hg tags
124 135 tip 2:3c74706b1ff8
125 136 some-tag 0:ba8636729451
126 137 $ cd ..
127 138
128 139
129 140 Test cases for hg-hg roundtrip
130 141
131 142 Helper
132 143
133 144 $ glog()
134 145 > {
135 > hg log -G --template '{rev} {node|short} "{desc}" files: {files}\n' $*
146 > hg log -G --template '{rev} {node|short} ({phase}) "{desc}" files: {files}\n' $*
136 147 > }
137 148
138 149 Create a tricky source repo
139 150
140 151 $ hg init source
141 152 $ cd source
142 153
143 154 $ echo 0 > 0
144 155 $ hg ci -Aqm '0: add 0'
145 156 $ echo a > a
146 157 $ mkdir dir
147 158 $ echo b > dir/b
148 159 $ hg ci -qAm '1: add a and dir/b'
149 160 $ echo c > dir/c
150 161 $ hg ci -qAm '2: add dir/c'
151 162 $ hg copy a e
152 163 $ echo b >> b
153 164 $ hg ci -qAm '3: copy a to e, change b'
154 165 $ hg up -qr -3
155 166 $ echo a >> a
156 167 $ hg ci -qAm '4: change a'
157 168 $ hg merge
158 169 merging a and e to e
159 170 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
160 171 (branch merge, don't forget to commit)
161 172 $ hg copy b dir/d
162 173 $ hg ci -qAm '5: merge 2 and 3, copy b to dir/d'
163 174 $ echo a >> a
164 175 $ hg ci -qAm '6: change a'
165 176
166 177 $ hg mani
167 178 0
168 179 a
169 180 b
170 181 dir/b
171 182 dir/c
172 183 dir/d
173 184 e
185 $ hg phase --public -r tip
174 186 $ glog
175 @ 6 0613c8e59a3d "6: change a" files: a
187 @ 6 0613c8e59a3d (public) "6: change a" files: a
176 188 |
177 o 5 717e9b37cdb7 "5: merge 2 and 3, copy b to dir/d" files: dir/d e
189 o 5 717e9b37cdb7 (public) "5: merge 2 and 3, copy b to dir/d" files: dir/d e
178 190 |\
179 | o 4 86a55cb968d5 "4: change a" files: a
191 | o 4 86a55cb968d5 (public) "4: change a" files: a
180 192 | |
181 o | 3 0e6e235919dd "3: copy a to e, change b" files: b e
193 o | 3 0e6e235919dd (public) "3: copy a to e, change b" files: b e
182 194 | |
183 o | 2 0394b0d5e4f7 "2: add dir/c" files: dir/c
195 o | 2 0394b0d5e4f7 (public) "2: add dir/c" files: dir/c
184 196 |/
185 o 1 333546584845 "1: add a and dir/b" files: a dir/b
197 o 1 333546584845 (public) "1: add a and dir/b" files: a dir/b
186 198 |
187 o 0 d1a24e2ebd23 "0: add 0" files: 0
199 o 0 d1a24e2ebd23 (public) "0: add 0" files: 0
188 200
189 201 $ cd ..
190 202
191 203 Convert excluding rev 0 and dir/ (and thus rev2):
192 204
193 205 $ cat << EOF > filemap
194 206 > exclude dir
195 207 > EOF
196 208
197 209 $ hg convert --filemap filemap source dest --config convert.hg.revs=1::
198 210 initializing destination dest repository
199 211 scanning source...
200 212 sorting...
201 213 converting...
202 214 5 1: add a and dir/b
203 215 4 2: add dir/c
204 216 3 3: copy a to e, change b
205 217 2 4: change a
206 218 1 5: merge 2 and 3, copy b to dir/d
207 219 0 6: change a
208 220
209 221 Verify that conversion skipped rev 2:
210 222
211 223 $ glog -R dest
212 o 4 78814e84a217 "6: change a" files: a
224 o 4 78814e84a217 (draft) "6: change a" files: a
213 225 |
214 o 3 f7cff662c5e5 "5: merge 2 and 3, copy b to dir/d" files: e
226 o 3 f7cff662c5e5 (draft) "5: merge 2 and 3, copy b to dir/d" files: e
215 227 |\
216 | o 2 ab40a95b0072 "4: change a" files: a
228 | o 2 ab40a95b0072 (draft) "4: change a" files: a
217 229 | |
218 o | 1 bd51f17597bf "3: copy a to e, change b" files: b e
230 o | 1 bd51f17597bf (draft) "3: copy a to e, change b" files: b e
219 231 |/
220 o 0 a4a1dae0fe35 "1: add a and dir/b" files: 0 a
232 o 0 a4a1dae0fe35 (draft) "1: add a and dir/b" files: 0 a
221 233
222 234
223 235 Verify mapping correct in both directions:
224 236
225 237 $ cat source/.hg/shamap
226 238 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5 333546584845f70c4cfecb992341aaef0e708166
227 239 bd51f17597bf32268e68a560b206898c3960cda2 0e6e235919dd8e9285ba8eb5adf703af9ad99378
228 240 ab40a95b00725307e79c2fd271000aa8af9759f4 86a55cb968d51770cba2a1630d6cc637b574580a
229 241 f7cff662c5e581e6f3f1a85ffdd2bcb35825f6ba 717e9b37cdb7eb9917ca8e30aa3f986e6d5b177d
230 242 78814e84a217894517c2de392b903ed05e6871a4 0613c8e59a3ddb9789072ef52f1ed13496489bb4
231 243 $ cat dest/.hg/shamap
232 244 333546584845f70c4cfecb992341aaef0e708166 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
233 245 0394b0d5e4f761ced559fd0bbdc6afc16cb3f7d1 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
234 246 0e6e235919dd8e9285ba8eb5adf703af9ad99378 bd51f17597bf32268e68a560b206898c3960cda2
235 247 86a55cb968d51770cba2a1630d6cc637b574580a ab40a95b00725307e79c2fd271000aa8af9759f4
236 248 717e9b37cdb7eb9917ca8e30aa3f986e6d5b177d f7cff662c5e581e6f3f1a85ffdd2bcb35825f6ba
237 249 0613c8e59a3ddb9789072ef52f1ed13496489bb4 78814e84a217894517c2de392b903ed05e6871a4
238 250
239 251 Verify meta data converted correctly:
240 252
241 253 $ hg -R dest log -r 1 --debug -p --git
242 254 changeset: 1:bd51f17597bf32268e68a560b206898c3960cda2
243 255 phase: draft
244 256 parent: 0:a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
245 257 parent: -1:0000000000000000000000000000000000000000
246 258 manifest: 1:040c72ed9b101773c24ac314776bfc846943781f
247 259 user: test
248 260 date: Thu Jan 01 00:00:00 1970 +0000
249 261 files+: b e
250 262 extra: branch=default
251 263 description:
252 264 3: copy a to e, change b
253 265
254 266
255 267 diff --git a/b b/b
256 268 new file mode 100644
257 269 --- /dev/null
258 270 +++ b/b
259 271 @@ -0,0 +1,1 @@
260 272 +b
261 273 diff --git a/a b/e
262 274 copy from a
263 275 copy to e
264 276
265 277 Verify files included and excluded correctly:
266 278
267 279 $ hg -R dest manifest -r tip
268 280 0
269 281 a
270 282 b
271 283 e
272 284
273 285
274 286 Make changes in dest and convert back:
275 287
276 288 $ hg -R dest up -q
277 289 $ echo dest > dest/dest
278 290 $ hg -R dest ci -Aqm 'change in dest'
279 291 $ hg -R dest tip
280 292 changeset: 5:a2e0e3cc6d1d
281 293 tag: tip
282 294 user: test
283 295 date: Thu Jan 01 00:00:00 1970 +0000
284 296 summary: change in dest
285 297
286 298
287 299 (converting merges back after using a filemap will probably cause chaos so we
288 300 exclude merges.)
289 301
290 302 $ hg convert dest source --config convert.hg.revs='!merge()'
291 303 scanning source...
292 304 sorting...
293 305 converting...
294 306 0 change in dest
295 307
296 308 Verify the conversion back:
297 309
298 310 $ hg -R source log --debug -r tip
299 311 changeset: 7:e6d364a69ff1248b2099e603b0c145504cade6f0
300 312 tag: tip
301 313 phase: draft
302 314 parent: 6:0613c8e59a3ddb9789072ef52f1ed13496489bb4
303 315 parent: -1:0000000000000000000000000000000000000000
304 316 manifest: 7:aa3e9542f3b76d4f1f1b2e9c7ce9dbb48b6a95ec
305 317 user: test
306 318 date: Thu Jan 01 00:00:00 1970 +0000
307 319 files+: dest
308 320 extra: branch=default
309 321 description:
310 322 change in dest
311 323
312 324
313 325 Files that had been excluded are still present:
314 326
315 327 $ hg -R source manifest -r tip
316 328 0
317 329 a
318 330 b
319 331 dest
320 332 dir/b
321 333 dir/c
322 334 dir/d
323 335 e
324 336
325 337 More source changes
326 338
327 339 $ cd source
328 340 $ echo 1 >> a
329 341 $ hg ci -m '8: source first branch'
330 342 created new head
331 343 $ hg up -qr -2
332 344 $ echo 2 >> a
333 345 $ hg ci -m '9: source second branch'
334 346 $ hg merge -q --tool internal:local
335 347 $ hg ci -m '10: source merge'
336 348 $ echo >> a
337 349 $ hg ci -m '11: source change'
338 350
339 351 $ hg mani
340 352 0
341 353 a
342 354 b
343 355 dest
344 356 dir/b
345 357 dir/c
346 358 dir/d
347 359 e
348 360
349 361 $ glog -r 6:
350 @ 11 0c8927d1f7f4 "11: source change" files: a
362 @ 11 0c8927d1f7f4 (draft) "11: source change" files: a
351 363 |
352 o 10 9ccb7ee8d261 "10: source merge" files: a
364 o 10 9ccb7ee8d261 (draft) "10: source merge" files: a
353 365 |\
354 | o 9 f131b1518dba "9: source second branch" files: a
366 | o 9 f131b1518dba (draft) "9: source second branch" files: a
355 367 | |
356 o | 8 669cf0e74b50 "8: source first branch" files: a
368 o | 8 669cf0e74b50 (draft) "8: source first branch" files: a
357 369 | |
358 | o 7 e6d364a69ff1 "change in dest" files: dest
370 | o 7 e6d364a69ff1 (draft) "change in dest" files: dest
359 371 |/
360 o 6 0613c8e59a3d "6: change a" files: a
372 o 6 0613c8e59a3d (public) "6: change a" files: a
361 373 |
362 374 $ cd ..
363 375
364 376 $ hg convert --filemap filemap source dest --config convert.hg.revs=3:
365 377 scanning source...
366 378 sorting...
367 379 converting...
368 380 3 8: source first branch
369 381 2 9: source second branch
370 382 1 10: source merge
371 383 0 11: source change
372 384
373 385 $ glog -R dest
374 o 9 8432d597b263 "11: source change" files: a
386 o 9 8432d597b263 (draft) "11: source change" files: a
375 387 |
376 o 8 632ffacdcd6f "10: source merge" files: a
388 o 8 632ffacdcd6f (draft) "10: source merge" files: a
377 389 |\
378 | o 7 049cfee90ee6 "9: source second branch" files: a
390 | o 7 049cfee90ee6 (draft) "9: source second branch" files: a
379 391 | |
380 o | 6 9b6845e036e5 "8: source first branch" files: a
392 o | 6 9b6845e036e5 (draft) "8: source first branch" files: a
381 393 | |
382 | @ 5 a2e0e3cc6d1d "change in dest" files: dest
394 | @ 5 a2e0e3cc6d1d (draft) "change in dest" files: dest
383 395 |/
384 o 4 78814e84a217 "6: change a" files: a
396 o 4 78814e84a217 (draft) "6: change a" files: a
385 397 |
386 o 3 f7cff662c5e5 "5: merge 2 and 3, copy b to dir/d" files: e
398 o 3 f7cff662c5e5 (draft) "5: merge 2 and 3, copy b to dir/d" files: e
387 399 |\
388 | o 2 ab40a95b0072 "4: change a" files: a
400 | o 2 ab40a95b0072 (draft) "4: change a" files: a
389 401 | |
390 o | 1 bd51f17597bf "3: copy a to e, change b" files: b e
402 o | 1 bd51f17597bf (draft) "3: copy a to e, change b" files: b e
391 403 |/
392 o 0 a4a1dae0fe35 "1: add a and dir/b" files: 0 a
404 o 0 a4a1dae0fe35 (draft) "1: add a and dir/b" files: 0 a
393 405
394 406 $ cd ..
395 407
396 408 Two way tests
397 409
398 410 $ hg init 0
399 411 $ echo f > 0/f
400 412 $ echo a > 0/a-only
401 413 $ echo b > 0/b-only
402 414 $ hg -R 0 ci -Aqm0
403 415
404 416 $ cat << EOF > filemap-a
405 417 > exclude b-only
406 418 > EOF
407 419 $ cat << EOF > filemap-b
408 420 > exclude a-only
409 421 > EOF
410 422 $ hg convert --filemap filemap-a 0 a
411 423 initializing destination a repository
412 424 scanning source...
413 425 sorting...
414 426 converting...
415 427 0 0
416 428 $ hg -R a up -q
417 429 $ echo a > a/f
418 430 $ hg -R a ci -ma
419 431
420 432 $ hg convert --filemap filemap-b 0 b
421 433 initializing destination b repository
422 434 scanning source...
423 435 sorting...
424 436 converting...
425 437 0 0
426 438 $ hg -R b up -q
427 439 $ echo b > b/f
428 440 $ hg -R b ci -mb
429 441
430 442 $ tail */.hg/shamap
431 443 ==> 0/.hg/shamap <==
432 444 86f3f774ffb682bffb5dc3c1d3b3da637cb9a0d6 8a028c7c77f6c7bd6d63bc3f02ca9f779eabf16a
433 445 dd9f218eb91fb857f2a62fe023e1d64a4e7812fe 8a028c7c77f6c7bd6d63bc3f02ca9f779eabf16a
434 446
435 447 ==> a/.hg/shamap <==
436 448 8a028c7c77f6c7bd6d63bc3f02ca9f779eabf16a 86f3f774ffb682bffb5dc3c1d3b3da637cb9a0d6
437 449
438 450 ==> b/.hg/shamap <==
439 451 8a028c7c77f6c7bd6d63bc3f02ca9f779eabf16a dd9f218eb91fb857f2a62fe023e1d64a4e7812fe
440 452
441 453 $ hg convert a 0
442 454 scanning source...
443 455 sorting...
444 456 converting...
445 457 0 a
446 458
447 459 $ hg convert b 0
448 460 scanning source...
449 461 sorting...
450 462 converting...
451 463 0 b
452 464
453 465 $ hg -R 0 log -G
454 466 o changeset: 2:637fbbbe96b6
455 467 | tag: tip
456 468 | parent: 0:8a028c7c77f6
457 469 | user: test
458 470 | date: Thu Jan 01 00:00:00 1970 +0000
459 471 | summary: b
460 472 |
461 473 | o changeset: 1:ec7b9c96e692
462 474 |/ user: test
463 475 | date: Thu Jan 01 00:00:00 1970 +0000
464 476 | summary: a
465 477 |
466 478 @ changeset: 0:8a028c7c77f6
467 479 user: test
468 480 date: Thu Jan 01 00:00:00 1970 +0000
469 481 summary: 0
470 482
471 483 $ hg convert --filemap filemap-b 0 a --config convert.hg.revs=1::
472 484 scanning source...
473 485 sorting...
474 486 converting...
475 487
476 488 $ hg -R 0 up -r1
477 489 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 490 $ echo f >> 0/f
479 491 $ hg -R 0 ci -mx
480 492
481 493 $ hg convert --filemap filemap-b 0 a --config convert.hg.revs=1::
482 494 scanning source...
483 495 sorting...
484 496 converting...
485 497 0 x
486 498
487 499 $ hg -R a log -G -T '{rev} {desc|firstline} ({files})\n'
488 500 o 2 x (f)
489 501 |
490 502 @ 1 a (f)
491 503 |
492 504 o 0 0 (a-only f)
493 505
494 506 $ hg -R a mani -r tip
495 507 a-only
496 508 f
497 509
498 510 An additional round, demonstrating that unchanged files don't get converted
499 511
500 512 $ echo f >> 0/f
501 513 $ echo f >> 0/a-only
502 514 $ hg -R 0 ci -m "extra f+a-only change"
503 515
504 516 $ hg convert --filemap filemap-b 0 a --config convert.hg.revs=1::
505 517 scanning source...
506 518 sorting...
507 519 converting...
508 520 0 extra f+a-only change
509 521
510 522 $ hg -R a log -G -T '{rev} {desc|firstline} ({files})\n'
511 523 o 3 extra f+a-only change (f)
512 524 |
513 525 o 2 x (f)
514 526 |
515 527 @ 1 a (f)
516 528 |
517 529 o 0 0 (a-only f)
518 530
519 531
520 532 Conversion after rollback
521 533
522 534 $ hg -R a rollback -f
523 repository tip rolled back to revision 2 (undo commit)
535 repository tip rolled back to revision 2 (undo convert)
524 536
525 537 $ hg convert --filemap filemap-b 0 a --config convert.hg.revs=1::
526 538 scanning source...
527 539 sorting...
528 540 converting...
529 541 0 extra f+a-only change
530 542
531 543 $ hg -R a log -G -T '{rev} {desc|firstline} ({files})\n'
532 544 o 3 extra f+a-only change (f)
533 545 |
534 546 o 2 x (f)
535 547 |
536 548 @ 1 a (f)
537 549 |
538 550 o 0 0 (a-only f)
539 551
540 552 Convert with --full adds and removes files that didn't change
541 553
542 554 $ echo f >> 0/f
543 555 $ hg -R 0 ci -m "f"
544 556 $ hg convert --filemap filemap-b --full 0 a --config convert.hg.revs=1::
545 557 scanning source...
546 558 sorting...
547 559 converting...
548 560 0 f
549 561 $ hg -R a status --change tip
550 562 M f
551 563 A b-only
552 564 R a-only
General Comments 0
You need to be logged in to leave comments. Login now