Mercurial > hg > mercurial-crew-with-dirclash
comparison hgext/convert/__init__.py @ 5016:4ebc8693ce72
convert: add filename filtering and renaming support
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Thu, 26 Jul 2007 13:34:36 -0700 |
parents | 914054ca532e |
children | c7623d2b2a66 |
comparison
equal
deleted
inserted
replaced
5015:cb100605a516 | 5016:4ebc8693ce72 |
---|---|
9 from cvs import convert_cvs | 9 from cvs import convert_cvs |
10 from git import convert_git | 10 from git import convert_git |
11 from hg import mercurial_source, mercurial_sink | 11 from hg import mercurial_source, mercurial_sink |
12 from subversion import convert_svn | 12 from subversion import convert_svn |
13 | 13 |
14 import os, shutil | 14 import os, shlex, shutil |
15 from mercurial import hg, ui, util, commands | 15 from mercurial import hg, ui, util, commands |
16 from mercurial.i18n import _ | |
16 | 17 |
17 commands.norepo += " convert" | 18 commands.norepo += " convert" |
18 | 19 |
19 converters = [convert_cvs, convert_git, convert_svn, mercurial_source, | 20 converters = [convert_cvs, convert_git, convert_svn, mercurial_source, |
20 mercurial_sink] | 21 mercurial_sink] |
40 except NoRepo: | 41 except NoRepo: |
41 pass | 42 pass |
42 raise util.Abort('%s: unknown repository type' % path) | 43 raise util.Abort('%s: unknown repository type' % path) |
43 | 44 |
44 class convert(object): | 45 class convert(object): |
45 def __init__(self, ui, source, dest, revmapfile, opts): | 46 def __init__(self, ui, source, dest, revmapfile, filemapper, opts): |
46 | 47 |
47 self.source = source | 48 self.source = source |
48 self.dest = dest | 49 self.dest = dest |
49 self.ui = ui | 50 self.ui = ui |
50 self.opts = opts | 51 self.opts = opts |
51 self.commitcache = {} | 52 self.commitcache = {} |
52 self.revmapfile = revmapfile | 53 self.revmapfile = revmapfile |
53 self.revmapfilefd = None | 54 self.revmapfilefd = None |
54 self.authors = {} | 55 self.authors = {} |
55 self.authorfile = None | 56 self.authorfile = None |
57 self.mapfile = filemapper | |
56 | 58 |
57 self.map = {} | 59 self.map = {} |
58 try: | 60 try: |
59 origrevmapfile = open(self.revmapfile, 'r') | 61 origrevmapfile = open(self.revmapfile, 'r') |
60 for l in origrevmapfile: | 62 for l in origrevmapfile: |
189 'Ignoring bad line in author file map %s: %s\n' | 191 'Ignoring bad line in author file map %s: %s\n' |
190 % (authorfile, line)) | 192 % (authorfile, line)) |
191 afile.close() | 193 afile.close() |
192 | 194 |
193 def copy(self, rev): | 195 def copy(self, rev): |
194 c = self.commitcache[rev] | 196 commit = self.commitcache[rev] |
195 files = self.source.getchanges(rev) | |
196 | |
197 do_copies = hasattr(self.dest, 'copyfile') | 197 do_copies = hasattr(self.dest, 'copyfile') |
198 | 198 filenames = [] |
199 for f, v in files: | 199 |
200 for f, v in self.source.getchanges(rev): | |
201 newf = self.mapfile(f) | |
202 if not newf: | |
203 continue | |
204 filenames.append(newf) | |
200 try: | 205 try: |
201 data = self.source.getfile(f, v) | 206 data = self.source.getfile(f, v) |
202 except IOError, inst: | 207 except IOError, inst: |
203 self.dest.delfile(f) | 208 self.dest.delfile(newf) |
204 else: | 209 else: |
205 e = self.source.getmode(f, v) | 210 e = self.source.getmode(f, v) |
206 self.dest.putfile(f, e, data) | 211 self.dest.putfile(newf, e, data) |
207 if do_copies: | 212 if do_copies: |
208 if f in c.copies: | 213 if f in commit.copies: |
209 # Merely marks that a copy happened. | 214 copyf = self.mapfile(commit.copies[f]) |
210 self.dest.copyfile(c.copies[f], f) | 215 if copyf: |
211 | 216 # Merely marks that a copy happened. |
212 | 217 self.dest.copyfile(copyf, newf) |
213 r = [self.map[v] for v in c.parents] | 218 |
214 f = [f for f, v in files] | 219 parents = [self.map[r] for r in commit.parents] |
215 newnode = self.dest.putcommit(f, r, c) | 220 newnode = self.dest.putcommit(filenames, parents, commit) |
216 self.mapentry(rev, newnode) | 221 self.mapentry(rev, newnode) |
217 | 222 |
218 def convert(self): | 223 def convert(self): |
219 try: | 224 try: |
220 self.dest.before() | 225 self.dest.before() |
259 | 264 |
260 def cleanup(self): | 265 def cleanup(self): |
261 self.dest.after() | 266 self.dest.after() |
262 if self.revmapfilefd: | 267 if self.revmapfilefd: |
263 self.revmapfilefd.close() | 268 self.revmapfilefd.close() |
269 | |
270 def rpairs(name): | |
271 e = len(name) | |
272 while e != -1: | |
273 yield name[:e], name[e+1:] | |
274 e = name.rfind('/', 0, e) | |
275 | |
276 class filemapper(object): | |
277 '''Map and filter filenames when importing. | |
278 A name can be mapped to itself, a new name, or None (omit from new | |
279 repository).''' | |
280 | |
281 def __init__(self, ui, path=None): | |
282 self.ui = ui | |
283 self.include = {} | |
284 self.exclude = {} | |
285 self.rename = {} | |
286 if path: | |
287 if self.parse(path): | |
288 raise util.Abort(_('errors in filemap')) | |
289 | |
290 def parse(self, path): | |
291 errs = 0 | |
292 def check(name, mapping, listname): | |
293 if name in mapping: | |
294 self.ui.warn(_('%s:%d: %r already in %s list\n') % | |
295 (lex.infile, lex.lineno, name, listname)) | |
296 return 1 | |
297 return 0 | |
298 lex = shlex.shlex(open(path), path, True) | |
299 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?' | |
300 cmd = lex.get_token() | |
301 while cmd: | |
302 if cmd == 'include': | |
303 name = lex.get_token() | |
304 errs += check(name, self.exclude, 'exclude') | |
305 self.include[name] = name | |
306 elif cmd == 'exclude': | |
307 name = lex.get_token() | |
308 errs += check(name, self.include, 'include') | |
309 errs += check(name, self.rename, 'rename') | |
310 self.exclude[name] = name | |
311 elif cmd == 'rename': | |
312 src = lex.get_token() | |
313 dest = lex.get_token() | |
314 errs += check(src, self.exclude, 'exclude') | |
315 self.rename[src] = dest | |
316 elif cmd == 'source': | |
317 errs += self.parse(lex.get_token()) | |
318 else: | |
319 self.ui.warn(_('%s:%d: unknown directive %r\n') % | |
320 (lex.infile, lex.lineno, cmd)) | |
321 errs += 1 | |
322 cmd = lex.get_token() | |
323 return errs | |
324 | |
325 def lookup(self, name, mapping): | |
326 for pre, suf in rpairs(name): | |
327 try: | |
328 return mapping[pre], pre, suf | |
329 except KeyError, err: | |
330 pass | |
331 return '', name, '' | |
332 | |
333 def __call__(self, name): | |
334 if self.include: | |
335 inc = self.lookup(name, self.include)[0] | |
336 else: | |
337 inc = name | |
338 if self.exclude: | |
339 exc = self.lookup(name, self.exclude)[0] | |
340 else: | |
341 exc = '' | |
342 if not inc or exc: | |
343 return None | |
344 newpre, pre, suf = self.lookup(name, self.rename) | |
345 if newpre: | |
346 if suf: | |
347 return newpre + '/' + suf | |
348 return newpre | |
349 return name | |
264 | 350 |
265 def _convert(ui, src, dest=None, revmapfile=None, **opts): | 351 def _convert(ui, src, dest=None, revmapfile=None, **opts): |
266 """Convert a foreign SCM repository to a Mercurial one. | 352 """Convert a foreign SCM repository to a Mercurial one. |
267 | 353 |
268 Accepted source formats: | 354 Accepted source formats: |
341 try: | 427 try: |
342 revmapfile = destc.revmapfile() | 428 revmapfile = destc.revmapfile() |
343 except: | 429 except: |
344 revmapfile = os.path.join(destc, "map") | 430 revmapfile = os.path.join(destc, "map") |
345 | 431 |
346 c = convert(ui, srcc, destc, revmapfile, opts) | 432 |
433 c = convert(ui, srcc, destc, revmapfile, filemapper(ui, opts['filemap']), | |
434 opts) | |
347 c.convert() | 435 c.convert() |
348 | 436 |
349 cmdtable = { | 437 cmdtable = { |
350 "convert": | 438 "convert": |
351 (_convert, | 439 (_convert, |
352 [('A', 'authors', '', 'username mapping filename'), | 440 [('A', 'authors', '', 'username mapping filename'), |
441 ('', 'filemap', '', 'remap file names using contents of file'), | |
353 ('r', 'rev', '', 'import up to target revision REV'), | 442 ('r', 'rev', '', 'import up to target revision REV'), |
354 ('', 'datesort', None, 'try to sort changesets by date')], | 443 ('', 'datesort', None, 'try to sort changesets by date')], |
355 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), | 444 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'), |
356 } | 445 } |