##// END OF EJS Templates
convert extension: Save a few opens on the map file...
Edouard Gomez -
r4588:9855939d default
parent child Browse files
Show More
@@ -1,242 +1,262 b''
1 1 # convert.py Foreign SCM converter
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from common import NoRepo
9 9 from cvs import convert_cvs
10 10 from git import convert_git
11 11 from hg import convert_mercurial
12 12
13 13 import os
14 14 from mercurial import hg, ui, util, commands
15 15
16 16 commands.norepo += " convert"
17 17
18 18 converters = [convert_cvs, convert_git, convert_mercurial]
19 19
20 20 def converter(ui, path):
21 21 if not os.path.isdir(path):
22 22 raise util.Abort("%s: not a directory" % path)
23 23 for c in converters:
24 24 try:
25 25 return c(ui, path)
26 26 except NoRepo:
27 27 pass
28 28 raise util.Abort("%s: unknown repository type" % path)
29 29
30 30 class convert(object):
31 31 def __init__(self, ui, source, dest, mapfile, opts):
32 32
33 33 self.source = source
34 34 self.dest = dest
35 35 self.ui = ui
36 self.mapfile = mapfile
37 36 self.opts = opts
38 37 self.commitcache = {}
38 self.mapfile = mapfile
39 self.mapfilefd = None
39 40
40 41 self.map = {}
41 42 try:
42 for l in file(self.mapfile):
43 origmapfile = open(self.mapfile, 'r')
44 for l in origmapfile:
43 45 sv, dv = l[:-1].split()
44 46 self.map[sv] = dv
47 origmapfile.close()
45 48 except IOError:
46 49 pass
47 50
48 51 def walktree(self, heads):
49 52 visit = heads
50 53 known = {}
51 54 parents = {}
52 55 while visit:
53 56 n = visit.pop(0)
54 57 if n in known or n in self.map: continue
55 58 known[n] = 1
56 59 self.commitcache[n] = self.source.getcommit(n)
57 60 cp = self.commitcache[n].parents
58 61 for p in cp:
59 62 parents.setdefault(n, []).append(p)
60 63 visit.append(p)
61 64
62 65 return parents
63 66
64 67 def toposort(self, parents):
65 68 visit = parents.keys()
66 69 seen = {}
67 70 children = {}
68 71
69 72 while visit:
70 73 n = visit.pop(0)
71 74 if n in seen: continue
72 75 seen[n] = 1
73 76 pc = 0
74 77 if n in parents:
75 78 for p in parents[n]:
76 79 if p not in self.map: pc += 1
77 80 visit.append(p)
78 81 children.setdefault(p, []).append(n)
79 82 if not pc: root = n
80 83
81 84 s = []
82 85 removed = {}
83 86 visit = children.keys()
84 87 while visit:
85 88 n = visit.pop(0)
86 89 if n in removed: continue
87 90 dep = 0
88 91 if n in parents:
89 92 for p in parents[n]:
90 93 if p in self.map: continue
91 94 if p not in removed:
92 95 # we're still dependent
93 96 visit.append(n)
94 97 dep = 1
95 98 break
96 99
97 100 if not dep:
98 101 # all n's parents are in the list
99 102 removed[n] = 1
100 103 if n not in self.map:
101 104 s.append(n)
102 105 if n in children:
103 106 for c in children[n]:
104 107 visit.insert(0, c)
105 108
106 109 if self.opts.get('datesort'):
107 110 depth = {}
108 111 for n in s:
109 112 depth[n] = 0
110 113 pl = [p for p in self.commitcache[n].parents
111 114 if p not in self.map]
112 115 if pl:
113 116 depth[n] = max([depth[p] for p in pl]) + 1
114 117
115 118 s = [(depth[n], self.commitcache[n].date, n) for n in s]
116 119 s.sort()
117 120 s = [e[2] for e in s]
118 121
119 122 return s
120 123
124 def mapentry(self, src, dst):
125 if self.mapfilefd is None:
126 try:
127 self.mapfilefd = open(self.mapfile, "a")
128 except IOError, (errno, strerror):
129 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
130 self.map[src] = dst
131 self.mapfilefd.write("%s %s\n" % (src, dst))
132 self.mapfilefd.flush()
133
121 134 def copy(self, rev):
122 135 c = self.commitcache[rev]
123 136 files = self.source.getchanges(rev)
124 137
125 138 for f, v in files:
126 139 try:
127 140 data = self.source.getfile(f, v)
128 141 except IOError, inst:
129 142 self.dest.delfile(f)
130 143 else:
131 144 e = self.source.getmode(f, v)
132 145 self.dest.putfile(f, e, data)
133 146
134 147 r = [self.map[v] for v in c.parents]
135 148 f = [f for f, v in files]
136 self.map[rev] = self.dest.putcommit(f, r, c)
137 file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
149 newnode = self.dest.putcommit(f, r, c)
150 self.mapentry(rev, newnode)
138 151
139 152 def convert(self):
140 self.ui.status("scanning source...\n")
141 heads = self.source.getheads()
142 parents = self.walktree(heads)
143 self.ui.status("sorting...\n")
144 t = self.toposort(parents)
145 num = len(t)
146 c = None
153 try:
154 self.ui.status("scanning source...\n")
155 heads = self.source.getheads()
156 parents = self.walktree(heads)
157 self.ui.status("sorting...\n")
158 t = self.toposort(parents)
159 num = len(t)
160 c = None
161
162 self.ui.status("converting...\n")
163 for c in t:
164 num -= 1
165 desc = self.commitcache[c].desc
166 if "\n" in desc:
167 desc = desc.splitlines()[0]
168 self.ui.status("%d %s\n" % (num, desc))
169 self.copy(c)
147 170
148 self.ui.status("converting...\n")
149 for c in t:
150 num -= 1
151 desc = self.commitcache[c].desc
152 if "\n" in desc:
153 desc = desc.splitlines()[0]
154 self.ui.status("%d %s\n" % (num, desc))
155 self.copy(c)
171 tags = self.source.gettags()
172 ctags = {}
173 for k in tags:
174 v = tags[k]
175 if v in self.map:
176 ctags[k] = self.map[v]
156 177
157 tags = self.source.gettags()
158 ctags = {}
159 for k in tags:
160 v = tags[k]
161 if v in self.map:
162 ctags[k] = self.map[v]
178 if c and ctags:
179 nrev = self.dest.puttags(ctags)
180 # write another hash correspondence to override the previous
181 # one so we don't end up with extra tag heads
182 if nrev:
183 self.mapentry(c, nrev)
184 finally:
185 self.cleanup()
163 186
164 if c and ctags:
165 nrev = self.dest.puttags(ctags)
166 # write another hash correspondence to override the previous
167 # one so we don't end up with extra tag heads
168 if nrev:
169 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
187 def cleanup(self):
188 if self.mapfilefd:
189 self.mapfilefd.close()
170 190
171 191 def _convert(ui, src, dest=None, mapfile=None, **opts):
172 192 '''Convert a foreign SCM repository to a Mercurial one.
173 193
174 194 Accepted source formats:
175 195 - GIT
176 196 - CVS
177 197
178 198 Accepted destination formats:
179 199 - Mercurial
180 200
181 201 If destination isn't given, a new Mercurial repo named <src>-hg will
182 202 be created. If <mapfile> isn't given, it will be put in a default
183 203 location (<dest>/.hg/shamap by default)
184 204
185 205 The <mapfile> is a simple text file that maps each source commit ID to
186 206 the destination ID for that revision, like so:
187 207
188 208 <source ID> <destination ID>
189 209
190 210 If the file doesn't exist, it's automatically created. It's updated
191 211 on each commit copied, so convert-repo can be interrupted and can
192 212 be run repeatedly to copy new commits.
193 213 '''
194 214
195 215 srcc = converter(ui, src)
196 216 if not hasattr(srcc, "getcommit"):
197 217 raise util.Abort("%s: can't read from this repo type" % src)
198 218
199 219 if not dest:
200 220 dest = src + "-hg"
201 221 ui.status("assuming destination %s\n" % dest)
202 222
203 223 # Try to be smart and initalize things when required
204 224 if os.path.isdir(dest):
205 225 if len(os.listdir(dest)) > 0:
206 226 try:
207 227 hg.repository(ui, dest)
208 228 ui.status("destination %s is a Mercurial repository\n" % dest)
209 229 except hg.RepoError:
210 230 raise util.Abort(
211 231 "destination directory %s is not empty.\n"
212 232 "Please specify an empty directory to be initialized\n"
213 233 "or an already initialized mercurial repository"
214 234 % dest)
215 235 else:
216 236 ui.status("initializing destination %s repository\n" % dest)
217 237 hg.repository(ui, dest, create=True)
218 238 elif os.path.exists(dest):
219 239 raise util.Abort("destination %s exists and is not a directory" % dest)
220 240 else:
221 241 ui.status("initializing destination %s repository\n" % dest)
222 242 hg.repository(ui, dest, create=True)
223 243
224 244 destc = converter(ui, dest)
225 245 if not hasattr(destc, "putcommit"):
226 246 raise util.Abort("%s: can't write to this repo type" % src)
227 247
228 248 if not mapfile:
229 249 try:
230 250 mapfile = destc.mapfile()
231 251 except:
232 252 mapfile = os.path.join(destc, "map")
233 253
234 254 c = convert(ui, srcc, destc, mapfile, opts)
235 255 c.convert()
236 256
237 257 cmdtable = {
238 258 "convert":
239 259 (_convert,
240 260 [('', 'datesort', None, 'try to sort changesets by date')],
241 261 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
242 262 }
General Comments 0
You need to be logged in to leave comments. Login now