comparison contrib/convert-repo @ 3947:0fab73b3f453

convert-repo: add some smarts autodetect source and destination repo types autodetect read and write capabilities of converters default destination directory (<src>-hg) default map file (<dest>/.hg/shamap) more verbose by default add a -q switch add IO functions
author Matt Mackall <mpm@selenic.com>
date Wed, 20 Dec 2006 16:31:46 -0600
parents 645e1dd4b8ae
children b58c1681d23b
comparison
equal deleted inserted replaced
3943:ac02810132ca 3947:0fab73b3f453
20 # If the file doesn't exist, it's automatically created. It's updated 20 # If the file doesn't exist, it's automatically created. It's updated
21 # on each commit copied, so convert-repo can be interrupted and can 21 # on each commit copied, so convert-repo can be interrupted and can
22 # be run repeatedly to copy new commits. 22 # be run repeatedly to copy new commits.
23 23
24 import sys, os, zlib, sha, time 24 import sys, os, zlib, sha, time
25
26 os.environ["HGENCODING"] = "utf-8" 25 os.environ["HGENCODING"] = "utf-8"
27 26 from mercurial import hg, ui, util, fancyopts
28 from mercurial import hg, ui, util 27
28 class Abort(Exception): pass
29
30 quiet = 0
31 def status(msg):
32 if not quiet: sys.stdout.write(str(msg))
33
34 def warn(msg):
35 sys.stderr.write(str(msg))
36
37 def abort(msg):
38 raise Abort(msg)
29 39
30 def recode(s): 40 def recode(s):
31 try: 41 try:
32 return s.decode("utf-8").encode("utf-8") 42 return s.decode("utf-8").encode("utf-8")
33 except: 43 except:
36 except: 46 except:
37 return s.decode("utf-8", "replace").encode("utf-8") 47 return s.decode("utf-8", "replace").encode("utf-8")
38 48
39 class convert_git: 49 class convert_git:
40 def __init__(self, path): 50 def __init__(self, path):
51 if os.path.isdir(path + "/.git"):
52 path += "/.git"
41 self.path = path 53 self.path = path
54 if not os.path.exists(path + "/HEAD"):
55 raise TypeError("couldn't open GIT repo %s" % path)
42 56
43 def getheads(self): 57 def getheads(self):
44 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path) 58 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
45 return [fh.read()[:-1]] 59 return [fh.read()[:-1]]
46 60
108 122
109 class convert_mercurial: 123 class convert_mercurial:
110 def __init__(self, path): 124 def __init__(self, path):
111 self.path = path 125 self.path = path
112 u = ui.ui() 126 u = ui.ui()
113 self.repo = hg.repository(u, path) 127 try:
128 self.repo = hg.repository(u, path)
129 except:
130 raise TypeError("could open hg repo %s" % path)
131
132 def mapfile(self):
133 return os.path.join(self.path, ".hg", "shamap")
114 134
115 def getheads(self): 135 def getheads(self):
116 h = self.repo.changelog.heads() 136 h = self.repo.changelog.heads()
117 return [ hg.hex(x) for x in h ] 137 return [ hg.hex(x) for x in h ]
118 138
168 newlines.append("%s %s\n" % (tags[tag], tag)) 188 newlines.append("%s %s\n" % (tags[tag], tag))
169 189
170 newlines.sort() 190 newlines.sort()
171 191
172 if newlines != oldlines: 192 if newlines != oldlines:
173 #print "updating tags" 193 status("updating tags\n")
174 f = self.repo.wfile(".hgtags", "w") 194 f = self.repo.wfile(".hgtags", "w")
175 f.write("".join(newlines)) 195 f.write("".join(newlines))
176 f.close() 196 f.close()
177 if not oldlines: self.repo.add([".hgtags"]) 197 if not oldlines: self.repo.add([".hgtags"])
178 date = "%s 0" % int(time.mktime(time.gmtime())) 198 date = "%s 0" % int(time.mktime(time.gmtime()))
179 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo", 199 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
180 date, self.repo.changelog.tip(), hg.nullid) 200 date, self.repo.changelog.tip(), hg.nullid)
181 return hg.hex(self.repo.changelog.tip()) 201 return hg.hex(self.repo.changelog.tip())
182 202
203 converters = [convert_git, convert_mercurial]
204
205 def converter(path):
206 if not os.path.isdir(path):
207 abort("%s: not a directory\n" % path)
208 for c in converters:
209 try:
210 return c(path)
211 except TypeError:
212 pass
213 abort("%s: unknown repository type\n" % path)
214
183 class convert: 215 class convert:
184 def __init__(self, source, dest, mapfile): 216 def __init__(self, source, dest, mapfile):
217
185 self.source = source 218 self.source = source
186 self.dest = dest 219 self.dest = dest
187 self.mapfile = mapfile 220 self.mapfile = mapfile
188 self.commitcache = {} 221 self.commitcache = {}
189 222
270 f = [f for f,v,e in files] 303 f = [f for f,v,e in files]
271 self.map[rev] = self.dest.putcommit(f, r, a, d, t) 304 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
272 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev])) 305 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
273 306
274 def convert(self): 307 def convert(self):
308 status("scanning source...\n")
275 heads = self.source.getheads() 309 heads = self.source.getheads()
276 parents = self.walktree(heads) 310 parents = self.walktree(heads)
311 status("sorting...\n")
277 t = self.toposort(parents) 312 t = self.toposort(parents)
278 t = [n for n in t if n not in self.map] 313 t = [n for n in t if n not in self.map]
279 num = len(t) 314 num = len(t)
280 c = None 315 c = None
281 316
317 status("converting...\n")
282 for c in t: 318 for c in t:
283 num -= 1 319 num -= 1
284 desc = self.commitcache[c][3].splitlines()[0] 320 desc = self.commitcache[c][3].splitlines()[0]
285 #print num, desc 321 status("%d %s\n" % (num, desc))
286 self.copy(c) 322 self.copy(c)
287 323
288 tags = self.source.gettags() 324 tags = self.source.gettags()
289 ctags = {} 325 ctags = {}
290 for k in tags: 326 for k in tags:
297 # write another hash correspondence to override the previous 333 # write another hash correspondence to override the previous
298 # one so we don't end up with extra tag heads 334 # one so we don't end up with extra tag heads
299 if nrev: 335 if nrev:
300 file(self.mapfile, "a").write("%s %s\n" % (c, nrev)) 336 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
301 337
302 gitpath, hgpath, mapfile = sys.argv[1:] 338 def command(src, dest=None, mapfile=None, **opts):
303 if os.path.isdir(gitpath + "/.git"): 339 srcc = converter(src)
304 gitpath += "/.git" 340 if not hasattr(srcc, "getcommit"):
305 341 abort("%s: can't read from this repo type\n" % src)
306 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile) 342
307 c.convert() 343 if not dest:
344 dest = src + "-hg"
345 status("assuming destination %s\n" % dest)
346 if not os.path.isdir(dest):
347 status("creating repository %s\n" % dest)
348 os.system("hg init " + dest)
349 destc = converter(dest)
350 if not hasattr(destc, "putcommit"):
351 abort("%s: can't write to this repo type\n" % src)
352
353 if not mapfile:
354 try:
355 mapfile = destc.mapfile()
356 except:
357 mapfile = os.path.join(destc, "map")
358
359 c = convert(srcc, destc, mapfile)
360 c.convert()
361
362 options = [('q', 'quiet', None, 'suppress output')]
363 opts = {}
364 args = fancyopts.fancyopts(sys.argv[1:], options, opts)
365
366 if opts['quiet']:
367 quiet = 1
368
369 try:
370 command(*args, **opts)
371 except Abort, inst:
372 warn(inst)
373 except KeyboardInterrupt:
374 status("interrupted\n")