Remove multiple parent logic for file changes
[fast-export.git] / hg-fast-export.py
blobb33cc4bb7d4f94f1d110393ed37d5979e5d522de
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 modified=files
77 removed=[]
78 else:
79 # take the changes from the first parent
80 f=repo.status(parents[0],revision)
81 modified=f.modified + f.added
82 removed=f.removed
83 return modified,removed
85 def get_author(logmessage,committer,authors):
86 """As git distincts between author and committer of a patch, try to
87 extract author by detecting Signed-off-by lines.
89 This walks from the end of the log message towards the top skipping
90 empty lines. Upon the first non-empty line, it walks all Signed-off-by
91 lines upwards to find the first one. For that (if found), it extracts
92 authorship information the usual way (authors table, cleaning, etc.)
94 If no Signed-off-by line is found, this defaults to the committer.
96 This may sound stupid (and it somehow is), but in log messages we
97 accidentially may have lines in the middle starting with
98 "Signed-off-by: foo" and thus matching our detection regex. Prevent
99 that."""
101 loglines=logmessage.split(b'\n')
102 i=len(loglines)
103 # from tail walk to top skipping empty lines
104 while i>=0:
105 i-=1
106 if len(loglines[i].strip())==0: continue
107 break
108 if i>=0:
109 # walk further upwards to find first sob line, store in 'first'
110 first=None
111 while i>=0:
112 m=sob_re.match(loglines[i])
113 if m==None: break
114 first=m
115 i-=1
116 # if the last non-empty line matches our Signed-Off-by regex: extract username
117 if first!=None:
118 r=fixup_user(first.group(1),authors)
119 return r
120 return committer
122 def remove_gitmodules(ctx):
123 """Removes all submodules of ctx parents"""
124 # Removing all submoduies coming from all parents is safe, as the submodules
125 # of the current commit will be re-added below. A possible optimization would
126 # be to only remove the submodules of the first parent.
127 for parent_ctx in ctx.parents():
128 for submodule in parent_ctx.substate.keys():
129 wr(b'D %s' % submodule)
130 wr(b'D .gitmodules')
132 def refresh_git_submodule(name,subrepo_info):
133 wr(b'M 160000 %s %s' % (subrepo_info[1],name))
134 stderr_buffer.write(
135 b"Adding/updating submodule %s, revision %s\n" % (name, subrepo_info[1])
137 return b'[submodule "%s"]\n\tpath = %s\n\turl = %s\n' % (name, name, subrepo_info[0])
139 def refresh_hg_submodule(name,subrepo_info):
140 gitRepoLocation=submodule_mappings[name] + b"/.git"
142 # Populate the cache to map mercurial revision to git revision
143 if not name in subrepo_cache:
144 subrepo_cache[name]=(load_cache(gitRepoLocation+b"/hg2git-mapping"),
145 load_cache(gitRepoLocation+b"/hg2git-marks",
146 lambda s: int(s)-1))
148 (mapping_cache,marks_cache)=subrepo_cache[name]
149 subrepo_hash=subrepo_info[1]
150 if subrepo_hash in mapping_cache:
151 revnum=mapping_cache[subrepo_hash]
152 gitSha=marks_cache[int(revnum)]
153 wr(b'M 160000 %s %s' % (gitSha,name))
154 stderr_buffer.write(
155 b"Adding/updating submodule %s, revision %s->%s\n"
156 % (name, subrepo_hash, gitSha)
158 return b'[submodule "%s"]\n\tpath = %s\n\turl = %s\n' % (name,name,
159 submodule_mappings[name])
160 else:
161 stderr_buffer.write(
162 b"Warning: Could not find hg revision %s for %s in git %s\n"
163 % (subrepo_hash, name, gitRepoLocation,)
165 return b''
167 def refresh_gitmodules(ctx):
168 """Updates list of ctx submodules according to .hgsubstate file"""
169 remove_gitmodules(ctx)
170 gitmodules=b""
171 # Create the .gitmodules file and all submodules
172 for name,subrepo_info in ctx.substate.items():
173 if subrepo_info[2]==b'git':
174 gitmodules+=refresh_git_submodule(name,subrepo_info)
175 elif submodule_mappings and name in submodule_mappings:
176 gitmodules+=refresh_hg_submodule(name,subrepo_info)
178 if len(gitmodules):
179 wr(b'M 100644 inline .gitmodules')
180 wr_data(gitmodules)
182 def export_file_contents(ctx,manifest,files,hgtags,encoding='',plugins={}):
183 count=0
184 max=len(files)
185 is_submodules_refreshed=False
186 for file in files:
187 if not is_submodules_refreshed and (file==b'.hgsub' or file==b'.hgsubstate'):
188 is_submodules_refreshed=True
189 refresh_gitmodules(ctx)
190 # Skip .hgtags files. They only get us in trouble.
191 if not hgtags and file == b".hgtags":
192 stderr_buffer.write(b'Skip %s\n' % file)
193 continue
194 if encoding:
195 filename=file.decode(encoding).encode('utf8')
196 else:
197 filename=file
198 if b'.git' in filename.split(b'/'): # Even on Windows, the path separator is / here.
199 stderr_buffer.write(
200 b'Ignoring file %s which cannot be tracked by git\n' % filename
202 continue
203 file_ctx=ctx.filectx(file)
204 d=file_ctx.data()
206 if plugins and plugins['file_data_filters']:
207 file_data = {'filename':filename,'file_ctx':file_ctx,'data':d}
208 for filter in plugins['file_data_filters']:
209 filter(file_data)
210 d=file_data['data']
211 filename=file_data['filename']
212 file_ctx=file_data['file_ctx']
214 wr(b'M %s inline %s' % (gitmode(manifest.flags(file)),
215 strip_leading_slash(filename)))
216 wr(b'data %d' % len(d)) # had some trouble with size()
217 wr(d)
218 count+=1
219 if count%cfg_export_boundary==0:
220 stderr_buffer.write(b'Exported %d/%d files\n' % (count,max))
221 if max>cfg_export_boundary:
222 stderr_buffer.write(b'Exported %d/%d files\n' % (count,max))
224 def sanitize_name(name,what="branch", mapping={}):
225 """Sanitize input roughly according to git-check-ref-format(1)"""
227 # NOTE: Do not update this transform to work around
228 # incompatibilities on your platform. If you change it and it starts
229 # modifying names which previously were not touched it will break
230 # preexisting setups which are doing incremental imports.
232 # Fast-export tries to not inflict arbitrary naming policy on the
233 # user, instead it aims to provide mechanisms allowing the user to
234 # apply their own policy. Therefore do not add a transform which can
235 # already be implemented with the -B and -T options to mangle branch
236 # and tag names. If you have a source repository where this is too
237 # much work to do manually, write a tool that does it for you.
240 def dot(name):
241 if not name: return name
242 if name[0:1] == b'.': return b'_'+name[1:]
243 return name
245 if not auto_sanitize:
246 return mapping.get(name,name)
247 n=mapping.get(name,name)
248 p=re.compile(b'([\\[ ~^:?\\\\*]|\.\.)')
249 n=p.sub(b'_', n)
250 if n[-1:] in (b'/', b'.'): n=n[:-1]+b'_'
251 n=b'/'.join([dot(s) for s in n.split(b'/')])
252 p=re.compile(b'_+')
253 n=p.sub(b'_', n)
255 if n!=name:
256 stderr_buffer.write(
257 b'Warning: sanitized %s [%s] to [%s]\n' % (what.encode(), name, n)
259 return n
261 def strip_leading_slash(filename):
262 if filename[0:1] == b'/':
263 return filename[1:]
264 return filename
266 def export_commit(ui,repo,revision,old_marks,max,count,authors,
267 branchesmap,sob,brmap,hgtags,encoding='',fn_encoding='',
268 plugins={}):
269 def get_branchname(name):
270 if name in brmap:
271 return brmap[name]
272 n=sanitize_name(name, "branch", branchesmap)
273 brmap[name]=n
274 return n
276 ctx=repo[revision]
278 if ctx.hidden():
279 return count
281 (_,user,(time,timezone),files,desc,branch,extra)=get_changeset(ui,repo,revision,authors,encoding)
283 branch=get_branchname(branch)
285 parents = [p for p in repo.changelog.parentrevs(revision) if p >= 0]
286 author = get_author(desc,user,authors)
287 hg_hash=ctx.hex()
289 if plugins and plugins['commit_message_filters']:
290 commit_data = {'branch': branch, 'parents': parents,
291 'author': author, 'desc': desc,
292 'revision': revision, 'hg_hash': hg_hash,
293 'committer': user, 'extra': extra}
294 for filter in plugins['commit_message_filters']:
295 filter(commit_data)
296 branch = commit_data['branch']
297 parents = commit_data['parents']
298 author = commit_data['author']
299 user = commit_data['committer']
300 desc = commit_data['desc'] + b'\n'
302 if len(parents)==0 and revision != 0:
303 wr(b'reset refs/heads/%s' % branch)
305 wr(b'commit refs/heads/%s' % branch)
306 wr(b'mark :%d' % (revision+1))
307 if sob:
308 wr(b'author %s %d %s' % (author,time,timezone))
309 wr(b'committer %s %d %s' % (user,time,timezone))
310 wr_data(desc)
312 man=ctx.manifest()
314 if not parents:
315 type='full'
316 else:
317 wr(b'from %s' % revnum_to_revref(parents[0], old_marks))
318 if len(parents) == 1:
319 type='simple delta'
320 else: # a merge with two parents
321 wr(b'merge %s' % revnum_to_revref(parents[1], old_marks))
322 type='thorough delta'
324 modified,removed=get_filechanges(repo,revision,parents,files)
326 stderr_buffer.write(
327 b'%s: Exporting %s revision %d/%d with %d/%d modified/removed files\n'
328 % (branch, type.encode(), revision + 1, max, len(modified), len(removed))
331 for filename in removed:
332 if fn_encoding:
333 filename=filename.decode(fn_encoding).encode('utf8')
334 filename=strip_leading_slash(filename)
335 if filename==b'.hgsub':
336 remove_gitmodules(ctx)
337 wr(b'D %s' % filename)
339 export_file_contents(ctx,man,modified,hgtags,fn_encoding,plugins)
340 wr()
342 return checkpoint(count)
344 def export_note(ui,repo,revision,count,authors,encoding,is_first):
345 ctx = repo[revision]
347 if ctx.hidden():
348 return count
350 (_,user,(time,timezone),_,_,_,_)=get_changeset(ui,repo,revision,authors,encoding)
352 wr(b'commit refs/notes/hg')
353 wr(b'committer %s %d %s' % (user,time,timezone))
354 wr(b'data 0')
355 if is_first:
356 wr(b'from refs/notes/hg^0')
357 wr(b'N inline :%d' % (revision+1))
358 hg_hash=ctx.hex()
359 wr_data(hg_hash)
360 wr()
361 return checkpoint(count)
363 def export_tags(ui,repo,old_marks,mapping_cache,count,authors,tagsmap):
364 l=repo.tagslist()
365 for tag,node in l:
366 # Remap the branch name
367 tag=sanitize_name(tag,"tag",tagsmap)
368 # ignore latest revision
369 if tag==b'tip': continue
370 # ignore tags to nodes that are missing (ie, 'in the future')
371 if hexlify(node) not in mapping_cache:
372 stderr_buffer.write(b'Tag %s refers to unseen node %s\n' % (tag, hexlify(node)))
373 continue
375 rev=int(mapping_cache[hexlify(node)])
377 ref=revnum_to_revref(rev, old_marks)
378 if ref==None:
379 stderr_buffer.write(
380 b'Failed to find reference for creating tag %s at r%d\n' % (tag, rev)
382 continue
383 stderr_buffer.write(b'Exporting tag [%s] at [hg r%d] [git %s]\n' % (tag, rev, ref))
384 wr(b'reset refs/tags/%s' % tag)
385 wr(b'from %s' % ref)
386 wr()
387 count=checkpoint(count)
388 return count
390 def load_mapping(name, filename, mapping_is_raw):
391 raw_regexp=re.compile(b'^([^=]+)[ ]*=[ ]*(.+)$')
392 string_regexp=b'"(((\\.)|(\\")|[^"])*)"'
393 quoted_regexp=re.compile(b'^'+string_regexp+b'[ ]*=[ ]*'+string_regexp+b'$')
395 def parse_raw_line(line):
396 m=raw_regexp.match(line)
397 if m==None:
398 return None
399 return (m.group(1).strip(), m.group(2).strip())
401 def process_unicode_escape_sequences(s):
402 # Replace unicode escape sequences in the otherwise UTF8-encoded bytestring s with
403 # the UTF8-encoded characters they represent. We need to do an additional
404 # .decode('utf8').encode('ascii', 'backslashreplace') to convert any non-ascii
405 # characters into their escape sequences so that the subsequent
406 # .decode('unicode-escape') succeeds:
407 return (
408 s.decode('utf8')
409 .encode('ascii', 'backslashreplace')
410 .decode('unicode-escape')
411 .encode('utf8')
414 def parse_quoted_line(line):
415 m=quoted_regexp.match(line)
416 if m==None:
417 return
419 return (process_unicode_escape_sequences(m.group(1)),
420 process_unicode_escape_sequences(m.group(5)))
422 cache={}
423 if not os.path.exists(filename):
424 sys.stderr.write('Could not open mapping file [%s]\n' % (filename))
425 return cache
426 f=open(filename,'rb')
429 for line in f.readlines():
430 l+=1
431 line=line.strip()
432 if l==1 and line[0:1]==b'#' and line==b'# quoted-escaped-strings':
433 continue
434 elif line==b'' or line[0:1]==b'#':
435 continue
436 m=parse_raw_line(line) if mapping_is_raw else parse_quoted_line(line)
437 if m==None:
438 sys.stderr.write('Invalid file format in [%s], line %d\n' % (filename,l))
439 continue
440 # put key:value in cache, key without ^:
441 cache[m[0]]=m[1]
442 a+=1
443 f.close()
444 sys.stderr.write('Loaded %d %s\n' % (a, name))
445 return cache
447 def branchtip(repo, heads):
448 '''return the tipmost branch head in heads'''
449 tip = heads[-1]
450 for h in reversed(heads):
451 if 'close' not in repo.changelog.read(h)[5]:
452 tip = h
453 break
454 return tip
456 def verify_heads(ui,repo,cache,force,ignore_unnamed_heads,branchesmap):
457 branches={}
458 for bn, heads in repo.branchmap().iteritems():
459 branches[bn] = branchtip(repo, heads)
460 l=[(-repo.changelog.rev(n), n, t) for t, n in branches.items()]
461 l.sort()
463 # get list of hg's branches to verify, don't take all git has
464 for _,_,b in l:
465 b=get_branch(b)
466 sanitized_name=sanitize_name(b,"branch",branchesmap)
467 sha1=get_git_sha1(sanitized_name)
468 c=cache.get(sanitized_name)
469 if not c and sha1:
470 stderr_buffer.write(
471 b'Error: Branch [%s] already exists and was not created by hg-fast-export, '
472 b'export would overwrite unrelated branch\n' % b)
473 if not force: return False
474 elif sha1!=c:
475 stderr_buffer.write(
476 b'Error: Branch [%s] modified outside hg-fast-export:'
477 b'\n%s (repo) != %s (cache)\n' % (b, b'<None>' if sha1 is None else sha1, c)
479 if not force: return False
481 # verify that branch has exactly one head
482 t={}
483 unnamed_heads=False
484 for h in repo.filtered(b'visible').heads():
485 branch=get_branch(repo[h].branch())
486 if t.get(branch,False):
487 stderr_buffer.write(
488 b'Error: repository has an unnamed head: hg r%d\n'
489 % repo.changelog.rev(h)
491 unnamed_heads=True
492 if not force and not ignore_unnamed_heads: return False
493 t[branch]=True
494 if unnamed_heads and not force and not ignore_unnamed_heads: return False
495 return True
497 def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile,
498 authors={},branchesmap={},tagsmap={},
499 sob=False,force=False,ignore_unnamed_heads=False,hgtags=False,notes=False,encoding='',fn_encoding='',
500 plugins={}):
501 def check_cache(filename, contents):
502 if len(contents) == 0:
503 sys.stderr.write('Warning: %s does not contain any data, this will probably make an incremental import fail\n' % filename)
505 _max=int(m)
507 old_marks=load_cache(marksfile,lambda s: int(s)-1)
508 mapping_cache=load_cache(mappingfile)
509 heads_cache=load_cache(headsfile)
510 state_cache=load_cache(tipfile)
512 if len(state_cache) != 0:
513 for (name, data) in [(marksfile, old_marks),
514 (mappingfile, mapping_cache),
515 (headsfile, state_cache)]:
516 check_cache(name, data)
518 ui,repo=setup_repo(repourl)
520 if not verify_heads(ui,repo,heads_cache,force,ignore_unnamed_heads,branchesmap):
521 return 1
523 try:
524 tip=repo.changelog.count()
525 except AttributeError:
526 tip=len(repo)
528 min=int(state_cache.get(b'tip',0))
529 max=_max
530 if _max<0 or max>tip:
531 max=tip
533 for rev in range(0,max):
534 ctx=repo[rev]
535 if ctx.hidden():
536 continue
537 mapping_cache[ctx.hex()] = b"%d" % rev
539 if submodule_mappings:
540 # Make sure that all mercurial submodules are registered in the submodule-mappings file
541 for rev in range(0,max):
542 ctx=repo[rev]
543 if ctx.hidden():
544 continue
545 if ctx.substate:
546 for key in ctx.substate:
547 if ctx.substate[key][2]=='hg' and key not in submodule_mappings:
548 sys.stderr.write("Error: %s not found in submodule-mappings\n" % (key))
549 return 1
552 brmap={}
553 for rev in range(min,max):
554 c=export_commit(ui,repo,rev,old_marks,max,c,authors,branchesmap,
555 sob,brmap,hgtags,encoding,fn_encoding,
556 plugins)
557 if notes:
558 for rev in range(min,max):
559 c=export_note(ui,repo,rev,c,authors, encoding, rev == min and min != 0)
561 state_cache[b'tip']=max
562 state_cache[b'repo']=repourl
563 save_cache(tipfile,state_cache)
564 save_cache(mappingfile,mapping_cache)
566 c=export_tags(ui,repo,old_marks,mapping_cache,c,authors,tagsmap)
568 sys.stderr.write('Issued %d commands\n' % c)
570 return 0
572 if __name__=='__main__':
573 def bail(parser,opt):
574 sys.stderr.write('Error: No %s option given\n' % opt)
575 parser.print_help()
576 sys.exit(2)
578 parser=OptionParser()
580 parser.add_option("-n", "--no-auto-sanitize",action="store_false",
581 dest="auto_sanitize",default=True,
582 help="Do not perform built-in (broken in many cases) sanitizing of names")
583 parser.add_option("-m","--max",type="int",dest="max",
584 help="Maximum hg revision to import")
585 parser.add_option("--mapping",dest="mappingfile",
586 help="File to read last run's hg-to-git SHA1 mapping")
587 parser.add_option("--marks",dest="marksfile",
588 help="File to read git-fast-import's marks from")
589 parser.add_option("--heads",dest="headsfile",
590 help="File to read last run's git heads from")
591 parser.add_option("--status",dest="statusfile",
592 help="File to read status from")
593 parser.add_option("-r","--repo",dest="repourl",
594 help="URL of repo to import")
595 parser.add_option("-s",action="store_true",dest="sob",
596 default=False,help="Enable parsing Signed-off-by lines")
597 parser.add_option("--hgtags",action="store_true",dest="hgtags",
598 default=False,help="Enable exporting .hgtags files")
599 parser.add_option("-A","--authors",dest="authorfile",
600 help="Read authormap from AUTHORFILE")
601 parser.add_option("-B","--branches",dest="branchesfile",
602 help="Read branch map from BRANCHESFILE")
603 parser.add_option("-T","--tags",dest="tagsfile",
604 help="Read tags map from TAGSFILE")
605 parser.add_option("-f","--force",action="store_true",dest="force",
606 default=False,help="Ignore validation errors by force, implies --ignore-unnamed-heads")
607 parser.add_option("--ignore-unnamed-heads",action="store_true",dest="ignore_unnamed_heads",
608 default=False,help="Ignore unnamed head errors")
609 parser.add_option("-M","--default-branch",dest="default_branch",
610 help="Set the default branch")
611 parser.add_option("-o","--origin",dest="origin_name",
612 help="use <name> as namespace to track upstream")
613 parser.add_option("--hg-hash",action="store_true",dest="notes",
614 default=False,help="Annotate commits with the hg hash as git notes in the hg namespace")
615 parser.add_option("-e",dest="encoding",
616 help="Assume commit and author strings retrieved from Mercurial are encoded in <encoding>")
617 parser.add_option("--fe",dest="fn_encoding",
618 help="Assume file names from Mercurial are encoded in <filename_encoding>")
619 parser.add_option("--mappings-are-raw",dest="raw_mappings", default=False,
620 help="Assume mappings are raw <key>=<value> lines")
621 parser.add_option("--filter-contents",dest="filter_contents",
622 help="Pipe contents of each exported file through FILTER_CONTENTS <file-path> <hg-hash> <is-binary>")
623 parser.add_option("--plugin-path", type="string", dest="pluginpath",
624 help="Additional search path for plugins ")
625 parser.add_option("--plugin", action="append", type="string", dest="plugins",
626 help="Add a plugin with the given init string <name=init>")
627 parser.add_option("--subrepo-map", type="string", dest="subrepo_map",
628 help="Provide a mapping file between the subrepository name and the submodule name")
630 (options,args)=parser.parse_args()
632 m=-1
633 auto_sanitize = options.auto_sanitize
634 if options.max!=None: m=options.max
636 if options.marksfile==None: bail(parser,'--marks')
637 if options.mappingfile==None: bail(parser,'--mapping')
638 if options.headsfile==None: bail(parser,'--heads')
639 if options.statusfile==None: bail(parser,'--status')
640 if options.repourl==None: bail(parser,'--repo')
642 if options.subrepo_map:
643 if not os.path.exists(options.subrepo_map):
644 sys.stderr.write('Subrepo mapping file not found %s\n'
645 % options.subrepo_map)
646 sys.exit(1)
647 submodule_mappings=load_mapping('subrepo mappings',
648 options.subrepo_map,False)
650 a={}
651 if options.authorfile!=None:
652 a=load_mapping('authors', options.authorfile, options.raw_mappings)
654 b={}
655 if options.branchesfile!=None:
656 b=load_mapping('branches', options.branchesfile, options.raw_mappings)
658 t={}
659 if options.tagsfile!=None:
660 t=load_mapping('tags', options.tagsfile, options.raw_mappings)
662 if options.default_branch!=None:
663 set_default_branch(options.default_branch)
665 if options.origin_name!=None:
666 set_origin_name(options.origin_name)
668 encoding=''
669 if options.encoding!=None:
670 encoding=options.encoding
672 fn_encoding=encoding
673 if options.fn_encoding!=None:
674 fn_encoding=options.fn_encoding
676 plugins=[]
677 if options.plugins!=None:
678 plugins+=options.plugins
680 if options.filter_contents!=None:
681 plugins+=['shell_filter_file_contents='+options.filter_contents]
683 plugins_dict={}
684 plugins_dict['commit_message_filters']=[]
685 plugins_dict['file_data_filters']=[]
687 if plugins and options.pluginpath:
688 sys.stderr.write('Using additional plugin path: ' + options.pluginpath + '\n')
690 for plugin in plugins:
691 split = plugin.split('=')
692 name, opts = split[0], '='.join(split[1:])
693 i = pluginloader.get_plugin(name,options.pluginpath)
694 sys.stderr.write('Loaded plugin ' + i['name'] + ' from path: ' + i['path'] +' with opts: ' + opts + '\n')
695 plugin = pluginloader.load_plugin(i).build_filter(opts)
696 if hasattr(plugin,'file_data_filter') and callable(plugin.file_data_filter):
697 plugins_dict['file_data_filters'].append(plugin.file_data_filter)
698 if hasattr(plugin, 'commit_message_filter') and callable(plugin.commit_message_filter):
699 plugins_dict['commit_message_filters'].append(plugin.commit_message_filter)
701 sys.exit(hg2git(options.repourl,m,options.marksfile,options.mappingfile,
702 options.headsfile, options.statusfile,
703 authors=a,branchesmap=b,tagsmap=t,
704 sob=options.sob,force=options.force,
705 ignore_unnamed_heads=options.ignore_unnamed_heads,
706 hgtags=options.hgtags,
707 notes=options.notes,encoding=encoding,fn_encoding=fn_encoding,
708 plugins=plugins_dict))