Trivial simplification
[fast-export.git] / hg-fast-export.py
blob71b518db0cf6c3c2c42834f9b0a94bdf4da62565
1 #!/usr/bin/env python2
3 # Copyright (c) 2007, 2008 Rocco Rutte <pdmef@gmx.net> and others.
4 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
6 from hg2git import setup_repo,fixup_user,get_branch,get_changeset
7 from hg2git import load_cache,save_cache,get_git_sha1,set_default_branch,set_origin_name
8 from optparse import OptionParser
9 import re
10 import sys
11 import os
12 from binascii import hexlify
13 import pluginloader
14 PY2 = sys.version_info.major == 2
15 if PY2:
16 str = unicode
18 if PY2 and sys.platform == "win32":
19 # On Windows, sys.stdout is initially opened in text mode, which means that
20 # when a LF (\n) character is written to sys.stdout, it will be converted
21 # into CRLF (\r\n). That makes git blow up, so use this platform-specific
22 # code to change the mode of sys.stdout to binary.
23 import msvcrt
24 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
26 # silly regex to catch Signed-off-by lines in log message
27 sob_re=re.compile(b'^Signed-[Oo]ff-[Bb]y: (.+)$')
28 # insert 'checkpoint' command after this many commits or none at all if 0
29 cfg_checkpoint_count=0
30 # write some progress message every this many file contents written
31 cfg_export_boundary=1000
33 subrepo_cache={}
34 submodule_mappings=None
36 # True if fast export should automatically try to sanitize
37 # author/branch/tag names.
38 auto_sanitize = None
40 stdout_buffer = sys.stdout if PY2 else sys.stdout.buffer
41 stderr_buffer = sys.stderr if PY2 else sys.stderr.buffer
43 def gitmode(flags):
44 return b'l' in flags and b'120000' or b'x' in flags and b'100755' or b'100644'
46 def wr_no_nl(msg=b''):
47 assert isinstance(msg, bytes)
48 if msg:
49 stdout_buffer.write(msg)
51 def wr(msg=b''):
52 wr_no_nl(msg + b'\n')
53 #map(lambda x: sys.stderr.write('\t[%s]\n' % x),msg.split('\n'))
55 def wr_data(data):
56 wr(b'data %d' % (len(data)))
57 wr(data)
59 def checkpoint(count):
60 count=count+1
61 if cfg_checkpoint_count>0 and count%cfg_checkpoint_count==0:
62 stderr_buffer.write(b"Checkpoint after %d commits\n" % count)
63 wr(b'checkpoint')
64 wr()
65 return count
67 def revnum_to_revref(rev, old_marks):
68 """Convert an hg revnum to a git-fast-import rev reference (an SHA1
69 or a mark)"""
70 return old_marks.get(rev) or b':%d' % (rev+1)
72 def get_filechanges(repo,revision,parents,files):
73 """Given some repository and revision, find all changed/deleted files."""
74 if not parents:
75 # first revision: feed in full manifest
76 return files,[]
77 else:
78 # take the changes from the first parent
79 f=repo.status(parents[0],revision)
80 return f.modified+f.added,f.removed
82 def get_author(logmessage,committer,authors):
83 """As git distincts between author and committer of a patch, try to
84 extract author by detecting Signed-off-by lines.
86 This walks from the end of the log message towards the top skipping
87 empty lines. Upon the first non-empty line, it walks all Signed-off-by
88 lines upwards to find the first one. For that (if found), it extracts
89 authorship information the usual way (authors table, cleaning, etc.)
91 If no Signed-off-by line is found, this defaults to the committer.
93 This may sound stupid (and it somehow is), but in log messages we
94 accidentially may have lines in the middle starting with
95 "Signed-off-by: foo" and thus matching our detection regex. Prevent
96 that."""
98 loglines=logmessage.split(b'\n')
99 i=len(loglines)
100 # from tail walk to top skipping empty lines
101 while i>=0:
102 i-=1
103 if len(loglines[i].strip())==0: continue
104 break
105 if i>=0:
106 # walk further upwards to find first sob line, store in 'first'
107 first=None
108 while i>=0:
109 m=sob_re.match(loglines[i])
110 if m==None: break
111 first=m
112 i-=1
113 # if the last non-empty line matches our Signed-Off-by regex: extract username
114 if first!=None:
115 r=fixup_user(first.group(1),authors)
116 return r
117 return committer
119 def remove_gitmodules(ctx):
120 """Removes all submodules of ctx parents"""
121 # Removing all submoduies coming from all parents is safe, as the submodules
122 # of the current commit will be re-added below. A possible optimization would
123 # be to only remove the submodules of the first parent.
124 for parent_ctx in ctx.parents():
125 for submodule in parent_ctx.substate.keys():
126 wr(b'D %s' % submodule)
127 wr(b'D .gitmodules')
129 def refresh_git_submodule(name,subrepo_info):
130 wr(b'M 160000 %s %s' % (subrepo_info[1],name))
131 stderr_buffer.write(
132 b"Adding/updating submodule %s, revision %s\n" % (name, subrepo_info[1])
134 return b'[submodule "%s"]\n\tpath = %s\n\turl = %s\n' % (name, name, subrepo_info[0])
136 def refresh_hg_submodule(name,subrepo_info):
137 gitRepoLocation=submodule_mappings[name] + b"/.git"
139 # Populate the cache to map mercurial revision to git revision
140 if not name in subrepo_cache:
141 subrepo_cache[name]=(load_cache(gitRepoLocation+b"/hg2git-mapping"),
142 load_cache(gitRepoLocation+b"/hg2git-marks",
143 lambda s: int(s)-1))
145 (mapping_cache,marks_cache)=subrepo_cache[name]
146 subrepo_hash=subrepo_info[1]
147 if subrepo_hash in mapping_cache:
148 revnum=mapping_cache[subrepo_hash]
149 gitSha=marks_cache[int(revnum)]
150 wr(b'M 160000 %s %s' % (gitSha,name))
151 stderr_buffer.write(
152 b"Adding/updating submodule %s, revision %s->%s\n"
153 % (name, subrepo_hash, gitSha)
155 return b'[submodule "%s"]\n\tpath = %s\n\turl = %s\n' % (name,name,
156 submodule_mappings[name])
157 else:
158 stderr_buffer.write(
159 b"Warning: Could not find hg revision %s for %s in git %s\n"
160 % (subrepo_hash, name, gitRepoLocation,)
162 return b''
164 def refresh_gitmodules(ctx):
165 """Updates list of ctx submodules according to .hgsubstate file"""
166 remove_gitmodules(ctx)
167 gitmodules=b""
168 # Create the .gitmodules file and all submodules
169 for name,subrepo_info in ctx.substate.items():
170 if subrepo_info[2]==b'git':
171 gitmodules+=refresh_git_submodule(name,subrepo_info)
172 elif submodule_mappings and name in submodule_mappings:
173 gitmodules+=refresh_hg_submodule(name,subrepo_info)
175 if len(gitmodules):
176 wr(b'M 100644 inline .gitmodules')
177 wr_data(gitmodules)
179 def export_file_contents(ctx,manifest,files,hgtags,encoding='',plugins={}):
180 count=0
181 max=len(files)
182 is_submodules_refreshed=False
183 for file in files:
184 if not is_submodules_refreshed and (file==b'.hgsub' or file==b'.hgsubstate'):
185 is_submodules_refreshed=True
186 refresh_gitmodules(ctx)
187 # Skip .hgtags files. They only get us in trouble.
188 if not hgtags and file == b".hgtags":
189 stderr_buffer.write(b'Skip %s\n' % file)
190 continue
191 if encoding:
192 filename=file.decode(encoding).encode('utf8')
193 else:
194 filename=file
195 if b'.git' in filename.split(b'/'): # Even on Windows, the path separator is / here.
196 stderr_buffer.write(
197 b'Ignoring file %s which cannot be tracked by git\n' % filename
199 continue
200 file_ctx=ctx.filectx(file)
201 d=file_ctx.data()
203 if plugins and plugins['file_data_filters']:
204 file_data = {'filename':filename,'file_ctx':file_ctx,'data':d}
205 for filter in plugins['file_data_filters']:
206 filter(file_data)
207 d=file_data['data']
208 filename=file_data['filename']
209 file_ctx=file_data['file_ctx']
211 wr(b'M %s inline %s' % (gitmode(manifest.flags(file)),
212 strip_leading_slash(filename)))
213 wr(b'data %d' % len(d)) # had some trouble with size()
214 wr(d)
215 count+=1
216 if count%cfg_export_boundary==0:
217 stderr_buffer.write(b'Exported %d/%d files\n' % (count,max))
218 if max>cfg_export_boundary:
219 stderr_buffer.write(b'Exported %d/%d files\n' % (count,max))
221 def sanitize_name(name,what="branch", mapping={}):
222 """Sanitize input roughly according to git-check-ref-format(1)"""
224 # NOTE: Do not update this transform to work around
225 # incompatibilities on your platform. If you change it and it starts
226 # modifying names which previously were not touched it will break
227 # preexisting setups which are doing incremental imports.
229 # Fast-export tries to not inflict arbitrary naming policy on the
230 # user, instead it aims to provide mechanisms allowing the user to
231 # apply their own policy. Therefore do not add a transform which can
232 # already be implemented with the -B and -T options to mangle branch
233 # and tag names. If you have a source repository where this is too
234 # much work to do manually, write a tool that does it for you.
237 def dot(name):
238 if not name: return name
239 if name[0:1] == b'.': return b'_'+name[1:]
240 return name
242 if not auto_sanitize:
243 return mapping.get(name,name)
244 n=mapping.get(name,name)
245 p=re.compile(b'([\\[ ~^:?\\\\*]|\.\.)')
246 n=p.sub(b'_', n)
247 if n[-1:] in (b'/', b'.'): n=n[:-1]+b'_'
248 n=b'/'.join([dot(s) for s in n.split(b'/')])
249 p=re.compile(b'_+')
250 n=p.sub(b'_', n)
252 if n!=name:
253 stderr_buffer.write(
254 b'Warning: sanitized %s [%s] to [%s]\n' % (what.encode(), name, n)
256 return n
258 def strip_leading_slash(filename):
259 if filename[0:1] == b'/':
260 return filename[1:]
261 return filename
263 def export_commit(ui,repo,revision,old_marks,max,count,authors,
264 branchesmap,sob,brmap,hgtags,encoding='',fn_encoding='',
265 plugins={}):
266 def get_branchname(name):
267 if name in brmap:
268 return brmap[name]
269 n=sanitize_name(name, "branch", branchesmap)
270 brmap[name]=n
271 return n
273 ctx=repo[revision]
275 if ctx.hidden():
276 return count
278 (_,user,(time,timezone),files,desc,branch,extra)=get_changeset(ui,repo,revision,authors,encoding)
280 branch=get_branchname(branch)
282 parents = [p for p in repo.changelog.parentrevs(revision) if p >= 0]
283 author = get_author(desc,user,authors)
284 hg_hash=ctx.hex()
286 if plugins and plugins['commit_message_filters']:
287 commit_data = {'branch': branch, 'parents': parents,
288 'author': author, 'desc': desc,
289 'revision': revision, 'hg_hash': hg_hash,
290 'committer': user, 'extra': extra}
291 for filter in plugins['commit_message_filters']:
292 filter(commit_data)
293 branch = commit_data['branch']
294 parents = commit_data['parents']
295 author = commit_data['author']
296 user = commit_data['committer']
297 desc = commit_data['desc'] + b'\n'
299 if len(parents)==0 and revision != 0:
300 wr(b'reset refs/heads/%s' % branch)
302 wr(b'commit refs/heads/%s' % branch)
303 wr(b'mark :%d' % (revision+1))
304 if sob:
305 wr(b'author %s %d %s' % (author,time,timezone))
306 wr(b'committer %s %d %s' % (user,time,timezone))
307 wr_data(desc)
309 man=ctx.manifest()
311 if not parents:
312 type='full'
313 else:
314 wr(b'from %s' % revnum_to_revref(parents[0], old_marks))
315 if len(parents) == 1:
316 type='simple delta'
317 else: # a merge with two parents
318 wr(b'merge %s' % revnum_to_revref(parents[1], old_marks))
319 type='thorough delta'
321 modified,removed=get_filechanges(repo,revision,parents,files)
323 stderr_buffer.write(
324 b'%s: Exporting %s revision %d/%d with %d/%d modified/removed files\n'
325 % (branch, type.encode(), revision + 1, max, len(modified), len(removed))
328 for filename in removed:
329 if fn_encoding:
330 filename=filename.decode(fn_encoding).encode('utf8')
331 filename=strip_leading_slash(filename)
332 if filename==b'.hgsub':
333 remove_gitmodules(ctx)
334 wr(b'D %s' % filename)
336 export_file_contents(ctx,man,modified,hgtags,fn_encoding,plugins)
337 wr()
339 return checkpoint(count)
341 def export_note(ui,repo,revision,count,authors,encoding,is_first):
342 ctx = repo[revision]
344 if ctx.hidden():
345 return count
347 (_,user,(time,timezone),_,_,_,_)=get_changeset(ui,repo,revision,authors,encoding)
349 wr(b'commit refs/notes/hg')
350 wr(b'committer %s %d %s' % (user,time,timezone))
351 wr(b'data 0')
352 if is_first:
353 wr(b'from refs/notes/hg^0')
354 wr(b'N inline :%d' % (revision+1))
355 hg_hash=ctx.hex()
356 wr_data(hg_hash)
357 wr()
358 return checkpoint(count)
360 def export_tags(ui,repo,old_marks,mapping_cache,count,authors,tagsmap):
361 l=repo.tagslist()
362 for tag,node in l:
363 # Remap the branch name
364 tag=sanitize_name(tag,"tag",tagsmap)
365 # ignore latest revision
366 if tag==b'tip': continue
367 # ignore tags to nodes that are missing (ie, 'in the future')
368 if hexlify(node) not in mapping_cache:
369 stderr_buffer.write(b'Tag %s refers to unseen node %s\n' % (tag, hexlify(node)))
370 continue
372 rev=int(mapping_cache[hexlify(node)])
374 ref=revnum_to_revref(rev, old_marks)
375 if ref==None:
376 stderr_buffer.write(
377 b'Failed to find reference for creating tag %s at r%d\n' % (tag, rev)
379 continue
380 stderr_buffer.write(b'Exporting tag [%s] at [hg r%d] [git %s]\n' % (tag, rev, ref))
381 wr(b'reset refs/tags/%s' % tag)
382 wr(b'from %s' % ref)
383 wr()
384 count=checkpoint(count)
385 return count
387 def load_mapping(name, filename, mapping_is_raw):
388 raw_regexp=re.compile(b'^([^=]+)[ ]*=[ ]*(.+)$')
389 string_regexp=b'"(((\\.)|(\\")|[^"])*)"'
390 quoted_regexp=re.compile(b'^'+string_regexp+b'[ ]*=[ ]*'+string_regexp+b'$')
392 def parse_raw_line(line):
393 m=raw_regexp.match(line)
394 if m==None:
395 return None
396 return (m.group(1).strip(), m.group(2).strip())
398 def process_unicode_escape_sequences(s):
399 # Replace unicode escape sequences in the otherwise UTF8-encoded bytestring s with
400 # the UTF8-encoded characters they represent. We need to do an additional
401 # .decode('utf8').encode('ascii', 'backslashreplace') to convert any non-ascii
402 # characters into their escape sequences so that the subsequent
403 # .decode('unicode-escape') succeeds:
404 return (
405 s.decode('utf8')
406 .encode('ascii', 'backslashreplace')
407 .decode('unicode-escape')
408 .encode('utf8')
411 def parse_quoted_line(line):
412 m=quoted_regexp.match(line)
413 if m==None:
414 return
416 return (process_unicode_escape_sequences(m.group(1)),
417 process_unicode_escape_sequences(m.group(5)))
419 cache={}
420 if not os.path.exists(filename):
421 sys.stderr.write('Could not open mapping file [%s]\n' % (filename))
422 return cache
423 f=open(filename,'rb')
426 for line in f.readlines():
427 l+=1
428 line=line.strip()
429 if l==1 and line[0:1]==b'#' and line==b'# quoted-escaped-strings':
430 continue
431 elif line==b'' or line[0:1]==b'#':
432 continue
433 m=parse_raw_line(line) if mapping_is_raw else parse_quoted_line(line)
434 if m==None:
435 sys.stderr.write('Invalid file format in [%s], line %d\n' % (filename,l))
436 continue
437 # put key:value in cache, key without ^:
438 cache[m[0]]=m[1]
439 a+=1
440 f.close()
441 sys.stderr.write('Loaded %d %s\n' % (a, name))
442 return cache
444 def branchtip(repo, heads):
445 '''return the tipmost branch head in heads'''
446 tip = heads[-1]
447 for h in reversed(heads):
448 if 'close' not in repo.changelog.read(h)[5]:
449 tip = h
450 break
451 return tip
453 def verify_heads(ui,repo,cache,force,ignore_unnamed_heads,branchesmap):
454 branches={}
455 for bn, heads in repo.branchmap().iteritems():
456 branches[bn] = branchtip(repo, heads)
457 l=[(-repo.changelog.rev(n), n, t) for t, n in branches.items()]
458 l.sort()
460 # get list of hg's branches to verify, don't take all git has
461 for _,_,b in l:
462 b=get_branch(b)
463 sanitized_name=sanitize_name(b,"branch",branchesmap)
464 sha1=get_git_sha1(sanitized_name)
465 c=cache.get(sanitized_name)
466 if not c and sha1:
467 stderr_buffer.write(
468 b'Error: Branch [%s] already exists and was not created by hg-fast-export, '
469 b'export would overwrite unrelated branch\n' % b)
470 if not force: return False
471 elif sha1!=c:
472 stderr_buffer.write(
473 b'Error: Branch [%s] modified outside hg-fast-export:'
474 b'\n%s (repo) != %s (cache)\n' % (b, b'<None>' if sha1 is None else sha1, c)
476 if not force: return False
478 # verify that branch has exactly one head
479 t={}
480 unnamed_heads=False
481 for h in repo.filtered(b'visible').heads():
482 branch=get_branch(repo[h].branch())
483 if t.get(branch,False):
484 stderr_buffer.write(
485 b'Error: repository has an unnamed head: hg r%d\n'
486 % repo.changelog.rev(h)
488 unnamed_heads=True
489 if not force and not ignore_unnamed_heads: return False
490 t[branch]=True
491 if unnamed_heads and not force and not ignore_unnamed_heads: return False
492 return True
494 def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile,
495 authors={},branchesmap={},tagsmap={},
496 sob=False,force=False,ignore_unnamed_heads=False,hgtags=False,notes=False,encoding='',fn_encoding='',
497 plugins={}):
498 def check_cache(filename, contents):
499 if len(contents) == 0:
500 sys.stderr.write('Warning: %s does not contain any data, this will probably make an incremental import fail\n' % filename)
502 _max=int(m)
504 old_marks=load_cache(marksfile,lambda s: int(s)-1)
505 mapping_cache=load_cache(mappingfile)
506 heads_cache=load_cache(headsfile)
507 state_cache=load_cache(tipfile)
509 if len(state_cache) != 0:
510 for (name, data) in [(marksfile, old_marks),
511 (mappingfile, mapping_cache),
512 (headsfile, state_cache)]:
513 check_cache(name, data)
515 ui,repo=setup_repo(repourl)
517 if not verify_heads(ui,repo,heads_cache,force,ignore_unnamed_heads,branchesmap):
518 return 1
520 try:
521 tip=repo.changelog.count()
522 except AttributeError:
523 tip=len(repo)
525 min=int(state_cache.get(b'tip',0))
526 max=_max
527 if _max<0 or max>tip:
528 max=tip
530 for rev in range(0,max):
531 ctx=repo[rev]
532 if ctx.hidden():
533 continue
534 mapping_cache[ctx.hex()] = b"%d" % rev
536 if submodule_mappings:
537 # Make sure that all mercurial submodules are registered in the submodule-mappings file
538 for rev in range(0,max):
539 ctx=repo[rev]
540 if ctx.hidden():
541 continue
542 if ctx.substate:
543 for key in ctx.substate:
544 if ctx.substate[key][2]=='hg' and key not in submodule_mappings:
545 sys.stderr.write("Error: %s not found in submodule-mappings\n" % (key))
546 return 1
549 brmap={}
550 for rev in range(min,max):
551 c=export_commit(ui,repo,rev,old_marks,max,c,authors,branchesmap,
552 sob,brmap,hgtags,encoding,fn_encoding,
553 plugins)
554 if notes:
555 for rev in range(min,max):
556 c=export_note(ui,repo,rev,c,authors, encoding, rev == min and min != 0)
558 state_cache[b'tip']=max
559 state_cache[b'repo']=repourl
560 save_cache(tipfile,state_cache)
561 save_cache(mappingfile,mapping_cache)
563 c=export_tags(ui,repo,old_marks,mapping_cache,c,authors,tagsmap)
565 sys.stderr.write('Issued %d commands\n' % c)
567 return 0
569 if __name__=='__main__':
570 def bail(parser,opt):
571 sys.stderr.write('Error: No %s option given\n' % opt)
572 parser.print_help()
573 sys.exit(2)
575 parser=OptionParser()
577 parser.add_option("-n", "--no-auto-sanitize",action="store_false",
578 dest="auto_sanitize",default=True,
579 help="Do not perform built-in (broken in many cases) sanitizing of names")
580 parser.add_option("-m","--max",type="int",dest="max",
581 help="Maximum hg revision to import")
582 parser.add_option("--mapping",dest="mappingfile",
583 help="File to read last run's hg-to-git SHA1 mapping")
584 parser.add_option("--marks",dest="marksfile",
585 help="File to read git-fast-import's marks from")
586 parser.add_option("--heads",dest="headsfile",
587 help="File to read last run's git heads from")
588 parser.add_option("--status",dest="statusfile",
589 help="File to read status from")
590 parser.add_option("-r","--repo",dest="repourl",
591 help="URL of repo to import")
592 parser.add_option("-s",action="store_true",dest="sob",
593 default=False,help="Enable parsing Signed-off-by lines")
594 parser.add_option("--hgtags",action="store_true",dest="hgtags",
595 default=False,help="Enable exporting .hgtags files")
596 parser.add_option("-A","--authors",dest="authorfile",
597 help="Read authormap from AUTHORFILE")
598 parser.add_option("-B","--branches",dest="branchesfile",
599 help="Read branch map from BRANCHESFILE")
600 parser.add_option("-T","--tags",dest="tagsfile",
601 help="Read tags map from TAGSFILE")
602 parser.add_option("-f","--force",action="store_true",dest="force",
603 default=False,help="Ignore validation errors by force, implies --ignore-unnamed-heads")
604 parser.add_option("--ignore-unnamed-heads",action="store_true",dest="ignore_unnamed_heads",
605 default=False,help="Ignore unnamed head errors")
606 parser.add_option("-M","--default-branch",dest="default_branch",
607 help="Set the default branch")
608 parser.add_option("-o","--origin",dest="origin_name",
609 help="use <name> as namespace to track upstream")
610 parser.add_option("--hg-hash",action="store_true",dest="notes",
611 default=False,help="Annotate commits with the hg hash as git notes in the hg namespace")
612 parser.add_option("-e",dest="encoding",
613 help="Assume commit and author strings retrieved from Mercurial are encoded in <encoding>")
614 parser.add_option("--fe",dest="fn_encoding",
615 help="Assume file names from Mercurial are encoded in <filename_encoding>")
616 parser.add_option("--mappings-are-raw",dest="raw_mappings", default=False,
617 help="Assume mappings are raw <key>=<value> lines")
618 parser.add_option("--filter-contents",dest="filter_contents",
619 help="Pipe contents of each exported file through FILTER_CONTENTS <file-path> <hg-hash> <is-binary>")
620 parser.add_option("--plugin-path", type="string", dest="pluginpath",
621 help="Additional search path for plugins ")
622 parser.add_option("--plugin", action="append", type="string", dest="plugins",
623 help="Add a plugin with the given init string <name=init>")
624 parser.add_option("--subrepo-map", type="string", dest="subrepo_map",
625 help="Provide a mapping file between the subrepository name and the submodule name")
627 (options,args)=parser.parse_args()
629 m=-1
630 auto_sanitize = options.auto_sanitize
631 if options.max!=None: m=options.max
633 if options.marksfile==None: bail(parser,'--marks')
634 if options.mappingfile==None: bail(parser,'--mapping')
635 if options.headsfile==None: bail(parser,'--heads')
636 if options.statusfile==None: bail(parser,'--status')
637 if options.repourl==None: bail(parser,'--repo')
639 if options.subrepo_map:
640 if not os.path.exists(options.subrepo_map):
641 sys.stderr.write('Subrepo mapping file not found %s\n'
642 % options.subrepo_map)
643 sys.exit(1)
644 submodule_mappings=load_mapping('subrepo mappings',
645 options.subrepo_map,False)
647 a={}
648 if options.authorfile!=None:
649 a=load_mapping('authors', options.authorfile, options.raw_mappings)
651 b={}
652 if options.branchesfile!=None:
653 b=load_mapping('branches', options.branchesfile, options.raw_mappings)
655 t={}
656 if options.tagsfile!=None:
657 t=load_mapping('tags', options.tagsfile, options.raw_mappings)
659 if options.default_branch!=None:
660 set_default_branch(options.default_branch)
662 if options.origin_name!=None:
663 set_origin_name(options.origin_name)
665 encoding=''
666 if options.encoding!=None:
667 encoding=options.encoding
669 fn_encoding=encoding
670 if options.fn_encoding!=None:
671 fn_encoding=options.fn_encoding
673 plugins=[]
674 if options.plugins!=None:
675 plugins+=options.plugins
677 if options.filter_contents!=None:
678 plugins+=['shell_filter_file_contents='+options.filter_contents]
680 plugins_dict={}
681 plugins_dict['commit_message_filters']=[]
682 plugins_dict['file_data_filters']=[]
684 if plugins and options.pluginpath:
685 sys.stderr.write('Using additional plugin path: ' + options.pluginpath + '\n')
687 for plugin in plugins:
688 split = plugin.split('=')
689 name, opts = split[0], '='.join(split[1:])
690 i = pluginloader.get_plugin(name,options.pluginpath)
691 sys.stderr.write('Loaded plugin ' + i['name'] + ' from path: ' + i['path'] +' with opts: ' + opts + '\n')
692 plugin = pluginloader.load_plugin(i).build_filter(opts)
693 if hasattr(plugin,'file_data_filter') and callable(plugin.file_data_filter):
694 plugins_dict['file_data_filters'].append(plugin.file_data_filter)
695 if hasattr(plugin, 'commit_message_filter') and callable(plugin.commit_message_filter):
696 plugins_dict['commit_message_filters'].append(plugin.commit_message_filter)
698 sys.exit(hg2git(options.repourl,m,options.marksfile,options.mappingfile,
699 options.headsfile, options.statusfile,
700 authors=a,branchesmap=b,tagsmap=t,
701 sob=options.sob,force=options.force,
702 ignore_unnamed_heads=options.ignore_unnamed_heads,
703 hgtags=options.hgtags,
704 notes=options.notes,encoding=encoding,fn_encoding=fn_encoding,
705 plugins=plugins_dict))