##// END OF EJS Templates
Add some more smart when initializing destination repository
Edouard Gomez -
r4521:d634b61e default
parent child Browse files
Show More
@@ -1,724 +1,743 b''
1 1 # convert.py Foreign SCM converter
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import sys, os, zlib, sha, time, re, locale, socket
9 from mercurial import hg, ui, util, commands
9 from mercurial import hg, ui, util, commands, repo
10 10
11 11 commands.norepo += " convert"
12 12
13 13 class NoRepo(Exception): pass
14 14
15 15 class commit(object):
16 16 def __init__(self, **parts):
17 17 for x in "author date desc parents".split():
18 18 if not x in parts:
19 19 raise util.Abort("commit missing field %s\n" % x)
20 20 self.__dict__.update(parts)
21 21
22 22 def recode(s):
23 23 try:
24 24 return s.decode("utf-8").encode("utf-8")
25 25 except:
26 26 try:
27 27 return s.decode("latin-1").encode("utf-8")
28 28 except:
29 29 return s.decode("utf-8", "replace").encode("utf-8")
30 30
31 31 class converter_source(object):
32 32 """Conversion source interface"""
33 33
34 34 def __init__(self, ui, path):
35 35 """Initialize conversion source (or raise NoRepo("message")
36 36 exception if path is not a valid repository)"""
37 37 raise NotImplementedError()
38 38
39 39 def getheads(self):
40 40 """Return a list of this repository's heads"""
41 41 raise NotImplementedError()
42 42
43 43 def getfile(self, name, rev):
44 44 """Return file contents as a string"""
45 45 raise NotImplementedError()
46 46
47 47 def getmode(self, name, rev):
48 48 """Return file mode, eg. '', 'x', or 'l'"""
49 49 raise NotImplementedError()
50 50
51 51 def getchanges(self, version):
52 52 """Return sorted list of (filename, id) tuples for all files changed in rev.
53 53
54 54 id just tells us which revision to return in getfile(), e.g. in
55 55 git it's an object hash."""
56 56 raise NotImplementedError()
57 57
58 58 def getcommit(self, version):
59 59 """Return the commit object for version"""
60 60 raise NotImplementedError()
61 61
62 62 def gettags(self):
63 63 """Return the tags as a dictionary of name: revision"""
64 64 raise NotImplementedError()
65 65
66 66 class converter_sink(object):
67 67 """Conversion sink (target) interface"""
68 68
69 69 def __init__(self, ui, path):
70 70 """Initialize conversion sink (or raise NoRepo("message")
71 71 exception if path is not a valid repository)"""
72 72 raise NotImplementedError()
73 73
74 74 def getheads(self):
75 75 """Return a list of this repository's heads"""
76 76 raise NotImplementedError()
77 77
78 78 def mapfile(self):
79 79 """Path to a file that will contain lines
80 80 source_rev_id sink_rev_id
81 81 mapping equivalent revision identifiers for each system."""
82 82 raise NotImplementedError()
83 83
84 84 def putfile(self, f, e, data):
85 85 """Put file for next putcommit().
86 86 f: path to file
87 87 e: '', 'x', or 'l' (regular file, executable, or symlink)
88 88 data: file contents"""
89 89 raise NotImplementedError()
90 90
91 91 def delfile(self, f):
92 92 """Delete file for next putcommit().
93 93 f: path to file"""
94 94 raise NotImplementedError()
95 95
96 96 def putcommit(self, files, parents, commit):
97 97 """Create a revision with all changed files listed in 'files'
98 98 and having listed parents. 'commit' is a commit object containing
99 99 at a minimum the author, date, and message for this changeset.
100 100 Called after putfile() and delfile() calls. Note that the sink
101 101 repository is not told to update itself to a particular revision
102 102 (or even what that revision would be) before it receives the
103 103 file data."""
104 104 raise NotImplementedError()
105 105
106 106 def puttags(self, tags):
107 107 """Put tags into sink.
108 108 tags: {tagname: sink_rev_id, ...}"""
109 109 raise NotImplementedError()
110 110
111 111
112 112 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
113 113 class convert_cvs(converter_source):
114 114 def __init__(self, ui, path):
115 115 self.path = path
116 116 self.ui = ui
117 117 cvs = os.path.join(path, "CVS")
118 118 if not os.path.exists(cvs):
119 119 raise NoRepo("couldn't open CVS repo %s" % path)
120 120
121 121 self.changeset = {}
122 122 self.files = {}
123 123 self.tags = {}
124 124 self.lastbranch = {}
125 125 self.parent = {}
126 126 self.socket = None
127 127 self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1]
128 128 self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1]
129 129 self.encoding = locale.getpreferredencoding()
130 130 self._parse()
131 131 self._connect()
132 132
133 133 def _parse(self):
134 134 if self.changeset:
135 135 return
136 136
137 137 d = os.getcwd()
138 138 try:
139 139 os.chdir(self.path)
140 140 id = None
141 141 state = 0
142 142 for l in os.popen("cvsps -A -u --cvs-direct -q"):
143 143 if state == 0: # header
144 144 if l.startswith("PatchSet"):
145 145 id = l[9:-2]
146 146 elif l.startswith("Date"):
147 147 date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"])
148 148 date = util.datestr(date)
149 149 elif l.startswith("Branch"):
150 150 branch = l[8:-1]
151 151 self.parent[id] = self.lastbranch.get(branch,'bad')
152 152 self.lastbranch[branch] = id
153 153 elif l.startswith("Ancestor branch"):
154 154 ancestor = l[17:-1]
155 155 self.parent[id] = self.lastbranch[ancestor]
156 156 elif l.startswith("Author"):
157 157 author = self.recode(l[8:-1])
158 158 elif l.startswith("Tag: "):
159 159 t = l[5:-1].rstrip()
160 160 if t != "(none)":
161 161 self.tags[t] = id
162 162 elif l.startswith("Log:"):
163 163 state = 1
164 164 log = ""
165 165 elif state == 1: # log
166 166 if l == "Members: \n":
167 167 files = {}
168 168 log = self.recode(log[:-1])
169 169 if log.isspace():
170 170 log = "*** empty log message ***\n"
171 171 state = 2
172 172 else:
173 173 log += l
174 174 elif state == 2:
175 175 if l == "\n": #
176 176 state = 0
177 177 p = [self.parent[id]]
178 178 if id == "1":
179 179 p = []
180 180 if branch == "HEAD":
181 181 branch = ""
182 182 c = commit(author=author, date=date, parents=p,
183 183 desc=log, branch=branch)
184 184 self.changeset[id] = c
185 185 self.files[id] = files
186 186 else:
187 187 colon = l.rfind(':')
188 188 file = l[1:colon]
189 189 rev = l[colon+1:-2]
190 190 rev = rev.split("->")[1]
191 191 files[file] = rev
192 192
193 193 self.heads = self.lastbranch.values()
194 194 finally:
195 195 os.chdir(d)
196 196
197 197 def _connect(self):
198 198 root = self.cvsroot
199 199 conntype = None
200 200 user, host = None, None
201 201 cmd = ['cvs', 'server']
202 202
203 203 self.ui.status("connecting to %s\n" % root)
204 204
205 205 if root.startswith(":pserver:"):
206 206 root = root[9:]
207 207 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root)
208 208 if m:
209 209 conntype = "pserver"
210 210 user, passw, serv, port, root = m.groups()
211 211 if not user:
212 212 user = "anonymous"
213 213 rr = ":pserver:" + user + "@" + serv + ":" + root
214 214 if port:
215 215 rr2, port = "-", int(port)
216 216 else:
217 217 rr2, port = rr, 2401
218 218 rr += str(port)
219 219
220 220 if not passw:
221 221 passw = "A"
222 222 pf = open(os.path.join(os.environ["HOME"], ".cvspass"))
223 223 for l in pf:
224 224 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
225 225 m = re.match(r'(/\d+\s+/)?(.*)', l)
226 226 l = m.group(2)
227 227 w, p = l.split(' ', 1)
228 228 if w in [rr, rr2]:
229 229 passw = p
230 230 break
231 231 pf.close()
232 232
233 233 sck = socket.socket()
234 234 sck.connect((serv, port))
235 235 sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw, "END AUTH REQUEST", ""]))
236 236 if sck.recv(128) != "I LOVE YOU\n":
237 237 raise NoRepo("CVS pserver authentication failed")
238 238
239 239 self.writep = self.readp = sck.makefile('r+')
240 240
241 241 if not conntype and root.startswith(":local:"):
242 242 conntype = "local"
243 243 root = root[7:]
244 244
245 245 if not conntype:
246 246 # :ext:user@host/home/user/path/to/cvsroot
247 247 if root.startswith(":ext:"):
248 248 root = root[5:]
249 249 m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
250 250 if not m:
251 251 conntype = "local"
252 252 else:
253 253 conntype = "rsh"
254 254 user, host, root = m.group(1), m.group(2), m.group(3)
255 255
256 256 if conntype != "pserver":
257 257 if conntype == "rsh":
258 258 rsh = os.environ.get("CVS_RSH" or "rsh")
259 259 if user:
260 260 cmd = [rsh, '-l', user, host] + cmd
261 261 else:
262 262 cmd = [rsh, host] + cmd
263 263
264 264 self.writep, self.readp = os.popen2(cmd)
265 265
266 266 self.realroot = root
267 267
268 268 self.writep.write("Root %s\n" % root)
269 269 self.writep.write("Valid-responses ok error Valid-requests Mode"
270 270 " M Mbinary E Checked-in Created Updated"
271 271 " Merged Removed\n")
272 272 self.writep.write("valid-requests\n")
273 273 self.writep.flush()
274 274 r = self.readp.readline()
275 275 if not r.startswith("Valid-requests"):
276 276 raise util.Abort("server sucks\n")
277 277 if "UseUnchanged" in r:
278 278 self.writep.write("UseUnchanged\n")
279 279 self.writep.flush()
280 280 r = self.readp.readline()
281 281
282 282 def getheads(self):
283 283 return self.heads
284 284
285 285 def _getfile(self, name, rev):
286 286 if rev.endswith("(DEAD)"):
287 287 raise IOError
288 288
289 289 args = ("-N -P -kk -r %s --" % rev).split()
290 290 args.append(os.path.join(self.cvsrepo, name))
291 291 for x in args:
292 292 self.writep.write("Argument %s\n" % x)
293 293 self.writep.write("Directory .\n%s\nco\n" % self.realroot)
294 294 self.writep.flush()
295 295
296 296 data = ""
297 297 while 1:
298 298 line = self.readp.readline()
299 299 if line.startswith("Created ") or line.startswith("Updated "):
300 300 self.readp.readline() # path
301 301 self.readp.readline() # entries
302 302 mode = self.readp.readline()[:-1]
303 303 count = int(self.readp.readline()[:-1])
304 304 data = self.readp.read(count)
305 305 elif line.startswith(" "):
306 306 data += line[1:]
307 307 elif line.startswith("M "):
308 308 pass
309 309 elif line.startswith("Mbinary "):
310 310 count = int(self.readp.readline()[:-1])
311 311 data = self.readp.read(count)
312 312 else:
313 313 if line == "ok\n":
314 314 return (data, "x" in mode and "x" or "")
315 315 elif line.startswith("E "):
316 316 self.ui.warn("cvs server: %s\n" % line[2:])
317 317 elif line.startswith("Remove"):
318 318 l = self.readp.readline()
319 319 l = self.readp.readline()
320 320 if l != "ok\n":
321 321 raise util.Abort("unknown CVS response: %s\n" % l)
322 322 else:
323 323 raise util.Abort("unknown CVS response: %s\n" % line)
324 324
325 325 def getfile(self, file, rev):
326 326 data, mode = self._getfile(file, rev)
327 327 self.modecache[(file, rev)] = mode
328 328 return data
329 329
330 330 def getmode(self, file, rev):
331 331 return self.modecache[(file, rev)]
332 332
333 333 def getchanges(self, rev):
334 334 self.modecache = {}
335 335 files = self.files[rev]
336 336 cl = files.items()
337 337 cl.sort()
338 338 return cl
339 339
340 340 def recode(self, text):
341 341 return text.decode(self.encoding, "replace").encode("utf-8")
342 342
343 343 def getcommit(self, rev):
344 344 return self.changeset[rev]
345 345
346 346 def gettags(self):
347 347 return self.tags
348 348
349 349 class convert_git(converter_source):
350 350 def __init__(self, ui, path):
351 351 if os.path.isdir(path + "/.git"):
352 352 path += "/.git"
353 353 self.path = path
354 354 self.ui = ui
355 355 if not os.path.exists(path + "/objects"):
356 356 raise NoRepo("couldn't open GIT repo %s" % path)
357 357
358 358 def getheads(self):
359 359 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
360 360 return [fh.read()[:-1]]
361 361
362 362 def catfile(self, rev, type):
363 363 if rev == "0" * 40: raise IOError()
364 364 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
365 365 return fh.read()
366 366
367 367 def getfile(self, name, rev):
368 368 return self.catfile(rev, "blob")
369 369
370 370 def getmode(self, name, rev):
371 371 return self.modecache[(name, rev)]
372 372
373 373 def getchanges(self, version):
374 374 self.modecache = {}
375 375 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
376 376 changes = []
377 377 for l in fh:
378 378 if "\t" not in l: continue
379 379 m, f = l[:-1].split("\t")
380 380 m = m.split()
381 381 h = m[3]
382 382 p = (m[1] == "100755")
383 383 s = (m[1] == "120000")
384 384 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
385 385 changes.append((f, h))
386 386 return changes
387 387
388 388 def getcommit(self, version):
389 389 c = self.catfile(version, "commit") # read the commit hash
390 390 end = c.find("\n\n")
391 391 message = c[end+2:]
392 392 message = recode(message)
393 393 l = c[:end].splitlines()
394 394 manifest = l[0].split()[1]
395 395 parents = []
396 396 for e in l[1:]:
397 397 n,v = e.split(" ", 1)
398 398 if n == "author":
399 399 p = v.split()
400 400 tm, tz = p[-2:]
401 401 author = " ".join(p[:-2])
402 402 if author[0] == "<": author = author[1:-1]
403 403 author = recode(author)
404 404 if n == "committer":
405 405 p = v.split()
406 406 tm, tz = p[-2:]
407 407 committer = " ".join(p[:-2])
408 408 if committer[0] == "<": committer = committer[1:-1]
409 409 committer = recode(committer)
410 410 message += "\ncommitter: %s\n" % committer
411 411 if n == "parent": parents.append(v)
412 412
413 413 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
414 414 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
415 415 date = tm + " " + str(tz)
416 416
417 417 c = commit(parents=parents, date=date, author=author, desc=message)
418 418 return c
419 419
420 420 def gettags(self):
421 421 tags = {}
422 422 fh = os.popen('git-ls-remote --tags "%s" 2>/dev/null' % self.path)
423 423 prefix = 'refs/tags/'
424 424 for line in fh:
425 425 line = line.strip()
426 426 if not line.endswith("^{}"):
427 427 continue
428 428 node, tag = line.split(None, 1)
429 429 if not tag.startswith(prefix):
430 430 continue
431 431 tag = tag[len(prefix):-3]
432 432 tags[tag] = node
433 433
434 434 return tags
435 435
436 436 class convert_mercurial(converter_sink):
437 437 def __init__(self, ui, path):
438 438 self.path = path
439 439 self.ui = ui
440 440 try:
441 441 self.repo = hg.repository(self.ui, path)
442 442 except:
443 443 raise NoRepo("could open hg repo %s" % path)
444 444
445 445 def mapfile(self):
446 446 return os.path.join(self.path, ".hg", "shamap")
447 447
448 448 def getheads(self):
449 449 h = self.repo.changelog.heads()
450 450 return [ hg.hex(x) for x in h ]
451 451
452 452 def putfile(self, f, e, data):
453 453 self.repo.wwrite(f, data, e)
454 454 if self.repo.dirstate.state(f) == '?':
455 455 self.repo.dirstate.update([f], "a")
456 456
457 457 def delfile(self, f):
458 458 try:
459 459 os.unlink(self.repo.wjoin(f))
460 460 #self.repo.remove([f])
461 461 except:
462 462 pass
463 463
464 464 def putcommit(self, files, parents, commit):
465 465 seen = {}
466 466 pl = []
467 467 for p in parents:
468 468 if p not in seen:
469 469 pl.append(p)
470 470 seen[p] = 1
471 471 parents = pl
472 472
473 473 if len(parents) < 2: parents.append("0" * 40)
474 474 if len(parents) < 2: parents.append("0" * 40)
475 475 p2 = parents.pop(0)
476 476
477 477 text = commit.desc
478 478 extra = {}
479 479 try:
480 480 extra["branch"] = commit.branch
481 481 except AttributeError:
482 482 pass
483 483
484 484 while parents:
485 485 p1 = p2
486 486 p2 = parents.pop(0)
487 487 a = self.repo.rawcommit(files, text, commit.author, commit.date,
488 488 hg.bin(p1), hg.bin(p2), extra=extra)
489 489 text = "(octopus merge fixup)\n"
490 490 p2 = hg.hex(self.repo.changelog.tip())
491 491
492 492 return p2
493 493
494 494 def puttags(self, tags):
495 495 try:
496 496 old = self.repo.wfile(".hgtags").read()
497 497 oldlines = old.splitlines(1)
498 498 oldlines.sort()
499 499 except:
500 500 oldlines = []
501 501
502 502 k = tags.keys()
503 503 k.sort()
504 504 newlines = []
505 505 for tag in k:
506 506 newlines.append("%s %s\n" % (tags[tag], tag))
507 507
508 508 newlines.sort()
509 509
510 510 if newlines != oldlines:
511 511 self.ui.status("updating tags\n")
512 512 f = self.repo.wfile(".hgtags", "w")
513 513 f.write("".join(newlines))
514 514 f.close()
515 515 if not oldlines: self.repo.add([".hgtags"])
516 516 date = "%s 0" % int(time.mktime(time.gmtime()))
517 517 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
518 518 date, self.repo.changelog.tip(), hg.nullid)
519 519 return hg.hex(self.repo.changelog.tip())
520 520
521 521 converters = [convert_cvs, convert_git, convert_mercurial]
522 522
523 523 def converter(ui, path):
524 524 if not os.path.isdir(path):
525 525 raise util.Abort("%s: not a directory\n" % path)
526 526 for c in converters:
527 527 try:
528 528 return c(ui, path)
529 529 except NoRepo:
530 530 pass
531 531 raise util.Abort("%s: unknown repository type\n" % path)
532 532
533 533 class convert(object):
534 534 def __init__(self, ui, source, dest, mapfile, opts):
535 535
536 536 self.source = source
537 537 self.dest = dest
538 538 self.ui = ui
539 539 self.mapfile = mapfile
540 540 self.opts = opts
541 541 self.commitcache = {}
542 542
543 543 self.map = {}
544 544 try:
545 545 for l in file(self.mapfile):
546 546 sv, dv = l[:-1].split()
547 547 self.map[sv] = dv
548 548 except IOError:
549 549 pass
550 550
551 551 def walktree(self, heads):
552 552 visit = heads
553 553 known = {}
554 554 parents = {}
555 555 while visit:
556 556 n = visit.pop(0)
557 557 if n in known or n in self.map: continue
558 558 known[n] = 1
559 559 self.commitcache[n] = self.source.getcommit(n)
560 560 cp = self.commitcache[n].parents
561 561 for p in cp:
562 562 parents.setdefault(n, []).append(p)
563 563 visit.append(p)
564 564
565 565 return parents
566 566
567 567 def toposort(self, parents):
568 568 visit = parents.keys()
569 569 seen = {}
570 570 children = {}
571 571
572 572 while visit:
573 573 n = visit.pop(0)
574 574 if n in seen: continue
575 575 seen[n] = 1
576 576 pc = 0
577 577 if n in parents:
578 578 for p in parents[n]:
579 579 if p not in self.map: pc += 1
580 580 visit.append(p)
581 581 children.setdefault(p, []).append(n)
582 582 if not pc: root = n
583 583
584 584 s = []
585 585 removed = {}
586 586 visit = children.keys()
587 587 while visit:
588 588 n = visit.pop(0)
589 589 if n in removed: continue
590 590 dep = 0
591 591 if n in parents:
592 592 for p in parents[n]:
593 593 if p in self.map: continue
594 594 if p not in removed:
595 595 # we're still dependent
596 596 visit.append(n)
597 597 dep = 1
598 598 break
599 599
600 600 if not dep:
601 601 # all n's parents are in the list
602 602 removed[n] = 1
603 603 if n not in self.map:
604 604 s.append(n)
605 605 if n in children:
606 606 for c in children[n]:
607 607 visit.insert(0, c)
608 608
609 609 if self.opts.get('datesort'):
610 610 depth = {}
611 611 for n in s:
612 612 depth[n] = 0
613 613 pl = [p for p in self.commitcache[n].parents if p not in self.map]
614 614 if pl:
615 615 depth[n] = max([depth[p] for p in pl]) + 1
616 616
617 617 s = [(depth[n], self.commitcache[n].date, n) for n in s]
618 618 s.sort()
619 619 s = [e[2] for e in s]
620 620
621 621 return s
622 622
623 623 def copy(self, rev):
624 624 c = self.commitcache[rev]
625 625 files = self.source.getchanges(rev)
626 626
627 627 for f,v in files:
628 628 try:
629 629 data = self.source.getfile(f, v)
630 630 except IOError, inst:
631 631 self.dest.delfile(f)
632 632 else:
633 633 e = self.source.getmode(f, v)
634 634 self.dest.putfile(f, e, data)
635 635
636 636 r = [self.map[v] for v in c.parents]
637 637 f = [f for f,v in files]
638 638 self.map[rev] = self.dest.putcommit(f, r, c)
639 639 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
640 640
641 641 def convert(self):
642 642 self.ui.status("scanning source...\n")
643 643 heads = self.source.getheads()
644 644 parents = self.walktree(heads)
645 645 self.ui.status("sorting...\n")
646 646 t = self.toposort(parents)
647 647 num = len(t)
648 648 c = None
649 649
650 650 self.ui.status("converting...\n")
651 651 for c in t:
652 652 num -= 1
653 653 desc = self.commitcache[c].desc
654 654 if "\n" in desc:
655 655 desc = desc.splitlines()[0]
656 656 self.ui.status("%d %s\n" % (num, desc))
657 657 self.copy(c)
658 658
659 659 tags = self.source.gettags()
660 660 ctags = {}
661 661 for k in tags:
662 662 v = tags[k]
663 663 if v in self.map:
664 664 ctags[k] = self.map[v]
665 665
666 666 if c and ctags:
667 667 nrev = self.dest.puttags(ctags)
668 668 # write another hash correspondence to override the previous
669 669 # one so we don't end up with extra tag heads
670 670 if nrev:
671 671 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
672 672
673 673 def _convert(ui, src, dest=None, mapfile=None, **opts):
674 674 '''Convert a foreign SCM repository to a Mercurial one.
675 675
676 676 Accepted source formats:
677 677 - GIT
678 678 - CVS
679 679
680 680 Accepted destination formats:
681 681 - Mercurial
682 682
683 683 If destination isn't given, a new Mercurial repo named <src>-hg will
684 684 be created. If <mapfile> isn't given, it will be put in a default
685 685 location (<dest>/.hg/shamap by default)
686 686
687 687 The <mapfile> is a simple text file that maps each source commit ID to
688 688 the destination ID for that revision, like so:
689 689
690 690 <source ID> <destination ID>
691 691
692 692 If the file doesn't exist, it's automatically created. It's updated
693 693 on each commit copied, so convert-repo can be interrupted and can
694 694 be run repeatedly to copy new commits.
695 695 '''
696 696
697 697 srcc = converter(ui, src)
698 698 if not hasattr(srcc, "getcommit"):
699 699 raise util.Abort("%s: can't read from this repo type\n" % src)
700 700
701 701 if not dest:
702 702 dest = src + "-hg"
703 703 ui.status("assuming destination %s\n" % dest)
704 if not os.path.isdir(dest):
705 ui.status("creating repository %s\n" % dest)
706 os.system("hg init " + dest)
704
705 # Try to be smart and initalize things when required
706 if os.path.isdir(dest):
707 if len(os.listdir(dest)) > 0:
708 try:
709 hg.repository(ui, dest)
710 ui.status("destination %s is a Mercurial repository\n" % dest)
711 except repo.RepoError:
712 raise util.Abort(
713 """destination directory %s is not empty.
714 Please specify an empty directory to be initialized or an already initialized
715 mercurial repository
716 """ % dest)
717 else:
718 ui.status("initializing destination %s repository\n" % dest)
719 hg.repository(ui, dest, create=True)
720 elif os.path.exists(dest):
721 raise util.Abort("destination %s exists and is not a directory\n" % dest)
722 else:
723 ui.status("initializing destination %s repository\n" % dest)
724 hg.repository(ui, dest, create=True)
725
707 726 destc = converter(ui, dest)
708 727 if not hasattr(destc, "putcommit"):
709 728 raise util.Abort("%s: can't write to this repo type\n" % src)
710 729
711 730 if not mapfile:
712 731 try:
713 732 mapfile = destc.mapfile()
714 733 except:
715 734 mapfile = os.path.join(destc, "map")
716 735
717 736 c = convert(ui, srcc, destc, mapfile, opts)
718 737 c.convert()
719 738
720 739 cmdtable = {
721 740 "convert": (_convert,
722 741 [('', 'datesort', None, 'try to sort changesets by date')],
723 742 'hg convert [OPTIONS] <src> [dst [map]]'),
724 743 }
General Comments 0
You need to be logged in to leave comments. Login now