##// END OF EJS Templates
Get set_exec from util in convert_repo...
mpm@selenic.com -
r450:9d785fd7 default
parent child Browse files
Show More
@@ -1,229 +1,229
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
23 import sys, os, zlib, sha
24 from mercurial import hg, ui
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 getfile(self, name, rev):
34 def getfile(self, name, rev):
35 a = file(self.path + ("/.git/objects/%s/%s"
35 a = file(self.path + ("/.git/objects/%s/%s"
36 % (rev[:2], rev[2:]))).read()
36 % (rev[:2], rev[2:]))).read()
37 b = zlib.decompress(a)
37 b = zlib.decompress(a)
38 if sha.sha(b).hexdigest() != rev: raise "bad hash"
38 if sha.sha(b).hexdigest() != rev: raise "bad hash"
39 head, text = b.split('\0', 1)
39 head, text = b.split('\0', 1)
40 return text
40 return text
41
41
42 def getchanges(self, version):
42 def getchanges(self, version):
43 path = os.getcwd()
43 path = os.getcwd()
44 os.chdir(self.path)
44 os.chdir(self.path)
45 fh = os.popen("git-diff-tree -m -r %s" % (version))
45 fh = os.popen("git-diff-tree -m -r %s" % (version))
46 os.chdir(path)
46 os.chdir(path)
47
47
48 changes = []
48 changes = []
49 for l in fh:
49 for l in fh:
50 if "\t" not in l: continue
50 if "\t" not in l: continue
51 m, f = l[:-1].split("\t")
51 m, f = l[:-1].split("\t")
52 m = m.split()
52 m = m.split()
53 h = m[3]
53 h = m[3]
54 p = (m[1] == "100755")
54 p = (m[1] == "100755")
55 changes.append((f, h, p))
55 changes.append((f, h, p))
56 return changes
56 return changes
57
57
58 def getcommit(self, version):
58 def getcommit(self, version):
59 c = self.getfile("", version) # read the commit hash
59 c = self.getfile("", version) # read the commit hash
60 end = c.find("\n\n")
60 end = c.find("\n\n")
61 message = c[end+2:]
61 message = c[end+2:]
62 l = c[:end].splitlines()
62 l = c[:end].splitlines()
63 manifest = l[0].split()[1]
63 manifest = l[0].split()[1]
64 parents = []
64 parents = []
65 for e in l[1:]:
65 for e in l[1:]:
66 n,v = e.split(" ", 1)
66 n,v = e.split(" ", 1)
67 if n == "author":
67 if n == "author":
68 p = v.split()
68 p = v.split()
69 date = " ".join(p[-2:])
69 date = " ".join(p[-2:])
70 author = " ".join(p[:-2])
70 author = " ".join(p[:-2])
71 if author[0] == "<": author = author[1:-1]
71 if author[0] == "<": author = author[1:-1]
72 if n == "committer":
72 if n == "committer":
73 p = v.split()
73 p = v.split()
74 date = " ".join(p[-2:])
74 date = " ".join(p[-2:])
75 committer = " ".join(p[:-2])
75 committer = " ".join(p[:-2])
76 if committer[0] == "<": committer = committer[1:-1]
76 if committer[0] == "<": committer = committer[1:-1]
77 message += "\ncommitter: %s %s\n" % (committer, date)
77 message += "\ncommitter: %s %s\n" % (committer, date)
78 if n == "parent": parents.append(v)
78 if n == "parent": parents.append(v)
79 return (parents, author, date, message)
79 return (parents, author, date, message)
80
80
81 class convert_mercurial:
81 class convert_mercurial:
82 def __init__(self, path):
82 def __init__(self, path):
83 self.path = path
83 self.path = path
84 u = ui.ui()
84 u = ui.ui()
85 self.repo = hg.repository(u, path)
85 self.repo = hg.repository(u, path)
86
86
87 def getheads(self):
87 def getheads(self):
88 h = self.repo.changelog.heads()
88 h = self.repo.changelog.heads()
89 h = [ hg.hex(x) for x in h ]
89 h = [ hg.hex(x) for x in h ]
90 return h
90 return h
91
91
92 def putfile(self, f, e, data):
92 def putfile(self, f, e, data):
93 self.repo.wfile(f, "w").write(data)
93 self.repo.wfile(f, "w").write(data)
94 hg.set_exec(self.repo.wjoin(f), e)
94 util.set_exec(self.repo.wjoin(f), e)
95
95
96 def delfile(self, f):
96 def delfile(self, f):
97 try:
97 try:
98 os.unlink(self.repo.wjoin(f))
98 os.unlink(self.repo.wjoin(f))
99 self.repo.remove([f])
99 self.repo.remove([f])
100 except:
100 except:
101 pass
101 pass
102
102
103 def putcommit(self, files, parents, author, dest, text):
103 def putcommit(self, files, parents, author, dest, text):
104 if not parents: parents = ["0" * 40]
104 if not parents: parents = ["0" * 40]
105 if len(parents) < 2: parents.append("0" * 40)
105 if len(parents) < 2: parents.append("0" * 40)
106
106
107 seen = {}
107 seen = {}
108 pl = []
108 pl = []
109 for p in parents:
109 for p in parents:
110 if p not in seen:
110 if p not in seen:
111 pl.append(p)
111 pl.append(p)
112 seen[p] = 1
112 seen[p] = 1
113 parents = pl
113 parents = pl
114
114
115 p2 = parents.pop(0)
115 p2 = parents.pop(0)
116 c = self.repo.changelog.count()
116 c = self.repo.changelog.count()
117 while parents:
117 while parents:
118 p1 = p2
118 p1 = p2
119 p2 = parents.pop(0)
119 p2 = parents.pop(0)
120 self.repo.rawcommit(files, text, author, dest,
120 self.repo.rawcommit(files, text, author, dest,
121 hg.bin(p1), hg.bin(p2))
121 hg.bin(p1), hg.bin(p2))
122 text = "(octopus merge fixup)\n"
122 text = "(octopus merge fixup)\n"
123
123
124 return hg.hex(self.repo.changelog.node(c))
124 return hg.hex(self.repo.changelog.node(c))
125
125
126 class convert:
126 class convert:
127 def __init__(self, source, dest, mapfile):
127 def __init__(self, source, dest, mapfile):
128 self.source = source
128 self.source = source
129 self.dest = dest
129 self.dest = dest
130 self.mapfile = mapfile
130 self.mapfile = mapfile
131 self.commitcache = {}
131 self.commitcache = {}
132
132
133 self.map = {}
133 self.map = {}
134 for l in file(self.mapfile):
134 for l in file(self.mapfile):
135 sv, dv = l[:-1].split()
135 sv, dv = l[:-1].split()
136 self.map[sv] = dv
136 self.map[sv] = dv
137
137
138 def walktree(self, heads):
138 def walktree(self, heads):
139 visit = heads
139 visit = heads
140 known = {}
140 known = {}
141 parents = {}
141 parents = {}
142 while visit:
142 while visit:
143 n = visit.pop(0)
143 n = visit.pop(0)
144 if n in known or n in self.map: continue
144 if n in known or n in self.map: continue
145 known[n] = 1
145 known[n] = 1
146 self.commitcache[n] = self.source.getcommit(n)
146 self.commitcache[n] = self.source.getcommit(n)
147 cp = self.commitcache[n][0]
147 cp = self.commitcache[n][0]
148 for p in cp:
148 for p in cp:
149 parents.setdefault(n, []).append(p)
149 parents.setdefault(n, []).append(p)
150 visit.append(p)
150 visit.append(p)
151
151
152 return parents
152 return parents
153
153
154 def toposort(self, parents):
154 def toposort(self, parents):
155 visit = parents.keys()
155 visit = parents.keys()
156 seen = {}
156 seen = {}
157 children = {}
157 children = {}
158 while visit:
158 while visit:
159 n = visit.pop(0)
159 n = visit.pop(0)
160 if n in seen: continue
160 if n in seen: continue
161 seen[n] = 1
161 seen[n] = 1
162 pc = 0
162 pc = 0
163 if n in parents:
163 if n in parents:
164 for p in parents[n]:
164 for p in parents[n]:
165 if p not in self.map: pc += 1
165 if p not in self.map: pc += 1
166 visit.append(p)
166 visit.append(p)
167 children.setdefault(p, []).append(n)
167 children.setdefault(p, []).append(n)
168 if not pc: root = n
168 if not pc: root = n
169
169
170 s = []
170 s = []
171 removed = {}
171 removed = {}
172 visit = parents.keys()
172 visit = parents.keys()
173 while visit:
173 while visit:
174 n = visit.pop(0)
174 n = visit.pop(0)
175 if n in removed: continue
175 if n in removed: continue
176 dep = 0
176 dep = 0
177 if n in parents:
177 if n in parents:
178 for p in parents[n]:
178 for p in parents[n]:
179 if p in self.map: continue
179 if p in self.map: continue
180 if p not in removed:
180 if p not in removed:
181 # we're still dependent
181 # we're still dependent
182 visit.append(n)
182 visit.append(n)
183 dep = 1
183 dep = 1
184 break
184 break
185
185
186 if not dep:
186 if not dep:
187 # all n's parents are in the list
187 # all n's parents are in the list
188 removed[n] = 1
188 removed[n] = 1
189 s.append(n)
189 s.append(n)
190 if n in children:
190 if n in children:
191 for c in children[n]:
191 for c in children[n]:
192 visit.insert(0, c)
192 visit.insert(0, c)
193
193
194 return s
194 return s
195
195
196 def copy(self, rev):
196 def copy(self, rev):
197 p, a, d, t = self.commitcache[rev]
197 p, a, d, t = self.commitcache[rev]
198 files = self.source.getchanges(rev)
198 files = self.source.getchanges(rev)
199
199
200 for f,v,e in files:
200 for f,v,e in files:
201 try:
201 try:
202 data = self.source.getfile(f, v)
202 data = self.source.getfile(f, v)
203 except IOError, inst:
203 except IOError, inst:
204 self.dest.delfile(f)
204 self.dest.delfile(f)
205 else:
205 else:
206 self.dest.putfile(f, e, data)
206 self.dest.putfile(f, e, data)
207
207
208 r = [self.map[v] for v in p]
208 r = [self.map[v] for v in p]
209 f = [f for f,v,e in files]
209 f = [f for f,v,e in files]
210 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
210 self.map[rev] = self.dest.putcommit(f, r, a, d, t)
211 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
211 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
212
212
213 def convert(self):
213 def convert(self):
214 heads = self.source.getheads()
214 heads = self.source.getheads()
215 parents = self.walktree(heads)
215 parents = self.walktree(heads)
216 t = self.toposort(parents)
216 t = self.toposort(parents)
217 num = len(t)
217 num = len(t)
218
218
219 for c in t:
219 for c in t:
220 num -= 1
220 num -= 1
221 if c in self.map: continue
221 if c in self.map: continue
222 desc = self.commitcache[c][3].splitlines()[0]
222 desc = self.commitcache[c][3].splitlines()[0]
223 print num, desc
223 print num, desc
224 self.copy(c)
224 self.copy(c)
225
225
226 gitpath, hgpath, mapfile = sys.argv[1:]
226 gitpath, hgpath, mapfile = sys.argv[1:]
227
227
228 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
228 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
229 c.convert()
229 c.convert()
General Comments 0
You need to be logged in to leave comments. Login now