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