##// END OF EJS Templates
Teach convert-repo to deal with mixed charsets in git
Teach convert-repo to deal with mixed charsets in git

File last commit:

r3821:158fce02 default
r3821:158fce02 default
Show More
convert-repo
305 lines | 9.0 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
Matt Mackall
Teach convert-repo to deal with mixed charsets in git
r3821
os.environ["HGENCODING"] = "utf-8"
Matt Mackall
Revert convert-repo changes
r1715 from mercurial import hg, ui, util
mpm@selenic.com
Add new convert-repo script...
r316
Matt Mackall
Teach convert-repo to deal with mixed charsets in git
r3821 def recode(s):
try:
return s.decode("utf-8").encode("utf-8")
except:
try:
return s.decode("latin-1").encode("utf-8")
except:
return s.decode("utf-8", "replace").encode("utf-8")
mpm@selenic.com
Add new convert-repo script...
r316 class convert_git:
def __init__(self, path):
self.path = path
def getheads(self):
Alexis S. L. Carvalho
make convert-repo deal with git symbolic refs....
r2657 fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
return [fh.read()[:-1]]
mpm@selenic.com
Add new convert-repo script...
r316
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 def catfile(self, rev, type):
if rev == "0" * 40: raise IOError()
Florian La Roche
git -> hg conversion script...
r1335 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
mpm@selenic.com
convert-repo: deal with packed git and other fixes...
r692 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):
Florian La Roche
git -> hg conversion script...
r1335 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
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:]
Matt Mackall
Teach convert-repo to deal with mixed charsets in git
r3821 message = recode(message)
mpm@selenic.com
Add new convert-repo script...
r316 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()
Matt Mackall
convert-repo: Fix timezone handling
r1385 tm, tz = p[-2:]
mpm@selenic.com
Add new convert-repo script...
r316 author = " ".join(p[:-2])
if author[0] == "<": author = author[1:-1]
Matt Mackall
Teach convert-repo to deal with mixed charsets in git
r3821 author = recode(author)
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()
Matt Mackall
convert-repo: Fix timezone handling
r1385 tm, tz = p[-2:]
mpm@selenic.com
convert-repo fixups...
r431 committer = " ".join(p[:-2])
if committer[0] == "<": committer = committer[1:-1]
Matt Mackall
Teach convert-repo to deal with mixed charsets in git
r3821 committer = recode(committer)
Matt Mackall
convert-repo: Fix timezone handling
r1385 message += "\ncommitter: %s\n" % v
mpm@selenic.com
Add new convert-repo script...
r316 if n == "parent": parents.append(v)
Matt Mackall
convert-repo: Fix timezone handling
r1385
tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
Vadim Gelfer
convert-repo: fix reversed time zone offset
r2093 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
Matt Mackall
convert-repo: Fix timezone handling
r1385 date = tm + " " + str(tz)
mpm@selenic.com
Add new convert-repo script...
r316 return (parents, author, date, message)
mpm@selenic.com
Teach convert-repo about tags...
r694 def gettags(self):
tags = {}
Florian La Roche
git -> hg conversion script...
r1335 for f in os.listdir(self.path + "/refs/tags"):
mpm@selenic.com
Teach convert-repo about tags...
r694 try:
Florian La Roche
git -> hg conversion script...
r1335 h = file(self.path + "/refs/tags/" + f).read().strip()
Matt Mackall
convert-repo: retrieve the commit hash from the tag object for tag import
r1386 c = self.catfile(h, "tag") # read the commit hash
h = c.splitlines()[0].split()[1]
mason@suse.com
Fix off by one in convert-repo tags...
r1237 tags[f] = h
mpm@selenic.com
Teach convert-repo about tags...
r694 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()
Florian La Roche
git -> hg conversion script...
r1335 return [ hg.hex(x) for x in 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
Matt Mackall
Revert convert-repo changes
r1715 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)
Matt Mackall
Revert convert-repo changes
r1715 self.repo.rawcommit(files, text, author, dest,
hg.bin(p1), hg.bin(p2))
mpm@selenic.com
convert-repo fixups...
r431 text = "(octopus merge fixup)\n"
Matt Mackall
convert-repo: fix up octopus merge conversion
r1389 p2 = hg.hex(self.repo.changelog.tip())
mpm@selenic.com
convert-repo fixups...
r431
Matt Mackall
convert-repo: fix up octopus merge conversion
r1389 return p2
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:
Florian La Roche
git -> hg conversion script...
r1335 #print "updating tags"
mpm@selenic.com
Teach convert-repo about tags...
r694 f = self.repo.wfile(".hgtags", "w")
f.write("".join(newlines))
f.close()
if not oldlines: self.repo.add([".hgtags"])
Florian La Roche
git -> hg conversion script...
r1335 date = "%s 0" % int(time.mktime(time.gmtime()))
mpm@selenic.com
Teach convert-repo about tags...
r694 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
date, self.repo.changelog.tip(), hg.nullid)
Matt Mackall
convert-repo: linearize the tag commit
r1387 return hg.hex(self.repo.changelog.tip())
mpm@selenic.com
Teach convert-repo about tags...
r694
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 = {}
Matt Mackall
convert-repo: automatically create empty map file
r1655 try:
for l in file(self.mapfile):
sv, dv = l[:-1].split()
self.map[sv] = dv
except IOError:
pass
mpm@selenic.com
Add new convert-repo script...
r316
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)
Matt Mackall
convert-repo: change duplicate elimination
r1388 t = [n for n in t if n not in self.map]
mpm@selenic.com
Add new convert-repo script...
r316 num = len(t)
Matt Mackall
Revert convert-repo changes
r1715 c = None
mpm@selenic.com
Add new convert-repo script...
r316
for c in t:
num -= 1
desc = self.commitcache[c][3].splitlines()[0]
Florian La Roche
git -> hg conversion script...
r1335 #print num, desc
mpm@selenic.com
Add new convert-repo script...
r316 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]
Matt Mackall
Revert convert-repo changes
r1715 if c and ctags:
Matt Mackall
convert-repo: linearize the tag commit
r1387 nrev = self.dest.puttags(ctags)
# write another hash correspondence to override the previous
# one so we don't end up with extra tag heads
file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
mpm@selenic.com
Teach convert-repo about tags...
r694
mpm@selenic.com
Add new convert-repo script...
r316 gitpath, hgpath, mapfile = sys.argv[1:]
Florian La Roche
git -> hg conversion script...
r1335 if os.path.isdir(gitpath + "/.git"):
gitpath += "/.git"
mpm@selenic.com
Add new convert-repo script...
r316
c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
c.convert()