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