##// END OF EJS Templates
convert: add filename filtering and renaming support
Bryan O'Sullivan -
r5016:4ebc8693 default
parent child Browse files
Show More
@@ -11,8 +11,9 b' 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
@@ -42,7 +43,7 b' def convertsink(ui, path):'
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
@@ -53,6 +54,7 b' class convert(object):'
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:
@@ -191,28 +193,31 b' class convert(object):'
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)
197 do_copies = hasattr(self.dest, 'copyfile')
198 filenames = []
196
199
197 do_copies = hasattr(self.dest, 'copyfile')
200 for f, v in self.source.getchanges(rev):
198
201 newf = self.mapfile(f)
199 for f, v in files:
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:
216 # Merely marks that a copy happened.
217 self.dest.copyfile(copyf, newf)
211
218
212
219 parents = [self.map[r] for r in commit.parents]
213 r = [self.map[v] for v in c.parents]
220 newnode = self.dest.putcommit(filenames, parents, commit)
214 f = [f for f, v in files]
215 newnode = self.dest.putcommit(f, r, c)
216 self.mapentry(rev, newnode)
221 self.mapentry(rev, newnode)
217
222
218 def convert(self):
223 def convert(self):
@@ -262,6 +267,87 b' class convert(object):'
262 if self.revmapfilefd:
267 if self.revmapfilefd:
263 self.revmapfilefd.close()
268 self.revmapfilefd.close()
264
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
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
@@ -343,13 +429,16 b' def _convert(ui, src, dest=None, revmapf'
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]]'),
@@ -59,7 +59,10 b' class mercurial_sink(converter_sink):'
59 pass
59 pass
60
60
61 def putcommit(self, files, parents, commit):
61 def putcommit(self, files, parents, commit):
62 seen = {}
62 if not files:
63 return hex(self.repo.changelog.tip())
64
65 seen = {hex(nullid): 1}
63 pl = []
66 pl = []
64 for p in parents:
67 for p in parents:
65 if p not in seen:
68 if p not in seen:
General Comments 0
You need to be logged in to leave comments. Login now