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