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