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