Mercurial > hg > mercurial-crew-with-dirclash
annotate contrib/convert-repo @ 1599:f93fde8f5027
remove the gpg stuff from hgeditor (superseded by the signing extension)
remove the gpg stuff from hgeditor, update the comments to make it clear
it can be used to show a diff while commiting
author | Benoit Boissinot <benoit.boissinot@ens-lyon.org> |
---|---|
date | Tue, 27 Dec 2005 13:12:53 -0600 |
parents | 9b3ef6f3cef5 |
children | 7bfd4724932a |
rev | line source |
---|---|
316 | 1 #!/usr/bin/env python |
2 # | |
3 # This is a generalized framework for converting between SCM | |
4 # repository formats. | |
5 # | |
6 # In its current form, it's hardcoded to convert incrementally between | |
7 # git and Mercurial. | |
8 # | |
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 | |
11 # Mercurial for that version. This mapping is kept in a simple text | |
12 # file with lines like so: | |
13 # | |
14 # <git hash> <mercurial hash> | |
15 # | |
16 # To convert the rest of the repo, run: | |
17 # | |
18 # convert-repo <git-dir> <hg-dir> <mapfile> | |
19 # | |
20 # This updates the mapfile on each commit copied, so it can be | |
21 # interrupted and can be run repeatedly to copy new commits. | |
22 | |
694 | 23 import sys, os, zlib, sha, time |
450 | 24 from mercurial import hg, ui, util |
316 | 25 |
26 class convert_git: | |
27 def __init__(self, path): | |
28 self.path = path | |
29 | |
30 def getheads(self): | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
31 return [file(self.path + "/HEAD").read()[:-1]] |
316 | 32 |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
33 def catfile(self, rev, type): |
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
34 if rev == "0" * 40: raise IOError() |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
35 fh = os.popen("GIT_DIR=%s git-cat-file %s %s 2>/dev/null" % (self.path, type, rev)) |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
36 return fh.read() |
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
37 |
316 | 38 def getfile(self, name, rev): |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
39 return self.catfile(rev, "blob") |
316 | 40 |
41 def getchanges(self, version): | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
42 fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version)) |
316 | 43 changes = [] |
44 for l in fh: | |
45 if "\t" not in l: continue | |
46 m, f = l[:-1].split("\t") | |
47 m = m.split() | |
48 h = m[3] | |
49 p = (m[1] == "100755") | |
50 changes.append((f, h, p)) | |
51 return changes | |
52 | |
53 def getcommit(self, version): | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
54 c = self.catfile(version, "commit") # read the commit hash |
316 | 55 end = c.find("\n\n") |
56 message = c[end+2:] | |
57 l = c[:end].splitlines() | |
58 manifest = l[0].split()[1] | |
59 parents = [] | |
60 for e in l[1:]: | |
61 n,v = e.split(" ", 1) | |
62 if n == "author": | |
63 p = v.split() | |
1385
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
64 tm, tz = p[-2:] |
316 | 65 author = " ".join(p[:-2]) |
66 if author[0] == "<": author = author[1:-1] | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
67 if n == "committer": |
431 | 68 p = v.split() |
1385
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
69 tm, tz = p[-2:] |
431 | 70 committer = " ".join(p[:-2]) |
71 if committer[0] == "<": committer = committer[1:-1] | |
1385
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
72 message += "\ncommitter: %s\n" % v |
316 | 73 if n == "parent": parents.append(v) |
1385
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
74 |
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
75 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:] |
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
76 tz = int(tzs) * (int(tzh) * 3600 + int(tzm)) |
adb3de56635b
convert-repo: Fix timezone handling
Matt Mackall <mpm@selenic.com>
parents:
1335
diff
changeset
|
77 date = tm + " " + str(tz) |
316 | 78 return (parents, author, date, message) |
79 | |
694 | 80 def gettags(self): |
81 tags = {} | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
82 for f in os.listdir(self.path + "/refs/tags"): |
694 | 83 try: |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
84 h = file(self.path + "/refs/tags/" + f).read().strip() |
1386
a1040345fdda
convert-repo: retrieve the commit hash from the tag object for tag import
Matt Mackall <mpm@selenic.com>
parents:
1385
diff
changeset
|
85 c = self.catfile(h, "tag") # read the commit hash |
a1040345fdda
convert-repo: retrieve the commit hash from the tag object for tag import
Matt Mackall <mpm@selenic.com>
parents:
1385
diff
changeset
|
86 h = c.splitlines()[0].split()[1] |
1237 | 87 tags[f] = h |
694 | 88 except: |
89 pass | |
90 return tags | |
91 | |
316 | 92 class convert_mercurial: |
93 def __init__(self, path): | |
94 self.path = path | |
95 u = ui.ui() | |
96 self.repo = hg.repository(u, path) | |
97 | |
98 def getheads(self): | |
99 h = self.repo.changelog.heads() | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
100 return [ hg.hex(x) for x in h ] |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
101 |
316 | 102 def putfile(self, f, e, data): |
103 self.repo.wfile(f, "w").write(data) | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
104 if self.repo.dirstate.state(f) == '?': |
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
105 self.repo.dirstate.update([f], "a") |
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
106 |
450 | 107 util.set_exec(self.repo.wjoin(f), e) |
316 | 108 |
109 def delfile(self, f): | |
110 try: | |
111 os.unlink(self.repo.wjoin(f)) | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
112 #self.repo.remove([f]) |
316 | 113 except: |
114 pass | |
115 | |
116 def putcommit(self, files, parents, author, dest, text): | |
431 | 117 seen = {} |
118 pl = [] | |
119 for p in parents: | |
120 if p not in seen: | |
121 pl.append(p) | |
122 seen[p] = 1 | |
123 parents = pl | |
316 | 124 |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
125 if len(parents) < 2: parents.append("0" * 40) |
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
126 if len(parents) < 2: parents.append("0" * 40) |
431 | 127 p2 = parents.pop(0) |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
128 |
431 | 129 while parents: |
130 p1 = p2 | |
131 p2 = parents.pop(0) | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
132 self.repo.rawcommit(files, text, author, dest, |
431 | 133 hg.bin(p1), hg.bin(p2)) |
134 text = "(octopus merge fixup)\n" | |
1389
9b3ef6f3cef5
convert-repo: fix up octopus merge conversion
Matt Mackall <mpm@selenic.com>
parents:
1388
diff
changeset
|
135 p2 = hg.hex(self.repo.changelog.tip()) |
431 | 136 |
1389
9b3ef6f3cef5
convert-repo: fix up octopus merge conversion
Matt Mackall <mpm@selenic.com>
parents:
1388
diff
changeset
|
137 return p2 |
316 | 138 |
694 | 139 def puttags(self, tags): |
140 try: | |
141 old = self.repo.wfile(".hgtags").read() | |
142 oldlines = old.splitlines(1) | |
143 oldlines.sort() | |
144 except: | |
145 oldlines = [] | |
146 | |
147 k = tags.keys() | |
148 k.sort() | |
149 newlines = [] | |
150 for tag in k: | |
151 newlines.append("%s %s\n" % (tags[tag], tag)) | |
152 | |
153 newlines.sort() | |
154 | |
155 if newlines != oldlines: | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
156 #print "updating tags" |
694 | 157 f = self.repo.wfile(".hgtags", "w") |
158 f.write("".join(newlines)) | |
159 f.close() | |
160 if not oldlines: self.repo.add([".hgtags"]) | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
161 date = "%s 0" % int(time.mktime(time.gmtime())) |
694 | 162 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo", |
163 date, self.repo.changelog.tip(), hg.nullid) | |
1387
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
164 return hg.hex(self.repo.changelog.tip()) |
694 | 165 |
316 | 166 class convert: |
167 def __init__(self, source, dest, mapfile): | |
168 self.source = source | |
169 self.dest = dest | |
170 self.mapfile = mapfile | |
171 self.commitcache = {} | |
172 | |
173 self.map = {} | |
174 for l in file(self.mapfile): | |
175 sv, dv = l[:-1].split() | |
176 self.map[sv] = dv | |
177 | |
178 def walktree(self, heads): | |
179 visit = heads | |
180 known = {} | |
181 parents = {} | |
182 while visit: | |
183 n = visit.pop(0) | |
184 if n in known or n in self.map: continue | |
185 known[n] = 1 | |
186 self.commitcache[n] = self.source.getcommit(n) | |
187 cp = self.commitcache[n][0] | |
188 for p in cp: | |
189 parents.setdefault(n, []).append(p) | |
190 visit.append(p) | |
191 | |
192 return parents | |
193 | |
194 def toposort(self, parents): | |
195 visit = parents.keys() | |
196 seen = {} | |
197 children = {} | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
198 |
316 | 199 while visit: |
200 n = visit.pop(0) | |
201 if n in seen: continue | |
202 seen[n] = 1 | |
203 pc = 0 | |
204 if n in parents: | |
205 for p in parents[n]: | |
206 if p not in self.map: pc += 1 | |
207 visit.append(p) | |
208 children.setdefault(p, []).append(n) | |
209 if not pc: root = n | |
210 | |
211 s = [] | |
212 removed = {} | |
692
695dd9a491da
convert-repo: deal with packed git and other fixes
mpm@selenic.com
parents:
450
diff
changeset
|
213 visit = children.keys() |
316 | 214 while visit: |
215 n = visit.pop(0) | |
216 if n in removed: continue | |
217 dep = 0 | |
218 if n in parents: | |
219 for p in parents[n]: | |
220 if p in self.map: continue | |
221 if p not in removed: | |
222 # we're still dependent | |
223 visit.append(n) | |
224 dep = 1 | |
225 break | |
226 | |
227 if not dep: | |
228 # all n's parents are in the list | |
229 removed[n] = 1 | |
230 s.append(n) | |
231 if n in children: | |
232 for c in children[n]: | |
233 visit.insert(0, c) | |
234 | |
235 return s | |
236 | |
237 def copy(self, rev): | |
238 p, a, d, t = self.commitcache[rev] | |
239 files = self.source.getchanges(rev) | |
240 | |
241 for f,v,e in files: | |
242 try: | |
243 data = self.source.getfile(f, v) | |
244 except IOError, inst: | |
245 self.dest.delfile(f) | |
246 else: | |
247 self.dest.putfile(f, e, data) | |
248 | |
249 r = [self.map[v] for v in p] | |
250 f = [f for f,v,e in files] | |
251 self.map[rev] = self.dest.putcommit(f, r, a, d, t) | |
252 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev])) | |
253 | |
254 def convert(self): | |
255 heads = self.source.getheads() | |
256 parents = self.walktree(heads) | |
257 t = self.toposort(parents) | |
1388
5eb2d3c54165
convert-repo: change duplicate elimination
Matt Mackall <mpm@selenic.com>
parents:
1387
diff
changeset
|
258 t = [n for n in t if n not in self.map] |
316 | 259 num = len(t) |
260 | |
261 for c in t: | |
262 num -= 1 | |
263 desc = self.commitcache[c][3].splitlines()[0] | |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
264 #print num, desc |
316 | 265 self.copy(c) |
266 | |
694 | 267 tags = self.source.gettags() |
268 ctags = {} | |
269 for k in tags: | |
270 v = tags[k] | |
271 if v in self.map: | |
272 ctags[k] = self.map[v] | |
273 | |
1387
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
274 if ctags: |
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
275 nrev = self.dest.puttags(ctags) |
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
276 # write another hash correspondence to override the previous |
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
277 # one so we don't end up with extra tag heads |
0c7e8d345564
convert-repo: linearize the tag commit
Matt Mackall <mpm@selenic.com>
parents:
1386
diff
changeset
|
278 file(self.mapfile, "a").write("%s %s\n" % (c, nrev)) |
694 | 279 |
316 | 280 gitpath, hgpath, mapfile = sys.argv[1:] |
1335
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
281 if os.path.isdir(gitpath + "/.git"): |
bea6356b8bca
git -> hg conversion script
Florian La Roche <laroche@redhat.com>
parents:
1237
diff
changeset
|
282 gitpath += "/.git" |
316 | 283 |
284 c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile) | |
285 c.convert() |