##// END OF EJS Templates
convert: remove pdb call. *blush*
Brendan Cully -
r4591:9ec0a3b6 default
parent child Browse files
Show More
@@ -1,315 +1,313 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 36 self.opts = opts
37 37 self.commitcache = {}
38 38 self.mapfile = mapfile
39 39 self.mapfilefd = None
40 40 self.authors = {}
41 41 self.authorfile = None
42 42
43 43 self.map = {}
44 44 try:
45 45 origmapfile = open(self.mapfile, 'r')
46 46 for l in origmapfile:
47 47 sv, dv = l[:-1].split()
48 48 self.map[sv] = dv
49 49 origmapfile.close()
50 50 except IOError:
51 51 pass
52 52
53 53 # Read first the dst author map if any
54 54 authorfile = self.dest.authorfile()
55 55 if authorfile and os.path.exists(authorfile):
56 56 self.readauthormap(authorfile)
57 57 # Extend/Override with new author map if necessary
58 58 if opts.get('authors'):
59 import pdb
60 pdb.set_trace()
61 59 self.readauthormap(opts.get('authors'))
62 60 self.authorfile = self.dest.authorfile()
63 61
64 62 def walktree(self, heads):
65 63 visit = heads
66 64 known = {}
67 65 parents = {}
68 66 while visit:
69 67 n = visit.pop(0)
70 68 if n in known or n in self.map: continue
71 69 known[n] = 1
72 70 self.commitcache[n] = self.source.getcommit(n)
73 71 cp = self.commitcache[n].parents
74 72 for p in cp:
75 73 parents.setdefault(n, []).append(p)
76 74 visit.append(p)
77 75
78 76 return parents
79 77
80 78 def toposort(self, parents):
81 79 visit = parents.keys()
82 80 seen = {}
83 81 children = {}
84 82
85 83 while visit:
86 84 n = visit.pop(0)
87 85 if n in seen: continue
88 86 seen[n] = 1
89 87 pc = 0
90 88 if n in parents:
91 89 for p in parents[n]:
92 90 if p not in self.map: pc += 1
93 91 visit.append(p)
94 92 children.setdefault(p, []).append(n)
95 93 if not pc: root = n
96 94
97 95 s = []
98 96 removed = {}
99 97 visit = children.keys()
100 98 while visit:
101 99 n = visit.pop(0)
102 100 if n in removed: continue
103 101 dep = 0
104 102 if n in parents:
105 103 for p in parents[n]:
106 104 if p in self.map: continue
107 105 if p not in removed:
108 106 # we're still dependent
109 107 visit.append(n)
110 108 dep = 1
111 109 break
112 110
113 111 if not dep:
114 112 # all n's parents are in the list
115 113 removed[n] = 1
116 114 if n not in self.map:
117 115 s.append(n)
118 116 if n in children:
119 117 for c in children[n]:
120 118 visit.insert(0, c)
121 119
122 120 if self.opts.get('datesort'):
123 121 depth = {}
124 122 for n in s:
125 123 depth[n] = 0
126 124 pl = [p for p in self.commitcache[n].parents
127 125 if p not in self.map]
128 126 if pl:
129 127 depth[n] = max([depth[p] for p in pl]) + 1
130 128
131 129 s = [(depth[n], self.commitcache[n].date, n) for n in s]
132 130 s.sort()
133 131 s = [e[2] for e in s]
134 132
135 133 return s
136 134
137 135 def mapentry(self, src, dst):
138 136 if self.mapfilefd is None:
139 137 try:
140 138 self.mapfilefd = open(self.mapfile, "a")
141 139 except IOError, (errno, strerror):
142 140 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
143 141 self.map[src] = dst
144 142 self.mapfilefd.write("%s %s\n" % (src, dst))
145 143 self.mapfilefd.flush()
146 144
147 145 def writeauthormap(self):
148 146 authorfile = self.authorfile
149 147 if authorfile:
150 148 self.ui.status('Writing author map file %s\n' % authorfile)
151 149 ofile = open(authorfile, 'w+')
152 150 for author in self.authors:
153 151 ofile.write("%s=%s\n" % (author, self.authors[author]))
154 152 ofile.close()
155 153
156 154 def readauthormap(self, authorfile):
157 155 afile = open(authorfile, 'r')
158 156 for line in afile:
159 157 try:
160 158 srcauthor = line.split('=')[0].strip()
161 159 dstauthor = line.split('=')[1].strip()
162 160 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
163 161 self.ui.status(
164 162 'Overriding mapping for author %s, was %s, will be %s\n'
165 163 % (srcauthor, self.authors[srcauthor], dstauthor))
166 164 else:
167 165 self.ui.debug('Mapping author %s to %s\n'
168 166 % (srcauthor, dstauthor))
169 167 self.authors[srcauthor] = dstauthor
170 168 except IndexError:
171 169 self.ui.warn(
172 170 'Ignoring bad line in author file map %s: %s\n'
173 171 % (authorfile, line))
174 172 afile.close()
175 173
176 174 def copy(self, rev):
177 175 c = self.commitcache[rev]
178 176 files = self.source.getchanges(rev)
179 177
180 178 for f, v in files:
181 179 try:
182 180 data = self.source.getfile(f, v)
183 181 except IOError, inst:
184 182 self.dest.delfile(f)
185 183 else:
186 184 e = self.source.getmode(f, v)
187 185 self.dest.putfile(f, e, data)
188 186
189 187 r = [self.map[v] for v in c.parents]
190 188 f = [f for f, v in files]
191 189 newnode = self.dest.putcommit(f, r, c)
192 190 self.mapentry(rev, newnode)
193 191
194 192 def convert(self):
195 193 try:
196 194 self.ui.status("scanning source...\n")
197 195 heads = self.source.getheads()
198 196 parents = self.walktree(heads)
199 197 self.ui.status("sorting...\n")
200 198 t = self.toposort(parents)
201 199 num = len(t)
202 200 c = None
203 201
204 202 self.ui.status("converting...\n")
205 203 for c in t:
206 204 num -= 1
207 205 desc = self.commitcache[c].desc
208 206 if "\n" in desc:
209 207 desc = desc.splitlines()[0]
210 208 author = self.commitcache[c].author
211 209 author = self.authors.get(author, author)
212 210 self.commitcache[c].author = author
213 211 self.ui.status("%d %s\n" % (num, desc))
214 212 self.copy(c)
215 213
216 214 tags = self.source.gettags()
217 215 ctags = {}
218 216 for k in tags:
219 217 v = tags[k]
220 218 if v in self.map:
221 219 ctags[k] = self.map[v]
222 220
223 221 if c and ctags:
224 222 nrev = self.dest.puttags(ctags)
225 223 # write another hash correspondence to override the previous
226 224 # one so we don't end up with extra tag heads
227 225 if nrev:
228 226 self.mapentry(c, nrev)
229 227
230 228 self.writeauthormap()
231 229 finally:
232 230 self.cleanup()
233 231
234 232 def cleanup(self):
235 233 if self.mapfilefd:
236 234 self.mapfilefd.close()
237 235
238 236 def _convert(ui, src, dest=None, mapfile=None, **opts):
239 237 '''Convert a foreign SCM repository to a Mercurial one.
240 238
241 239 Accepted source formats:
242 240 - GIT
243 241 - CVS
244 242
245 243 Accepted destination formats:
246 244 - Mercurial
247 245
248 246 If destination isn't given, a new Mercurial repo named <src>-hg will
249 247 be created. If <mapfile> isn't given, it will be put in a default
250 248 location (<dest>/.hg/shamap by default)
251 249
252 250 The <mapfile> is a simple text file that maps each source commit ID to
253 251 the destination ID for that revision, like so:
254 252 <source ID> <destination ID>
255 253
256 254 If the file doesn't exist, it's automatically created. It's updated
257 255 on each commit copied, so convert-repo can be interrupted and can
258 256 be run repeatedly to copy new commits.
259 257
260 258 The [username mapping] file is a simple text file that maps each source
261 259 commit author to a destination commit author. It is handy for source SCMs
262 260 that use unix logins to identify authors (eg: CVS). One line per author
263 261 mapping and the line format is:
264 262 srcauthor=whatever string you want
265 263 '''
266 264
267 265 srcc = converter(ui, src)
268 266 if not hasattr(srcc, "getcommit"):
269 267 raise util.Abort("%s: can't read from this repo type" % src)
270 268
271 269 if not dest:
272 270 dest = src + "-hg"
273 271 ui.status("assuming destination %s\n" % dest)
274 272
275 273 # Try to be smart and initalize things when required
276 274 if os.path.isdir(dest):
277 275 if len(os.listdir(dest)) > 0:
278 276 try:
279 277 hg.repository(ui, dest)
280 278 ui.status("destination %s is a Mercurial repository\n" % dest)
281 279 except hg.RepoError:
282 280 raise util.Abort(
283 281 "destination directory %s is not empty.\n"
284 282 "Please specify an empty directory to be initialized\n"
285 283 "or an already initialized mercurial repository"
286 284 % dest)
287 285 else:
288 286 ui.status("initializing destination %s repository\n" % dest)
289 287 hg.repository(ui, dest, create=True)
290 288 elif os.path.exists(dest):
291 289 raise util.Abort("destination %s exists and is not a directory" % dest)
292 290 else:
293 291 ui.status("initializing destination %s repository\n" % dest)
294 292 hg.repository(ui, dest, create=True)
295 293
296 294 destc = converter(ui, dest)
297 295 if not hasattr(destc, "putcommit"):
298 296 raise util.Abort("%s: can't write to this repo type" % src)
299 297
300 298 if not mapfile:
301 299 try:
302 300 mapfile = destc.mapfile()
303 301 except:
304 302 mapfile = os.path.join(destc, "map")
305 303
306 304 c = convert(ui, srcc, destc, mapfile, opts)
307 305 c.convert()
308 306
309 307 cmdtable = {
310 308 "convert":
311 309 (_convert,
312 310 [('A', 'authors', '', 'username mapping filename'),
313 311 ('', 'datesort', None, 'try to sort changesets by date')],
314 312 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
315 313 }
General Comments 0
You need to be logged in to leave comments. Login now