##// END OF EJS Templates
convert-repo: fix reversed time zone offset
Vadim Gelfer -
r2093:5cc41472 default
parent child Browse files
Show More
@@ -1,289 +1,289 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # This is a generalized framework for converting between SCM
3 # This is a generalized framework for converting between SCM
4 # repository formats.
4 # repository formats.
5 #
5 #
6 # In its current form, it's hardcoded to convert incrementally between
6 # In its current form, it's hardcoded to convert incrementally between
7 # git and Mercurial.
7 # git and Mercurial.
8 #
8 #
9 # To use, you must first import the first git version into Mercurial,
9 # To use, you must first import the first git version into Mercurial,
10 # and establish a mapping between the git commit hash and the hash in
10 # and establish a mapping between the git commit hash and the hash in
11 # Mercurial for that version. This mapping is kept in a simple text
11 # Mercurial for that version. This mapping is kept in a simple text
12 # file with lines like so:
12 # file with lines like so:
13 #
13 #
14 # <git hash> <mercurial hash>
14 # <git hash> <mercurial hash>
15 #
15 #
16 # To convert the rest of the repo, run:
16 # To convert the rest of the repo, run:
17 #
17 #
18 # convert-repo <git-dir> <hg-dir> <mapfile>
18 # convert-repo <git-dir> <hg-dir> <mapfile>
19 #
19 #
20 # This updates the mapfile on each commit copied, so it can be
20 # This updates the mapfile on each commit copied, so it can be
21 # interrupted and can be run repeatedly to copy new commits.
21 # interrupted and can be run repeatedly to copy new commits.
22
22
23 import sys, os, zlib, sha, time
23 import sys, os, zlib, sha, time
24 from mercurial import hg, ui, util
24 from mercurial import hg, ui, util
25
25
26 class convert_git:
26 class convert_git:
27 def __init__(self, path):
27 def __init__(self, path):
28 self.path = path
28 self.path = path
29
29
30 def getheads(self):
30 def getheads(self):
31 return [file(self.path + "/HEAD").read()[:-1]]
31 return [file(self.path + "/HEAD").read()[:-1]]
32
32
33 def catfile(self, rev, type):
33 def catfile(self, rev, type):
34 if rev == "0" * 40: raise IOError()
34 if rev == "0" * 40: raise IOError()
35 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
35 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev))
36 return fh.read()
36 return fh.read()
37
37
38 def getfile(self, name, rev):
38 def getfile(self, name, rev):
39 return self.catfile(rev, "blob")
39 return self.catfile(rev, "blob")
40
40
41 def getchanges(self, version):
41 def getchanges(self, version):
42 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
42 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version))
43 changes = []
43 changes = []
44 for l in fh:
44 for l in fh:
45 if "\t" not in l: continue
45 if "\t" not in l: continue
46 m, f = l[:-1].split("\t")
46 m, f = l[:-1].split("\t")
47 m = m.split()
47 m = m.split()
48 h = m[3]
48 h = m[3]
49 p = (m[1] == "100755")
49 p = (m[1] == "100755")
50 changes.append((f, h, p))
50 changes.append((f, h, p))
51 return changes
51 return changes
52
52
53 def getcommit(self, version):
53 def getcommit(self, version):
54 c = self.catfile(version, "commit") # read the commit hash
54 c = self.catfile(version, "commit") # read the commit hash
55 end = c.find("\n\n")
55 end = c.find("\n\n")
56 message = c[end+2:]
56 message = c[end+2:]
57 l = c[:end].splitlines()
57 l = c[:end].splitlines()
58 manifest = l[0].split()[1]
58 manifest = l[0].split()[1]
59 parents = []
59 parents = []
60 for e in l[1:]:
60 for e in l[1:]:
61 n,v = e.split(" ", 1)
61 n,v = e.split(" ", 1)
62 if n == "author":
62 if n == "author":
63 p = v.split()
63 p = v.split()
64 tm, tz = p[-2:]
64 tm, tz = p[-2:]
65 author = " ".join(p[:-2])
65 author = " ".join(p[:-2])
66 if author[0] == "<": author = author[1:-1]
66 if author[0] == "<": author = author[1:-1]
67 if n == "committer":
67 if n == "committer":
68 p = v.split()
68 p = v.split()
69 tm, tz = p[-2:]
69 tm, tz = p[-2:]
70 committer = " ".join(p[:-2])
70 committer = " ".join(p[:-2])
71 if committer[0] == "<": committer = committer[1:-1]
71 if committer[0] == "<": committer = committer[1:-1]
72 message += "\ncommitter: %s\n" % v
72 message += "\ncommitter: %s\n" % v
73 if n == "parent": parents.append(v)
73 if n == "parent": parents.append(v)
74
74
75 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
75 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
76 tz = int(tzs) * (int(tzh) * 3600 + int(tzm))
76 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
77 date = tm + " " + str(tz)
77 date = tm + " " + str(tz)
78 return (parents, author, date, message)
78 return (parents, author, date, message)
79
79
80 def gettags(self):
80 def gettags(self):
81 tags = {}
81 tags = {}
82 for f in os.listdir(self.path + "/refs/tags"):
82 for f in os.listdir(self.path + "/refs/tags"):
83 try:
83 try:
84 h = file(self.path + "/refs/tags/" + f).read().strip()
84 h = file(self.path + "/refs/tags/" + f).read().strip()
85 c = self.catfile(h, "tag") # read the commit hash
85 c = self.catfile(h, "tag") # read the commit hash
86 h = c.splitlines()[0].split()[1]
86 h = c.splitlines()[0].split()[1]
87 tags[f] = h
87 tags[f] = h
88 except:
88 except:
89 pass
89 pass
90 return tags
90 return tags
91
91
92 class convert_mercurial:
92 class convert_mercurial:
93 def __init__(self, path):
93 def __init__(self, path):
94 self.path = path
94 self.path = path
95 u = ui.ui()
95 u = ui.ui()
96 self.repo = hg.repository(u, path)
96 self.repo = hg.repository(u, path)
97
97
98 def getheads(self):
98 def getheads(self):
99 h = self.repo.changelog.heads()
99 h = self.repo.changelog.heads()
100 return [ hg.hex(x) for x in h ]
100 return [ hg.hex(x) for x in h ]
101
101
102 def putfile(self, f, e, data):
102 def putfile(self, f, e, data):
103 self.repo.wfile(f, "w").write(data)
103 self.repo.wfile(f, "w").write(data)
104 if self.repo.dirstate.state(f) == '?':
104 if self.repo.dirstate.state(f) == '?':
105 self.repo.dirstate.update([f], "a")
105 self.repo.dirstate.update([f], "a")
106
106
107 util.set_exec(self.repo.wjoin(f), e)
107 util.set_exec(self.repo.wjoin(f), e)
108
108
109 def delfile(self, f):
109 def delfile(self, f):
110 try:
110 try:
111 os.unlink(self.repo.wjoin(f))
111 os.unlink(self.repo.wjoin(f))
112 #self.repo.remove([f])
112 #self.repo.remove([f])
113 except:
113 except:
114 pass
114 pass
115
115
116 def putcommit(self, files, parents, author, dest, text):
116 def putcommit(self, files, parents, author, dest, text):
117 seen = {}
117 seen = {}
118 pl = []
118 pl = []
119 for p in parents:
119 for p in parents:
120 if p not in seen:
120 if p not in seen:
121 pl.append(p)
121 pl.append(p)
122 seen[p] = 1
122 seen[p] = 1
123 parents = pl
123 parents = pl
124
124
125 if len(parents) < 2: parents.append("0" * 40)
125 if len(parents) < 2: parents.append("0" * 40)
126 if len(parents) < 2: parents.append("0" * 40)
126 if len(parents) < 2: parents.append("0" * 40)
127 p2 = parents.pop(0)
127 p2 = parents.pop(0)
128
128
129 while parents:
129 while parents:
130 p1 = p2
130 p1 = p2
131 p2 = parents.pop(0)
131 p2 = parents.pop(0)
132 self.repo.rawcommit(files, text, author, dest,
132 self.repo.rawcommit(files, text, author, dest,
133 hg.bin(p1), hg.bin(p2))
133 hg.bin(p1), hg.bin(p2))
134 text = "(octopus merge fixup)\n"
134 text = "(octopus merge fixup)\n"
135 p2 = hg.hex(self.repo.changelog.tip())
135 p2 = hg.hex(self.repo.changelog.tip())
136
136
137 return p2
137 return p2
138
138
139 def puttags(self, tags):
139 def puttags(self, tags):
140 try:
140 try:
141 old = self.repo.wfile(".hgtags").read()
141 old = self.repo.wfile(".hgtags").read()
142 oldlines = old.splitlines(1)
142 oldlines = old.splitlines(1)
143 oldlines.sort()
143 oldlines.sort()
144 except:
144 except:
145 oldlines = []
145 oldlines = []
146
146
147 k = tags.keys()
147 k = tags.keys()
148 k.sort()
148 k.sort()
149 newlines = []
149 newlines = []
150 for tag in k:
150 for tag in k:
151 newlines.append("%s %s\n" % (tags[tag], tag))
151 newlines.append("%s %s\n" % (tags[tag], tag))
152
152
153 newlines.sort()
153 newlines.sort()
154
154
155 if newlines != oldlines:
155 if newlines != oldlines:
156 #print "updating tags"
156 #print "updating tags"
157 f = self.repo.wfile(".hgtags", "w")
157 f = self.repo.wfile(".hgtags", "w")
158 f.write("".join(newlines))
158 f.write("".join(newlines))
159 f.close()
159 f.close()
160 if not oldlines: self.repo.add([".hgtags"])
160 if not oldlines: self.repo.add([".hgtags"])
161 date = "%s 0" % int(time.mktime(time.gmtime()))
161 date = "%s 0" % int(time.mktime(time.gmtime()))
162 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
162 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
163 date, self.repo.changelog.tip(), hg.nullid)
163 date, self.repo.changelog.tip(), hg.nullid)
164 return hg.hex(self.repo.changelog.tip())
164 return hg.hex(self.repo.changelog.tip())
165
165
166 class convert:
166 class convert:
167 def __init__(self, source, dest, mapfile):
167 def __init__(self, source, dest, mapfile):
168 self.source = source
168 self.source = source
169 self.dest = dest
169 self.dest = dest
170 self.mapfile = mapfile
170 self.mapfile = mapfile
171 self.commitcache = {}
171 self.commitcache = {}
172
172
173 self.map = {}
173 self.map = {}
174 try:
174 try:
175 for l in file(self.mapfile):
175 for l in file(self.mapfile):
176 sv, dv = l[:-1].split()
176 sv, dv = l[:-1].split()
177 self.map[sv] = dv
177 self.map[sv] = dv
178 except IOError:
178 except IOError:
179 pass
179 pass
180
180
181 def walktree(self, heads):
181 def walktree(self, heads):
182 visit = heads
182 visit = heads
183 known = {}
183 known = {}
184 parents = {}
184 parents = {}
185 while visit:
185 while visit:
186 n = visit.pop(0)
186 n = visit.pop(0)
187 if n in known or n in self.map: continue
187 if n in known or n in self.map: continue
188 known[n] = 1
188 known[n] = 1
189 self.commitcache[n] = self.source.getcommit(n)
189 self.commitcache[n] = self.source.getcommit(n)
190 cp = self.commitcache[n][0]
190 cp = self.commitcache[n][0]
191 for p in cp:
191 for p in cp:
192 parents.setdefault(n, []).append(p)
192 parents.setdefault(n, []).append(p)
193 visit.append(p)
193 visit.append(p)
194
194
195 return parents
195 return parents
196
196
197 def toposort(self, parents):
197 def toposort(self, parents):
198 visit = parents.keys()
198 visit = parents.keys()
199 seen = {}
199 seen = {}
200 children = {}
200 children = {}
201
201
202 while visit:
202 while visit:
203 n = visit.pop(0)
203 n = visit.pop(0)
204 if n in seen: continue
204 if n in seen: continue
205 seen[n] = 1
205 seen[n] = 1
206 pc = 0
206 pc = 0
207 if n in parents:
207 if n in parents:
208 for p in parents[n]:
208 for p in parents[n]:
209 if p not in self.map: pc += 1
209 if p not in self.map: pc += 1
210 visit.append(p)
210 visit.append(p)
211 children.setdefault(p, []).append(n)
211 children.setdefault(p, []).append(n)
212 if not pc: root = n
212 if not pc: root = n
213
213
214 s = []
214 s = []
215 removed = {}
215 removed = {}
216 visit = children.keys()
216 visit = children.keys()
217 while visit:
217 while visit:
218 n = visit.pop(0)
218 n = visit.pop(0)
219 if n in removed: continue
219 if n in removed: continue
220 dep = 0
220 dep = 0
221 if n in parents:
221 if n in parents:
222 for p in parents[n]:
222 for p in parents[n]:
223 if p in self.map: continue
223 if p in self.map: continue
224 if p not in removed:
224 if p not in removed:
225 # we're still dependent
225 # we're still dependent
226 visit.append(n)
226 visit.append(n)
227 dep = 1
227 dep = 1
228 break
228 break
229
229
230 if not dep:
230 if not dep:
231 # all n's parents are in the list
231 # all n's parents are in the list
232 removed[n] = 1
232 removed[n] = 1
233 s.append(n)
233 s.append(n)
234 if n in children:
234 if n in children:
235 for c in children[n]:
235 for c in children[n]:
236 visit.insert(0, c)
236 visit.insert(0, c)
237
237
238 return s
238 return s
239
239
240 def copy(self, rev):
240 def copy(self, rev):
241 p, a, d, t = self.commitcache[rev]
241 p, a, d, t = self.commitcache[rev]
242 files = self.source.getchanges(rev)
242 files = self.source.getchanges(rev)
243
243
244 for f,v,e in files:
244 for f,v,e in files:
245 try:
245 try:
246 data = self.source.getfile(f, v)
246 data = self.source.getfile(f, v)
247 except IOError, inst:
247 except IOError, inst:
248 self.dest.delfile(f)
248 self.dest.delfile(f)
249 else:
249 else:
250 self.dest.putfile(f, e, data)
250 self.dest.putfile(f, e, data)
251
251
252 r = [self.map[v] for v in p]
252 r = [self.map[v] for v in p]
253 f = [f for f,v,e in files]
253 f = [f for f,v,e in files]
254 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
254 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
255 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
255 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
256
256
257 def convert(self):
257 def convert(self):
258 heads = self.source.getheads()
258 heads = self.source.getheads()
259 parents = self.walktree(heads)
259 parents = self.walktree(heads)
260 t = self.toposort(parents)
260 t = self.toposort(parents)
261 t = [n for n in t if n not in self.map]
261 t = [n for n in t if n not in self.map]
262 num = len(t)
262 num = len(t)
263 c = None
263 c = None
264
264
265 for c in t:
265 for c in t:
266 num -= 1
266 num -= 1
267 desc = self.commitcache[c][3].splitlines()[0]
267 desc = self.commitcache[c][3].splitlines()[0]
268 #print num, desc
268 #print num, desc
269 self.copy(c)
269 self.copy(c)
270
270
271 tags = self.source.gettags()
271 tags = self.source.gettags()
272 ctags = {}
272 ctags = {}
273 for k in tags:
273 for k in tags:
274 v = tags[k]
274 v = tags[k]
275 if v in self.map:
275 if v in self.map:
276 ctags[k] = self.map[v]
276 ctags[k] = self.map[v]
277
277
278 if c and ctags:
278 if c and ctags:
279 nrev = self.dest.puttags(ctags)
279 nrev = self.dest.puttags(ctags)
280 # write another hash correspondence to override the previous
280 # write another hash correspondence to override the previous
281 # one so we don't end up with extra tag heads
281 # one so we don't end up with extra tag heads
282 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
282 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
283
283
284 gitpath, hgpath, mapfile = sys.argv[1:]
284 gitpath, hgpath, mapfile = sys.argv[1:]
285 if os.path.isdir(gitpath + "/.git"):
285 if os.path.isdir(gitpath + "/.git"):
286 gitpath += "/.git"
286 gitpath += "/.git"
287
287
288 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
288 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
289 c.convert()
289 c.convert()
General Comments 0
You need to be logged in to leave comments. Login now