##// END OF EJS Templates
convert-repo: Fix timezone handling
Matt Mackall -
r1385:adb3de56 default
parent child Browse files
Show More
@@ -1,273 +1,277 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 date = " ".join(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 date = " ".join(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 %s\n" % (committer, date)
72 message += "\ncommitter: %s\n" % v
73 if n == "parent": parents.append(v)
73 if n == "parent": parents.append(v)
74
75 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
76 tz = int(tzs) * (int(tzh) * 3600 + int(tzm))
77 date = tm + " " + str(tz)
74 return (parents, author, date, message)
78 return (parents, author, date, message)
75
79
76 def gettags(self):
80 def gettags(self):
77 tags = {}
81 tags = {}
78 for f in os.listdir(self.path + "/refs/tags"):
82 for f in os.listdir(self.path + "/refs/tags"):
79 try:
83 try:
80 h = file(self.path + "/refs/tags/" + f).read().strip()
84 h = file(self.path + "/refs/tags/" + f).read().strip()
81 tags[f] = h
85 tags[f] = h
82 except:
86 except:
83 pass
87 pass
84 return tags
88 return tags
85
89
86 class convert_mercurial:
90 class convert_mercurial:
87 def __init__(self, path):
91 def __init__(self, path):
88 self.path = path
92 self.path = path
89 u = ui.ui()
93 u = ui.ui()
90 self.repo = hg.repository(u, path)
94 self.repo = hg.repository(u, path)
91
95
92 def getheads(self):
96 def getheads(self):
93 h = self.repo.changelog.heads()
97 h = self.repo.changelog.heads()
94 return [ hg.hex(x) for x in h ]
98 return [ hg.hex(x) for x in h ]
95
99
96 def putfile(self, f, e, data):
100 def putfile(self, f, e, data):
97 self.repo.wfile(f, "w").write(data)
101 self.repo.wfile(f, "w").write(data)
98 if self.repo.dirstate.state(f) == '?':
102 if self.repo.dirstate.state(f) == '?':
99 self.repo.dirstate.update([f], "a")
103 self.repo.dirstate.update([f], "a")
100
104
101 util.set_exec(self.repo.wjoin(f), e)
105 util.set_exec(self.repo.wjoin(f), e)
102
106
103 def delfile(self, f):
107 def delfile(self, f):
104 try:
108 try:
105 os.unlink(self.repo.wjoin(f))
109 os.unlink(self.repo.wjoin(f))
106 #self.repo.remove([f])
110 #self.repo.remove([f])
107 except:
111 except:
108 pass
112 pass
109
113
110 def putcommit(self, files, parents, author, dest, text):
114 def putcommit(self, files, parents, author, dest, text):
111 seen = {}
115 seen = {}
112 pl = []
116 pl = []
113 for p in parents:
117 for p in parents:
114 if p not in seen:
118 if p not in seen:
115 pl.append(p)
119 pl.append(p)
116 seen[p] = 1
120 seen[p] = 1
117 parents = pl
121 parents = pl
118
122
119 if len(parents) < 2: parents.append("0" * 40)
123 if len(parents) < 2: parents.append("0" * 40)
120 if len(parents) < 2: parents.append("0" * 40)
124 if len(parents) < 2: parents.append("0" * 40)
121 p2 = parents.pop(0)
125 p2 = parents.pop(0)
122
126
123 while parents:
127 while parents:
124 p1 = p2
128 p1 = p2
125 p2 = parents.pop(0)
129 p2 = parents.pop(0)
126 self.repo.rawcommit(files, text, author, dest,
130 self.repo.rawcommit(files, text, author, dest,
127 hg.bin(p1), hg.bin(p2))
131 hg.bin(p1), hg.bin(p2))
128 text = "(octopus merge fixup)\n"
132 text = "(octopus merge fixup)\n"
129
133
130 return hg.hex(self.repo.changelog.tip())
134 return hg.hex(self.repo.changelog.tip())
131
135
132 def puttags(self, tags):
136 def puttags(self, tags):
133 try:
137 try:
134 old = self.repo.wfile(".hgtags").read()
138 old = self.repo.wfile(".hgtags").read()
135 oldlines = old.splitlines(1)
139 oldlines = old.splitlines(1)
136 oldlines.sort()
140 oldlines.sort()
137 except:
141 except:
138 oldlines = []
142 oldlines = []
139
143
140 k = tags.keys()
144 k = tags.keys()
141 k.sort()
145 k.sort()
142 newlines = []
146 newlines = []
143 for tag in k:
147 for tag in k:
144 newlines.append("%s %s\n" % (tags[tag], tag))
148 newlines.append("%s %s\n" % (tags[tag], tag))
145
149
146 newlines.sort()
150 newlines.sort()
147
151
148 if newlines != oldlines:
152 if newlines != oldlines:
149 #print "updating tags"
153 #print "updating tags"
150 f = self.repo.wfile(".hgtags", "w")
154 f = self.repo.wfile(".hgtags", "w")
151 f.write("".join(newlines))
155 f.write("".join(newlines))
152 f.close()
156 f.close()
153 if not oldlines: self.repo.add([".hgtags"])
157 if not oldlines: self.repo.add([".hgtags"])
154 date = "%s 0" % int(time.mktime(time.gmtime()))
158 date = "%s 0" % int(time.mktime(time.gmtime()))
155 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
159 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
156 date, self.repo.changelog.tip(), hg.nullid)
160 date, self.repo.changelog.tip(), hg.nullid)
157
161
158 class convert:
162 class convert:
159 def __init__(self, source, dest, mapfile):
163 def __init__(self, source, dest, mapfile):
160 self.source = source
164 self.source = source
161 self.dest = dest
165 self.dest = dest
162 self.mapfile = mapfile
166 self.mapfile = mapfile
163 self.commitcache = {}
167 self.commitcache = {}
164
168
165 self.map = {}
169 self.map = {}
166 for l in file(self.mapfile):
170 for l in file(self.mapfile):
167 sv, dv = l[:-1].split()
171 sv, dv = l[:-1].split()
168 self.map[sv] = dv
172 self.map[sv] = dv
169
173
170 def walktree(self, heads):
174 def walktree(self, heads):
171 visit = heads
175 visit = heads
172 known = {}
176 known = {}
173 parents = {}
177 parents = {}
174 while visit:
178 while visit:
175 n = visit.pop(0)
179 n = visit.pop(0)
176 if n in known or n in self.map: continue
180 if n in known or n in self.map: continue
177 known[n] = 1
181 known[n] = 1
178 self.commitcache[n] = self.source.getcommit(n)
182 self.commitcache[n] = self.source.getcommit(n)
179 cp = self.commitcache[n][0]
183 cp = self.commitcache[n][0]
180 for p in cp:
184 for p in cp:
181 parents.setdefault(n, []).append(p)
185 parents.setdefault(n, []).append(p)
182 visit.append(p)
186 visit.append(p)
183
187
184 return parents
188 return parents
185
189
186 def toposort(self, parents):
190 def toposort(self, parents):
187 visit = parents.keys()
191 visit = parents.keys()
188 seen = {}
192 seen = {}
189 children = {}
193 children = {}
190
194
191 while visit:
195 while visit:
192 n = visit.pop(0)
196 n = visit.pop(0)
193 if n in seen: continue
197 if n in seen: continue
194 seen[n] = 1
198 seen[n] = 1
195 pc = 0
199 pc = 0
196 if n in parents:
200 if n in parents:
197 for p in parents[n]:
201 for p in parents[n]:
198 if p not in self.map: pc += 1
202 if p not in self.map: pc += 1
199 visit.append(p)
203 visit.append(p)
200 children.setdefault(p, []).append(n)
204 children.setdefault(p, []).append(n)
201 if not pc: root = n
205 if not pc: root = n
202
206
203 s = []
207 s = []
204 removed = {}
208 removed = {}
205 visit = children.keys()
209 visit = children.keys()
206 while visit:
210 while visit:
207 n = visit.pop(0)
211 n = visit.pop(0)
208 if n in removed: continue
212 if n in removed: continue
209 dep = 0
213 dep = 0
210 if n in parents:
214 if n in parents:
211 for p in parents[n]:
215 for p in parents[n]:
212 if p in self.map: continue
216 if p in self.map: continue
213 if p not in removed:
217 if p not in removed:
214 # we're still dependent
218 # we're still dependent
215 visit.append(n)
219 visit.append(n)
216 dep = 1
220 dep = 1
217 break
221 break
218
222
219 if not dep:
223 if not dep:
220 # all n's parents are in the list
224 # all n's parents are in the list
221 removed[n] = 1
225 removed[n] = 1
222 s.append(n)
226 s.append(n)
223 if n in children:
227 if n in children:
224 for c in children[n]:
228 for c in children[n]:
225 visit.insert(0, c)
229 visit.insert(0, c)
226
230
227 return s
231 return s
228
232
229 def copy(self, rev):
233 def copy(self, rev):
230 p, a, d, t = self.commitcache[rev]
234 p, a, d, t = self.commitcache[rev]
231 files = self.source.getchanges(rev)
235 files = self.source.getchanges(rev)
232
236
233 for f,v,e in files:
237 for f,v,e in files:
234 try:
238 try:
235 data = self.source.getfile(f, v)
239 data = self.source.getfile(f, v)
236 except IOError, inst:
240 except IOError, inst:
237 self.dest.delfile(f)
241 self.dest.delfile(f)
238 else:
242 else:
239 self.dest.putfile(f, e, data)
243 self.dest.putfile(f, e, data)
240
244
241 r = [self.map[v] for v in p]
245 r = [self.map[v] for v in p]
242 f = [f for f,v,e in files]
246 f = [f for f,v,e in files]
243 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
247 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
244 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
248 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
245
249
246 def convert(self):
250 def convert(self):
247 heads = self.source.getheads()
251 heads = self.source.getheads()
248 parents = self.walktree(heads)
252 parents = self.walktree(heads)
249 t = self.toposort(parents)
253 t = self.toposort(parents)
250 num = len(t)
254 num = len(t)
251
255
252 for c in t:
256 for c in t:
253 num -= 1
257 num -= 1
254 if c in self.map: continue
258 if c in self.map: continue
255 desc = self.commitcache[c][3].splitlines()[0]
259 desc = self.commitcache[c][3].splitlines()[0]
256 #print num, desc
260 #print num, desc
257 self.copy(c)
261 self.copy(c)
258
262
259 tags = self.source.gettags()
263 tags = self.source.gettags()
260 ctags = {}
264 ctags = {}
261 for k in tags:
265 for k in tags:
262 v = tags[k]
266 v = tags[k]
263 if v in self.map:
267 if v in self.map:
264 ctags[k] = self.map[v]
268 ctags[k] = self.map[v]
265
269
266 self.dest.puttags(ctags)
270 self.dest.puttags(ctags)
267
271
268 gitpath, hgpath, mapfile = sys.argv[1:]
272 gitpath, hgpath, mapfile = sys.argv[1:]
269 if os.path.isdir(gitpath + "/.git"):
273 if os.path.isdir(gitpath + "/.git"):
270 gitpath += "/.git"
274 gitpath += "/.git"
271
275
272 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
276 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
273 c.convert()
277 c.convert()
General Comments 0
You need to be logged in to leave comments. Login now