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