##// END OF EJS Templates
Use commit time instead of stat to check time of last change.
Use commit time instead of stat to check time of last change.

File last commit:

r705:57486910 merge default
r1138:51f26e85 default
Show More
convert-repo
282 lines | 8.1 KiB | text/plain | TextLexer
mpm@selenic.com
Add new convert-repo script...
r316 #!/usr/bin/env python
#
# This is a generalized framework for converting between SCM
# repository formats.
#
# In its current form, it's hardcoded to convert incrementally between
# git and Mercurial.
#
# To use, you must first import the first git version into Mercurial,
# and establish a mapping between the git commit hash and the hash in
# Mercurial for that version. This mapping is kept in a simple text
# file with lines like so:
#
# <git hash> <mercurial hash>
#
# To convert the rest of the repo, run:
#
# convert-repo <git-dir> <hg-dir> <mapfile>
#
# This updates the mapfile on each commit copied, so it can be
# interrupted and can be run repeatedly to copy new commits.
mpm@selenic.com
Teach convert-repo about tags...
r694 import sys, os, zlib, sha, time
mpm@selenic.com
Get set_exec from util in convert_repo...
r450 from mercurial import hg, ui, util
mpm@selenic.com
Add new convert-repo script...
r316
class convert_git:
def __init__(self, path):
self.path = path
def getheads(self):
h = file(self.path + "/.git/HEAD").read()[:-1]
return [h]
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 def catfile(self, rev, type):
if rev == "0" * 40: raise IOError()
path = os.getcwd()
os.chdir(self.path)
mpm@selenic.com
Teach convert-repo about tags...
r694 fh = os.popen("git-cat-file %s %s 2>/dev/null" % (type, rev))
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 os.chdir(path)
return fh.read()
mpm@selenic.com
Add new convert-repo script...
r316 def getfile(self, name, rev):
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 return self.catfile(rev, "blob")
mpm@selenic.com
Add new convert-repo script...
r316
def getchanges(self, version):
path = os.getcwd()
os.chdir(self.path)
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 fh = os.popen("git-diff-tree --root -m -r %s" % (version))
mpm@selenic.com
Add new convert-repo script...
r316 os.chdir(path)
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692
mpm@selenic.com
Add new convert-repo script...
r316 changes = []
for l in fh:
if "\t" not in l: continue
m, f = l[:-1].split("\t")
m = m.split()
h = m[3]
p = (m[1] == "100755")
changes.append((f, h, p))
return changes
def getcommit(self, version):
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 c = self.catfile(version, "commit") # read the commit hash
mpm@selenic.com
Add new convert-repo script...
r316 end = c.find("\n\n")
message = c[end+2:]
l = c[:end].splitlines()
manifest = l[0].split()[1]
parents = []
for e in l[1:]:
n,v = e.split(" ", 1)
if n == "author":
p = v.split()
date = " ".join(p[-2:])
author = " ".join(p[:-2])
if author[0] == "<": author = author[1:-1]
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 if n == "committer":
mpm@selenic.com
convert-repo fixups...
r431 p = v.split()
date = " ".join(p[-2:])
committer = " ".join(p[:-2])
if committer[0] == "<": committer = committer[1:-1]
message += "\ncommitter: %s %s\n" % (committer, date)
mpm@selenic.com
Add new convert-repo script...
r316 if n == "parent": parents.append(v)
return (parents, author, date, message)
mpm@selenic.com
Teach convert-repo about tags...
r694 def gettags(self):
tags = {}
for f in os.listdir(self.path + "/.git/refs/tags"):
try:
h = file(self.path + "/.git/refs/tags/" + f).read().strip()
p, a, d, m = self.getcommit(h)
if not p: p = [h] # git is ugly, don't blame me
tags[f] = p[0]
except:
pass
return tags
mpm@selenic.com
Add new convert-repo script...
r316 class convert_mercurial:
def __init__(self, path):
self.path = path
u = ui.ui()
self.repo = hg.repository(u, path)
def getheads(self):
h = self.repo.changelog.heads()
h = [ hg.hex(x) for x in h ]
return h
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692
mpm@selenic.com
Add new convert-repo script...
r316 def putfile(self, f, e, data):
self.repo.wfile(f, "w").write(data)
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 if self.repo.dirstate.state(f) == '?':
self.repo.dirstate.update([f], "a")
mpm@selenic.com
Get set_exec from util in convert_repo...
r450 util.set_exec(self.repo.wjoin(f), e)
mpm@selenic.com
Add new convert-repo script...
r316
def delfile(self, f):
try:
os.unlink(self.repo.wjoin(f))
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 #self.repo.remove([f])
mpm@selenic.com
Add new convert-repo script...
r316 except:
pass
def putcommit(self, files, parents, author, dest, text):
mpm@selenic.com
convert-repo fixups...
r431 seen = {}
pl = []
for p in parents:
if p not in seen:
pl.append(p)
seen[p] = 1
parents = pl
mpm@selenic.com
Add new convert-repo script...
r316
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 if len(parents) < 2: parents.append("0" * 40)
if len(parents) < 2: parents.append("0" * 40)
mpm@selenic.com
convert-repo fixups...
r431 p2 = parents.pop(0)
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692
mpm@selenic.com
convert-repo fixups...
r431 while parents:
p1 = p2
p2 = parents.pop(0)
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 self.repo.rawcommit(files, text, author, dest,
mpm@selenic.com
convert-repo fixups...
r431 hg.bin(p1), hg.bin(p2))
text = "(octopus merge fixup)\n"
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 return hg.hex(self.repo.changelog.tip())
mpm@selenic.com
Add new convert-repo script...
r316
mpm@selenic.com
Teach convert-repo about tags...
r694 def puttags(self, tags):
try:
old = self.repo.wfile(".hgtags").read()
oldlines = old.splitlines(1)
oldlines.sort()
except:
oldlines = []
k = tags.keys()
k.sort()
newlines = []
for tag in k:
newlines.append("%s %s\n" % (tags[tag], tag))
newlines.sort()
if newlines != oldlines:
print "updating tags"
f = self.repo.wfile(".hgtags", "w")
f.write("".join(newlines))
f.close()
if not oldlines: self.repo.add([".hgtags"])
date = "%s 0" % time.mktime(time.gmtime())
self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
date, self.repo.changelog.tip(), hg.nullid)
mpm@selenic.com
Add new convert-repo script...
r316 class convert:
def __init__(self, source, dest, mapfile):
self.source = source
self.dest = dest
self.mapfile = mapfile
self.commitcache = {}
self.map = {}
for l in file(self.mapfile):
sv, dv = l[:-1].split()
self.map[sv] = dv
def walktree(self, heads):
visit = heads
known = {}
parents = {}
while visit:
n = visit.pop(0)
if n in known or n in self.map: continue
known[n] = 1
self.commitcache[n] = self.source.getcommit(n)
cp = self.commitcache[n][0]
for p in cp:
parents.setdefault(n, []).append(p)
visit.append(p)
return parents
def toposort(self, parents):
visit = parents.keys()
seen = {}
children = {}
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692
mpm@selenic.com
Add new convert-repo script...
r316 while visit:
n = visit.pop(0)
if n in seen: continue
seen[n] = 1
pc = 0
if n in parents:
for p in parents[n]:
if p not in self.map: pc += 1
visit.append(p)
children.setdefault(p, []).append(n)
if not pc: root = n
s = []
removed = {}
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 visit = children.keys()
mpm@selenic.com
Add new convert-repo script...
r316 while visit:
n = visit.pop(0)
if n in removed: continue
dep = 0
if n in parents:
for p in parents[n]:
if p in self.map: continue
if p not in removed:
# we're still dependent
visit.append(n)
dep = 1
break
if not dep:
# all n's parents are in the list
removed[n] = 1
s.append(n)
if n in children:
for c in children[n]:
visit.insert(0, c)
return s
def copy(self, rev):
p, a, d, t = self.commitcache[rev]
files = self.source.getchanges(rev)
for f,v,e in files:
try:
data = self.source.getfile(f, v)
except IOError, inst:
self.dest.delfile(f)
else:
self.dest.putfile(f, e, data)
r = [self.map[v] for v in p]
f = [f for f,v,e in files]
self.map[rev] = self.dest.putcommit(f, r, a, d, t)
file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
def convert(self):
heads = self.source.getheads()
parents = self.walktree(heads)
t = self.toposort(parents)
num = len(t)
for c in t:
num -= 1
if c in self.map: continue
desc = self.commitcache[c][3].splitlines()[0]
print num, desc
self.copy(c)
mpm@selenic.com
Teach convert-repo about tags...
r694 tags = self.source.gettags()
ctags = {}
for k in tags:
v = tags[k]
if v in self.map:
ctags[k] = self.map[v]
self.dest.puttags(ctags)
mpm@selenic.com
Add new convert-repo script...
r316 gitpath, hgpath, mapfile = sys.argv[1:]
c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
c.convert()