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
12 from binascii
import hexlify
14 PY2
= sys
.version_info
.major
== 2
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.
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
34 submodule_mappings
=None
36 # True if fast export should automatically try to sanitize
37 # author/branch/tag names.
40 stdout_buffer
= sys
.stdout
if PY2
else sys
.stdout
.buffer
41 stderr_buffer
= sys
.stderr
if PY2
else sys
.stderr
.buffer
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
)
49 stdout_buffer
.write(msg
)
53 #map(lambda x: sys.stderr.write('\t[%s]\n' % x),msg.split('\n'))
56 wr(b
'data %d' % (len(data
)))
59 def checkpoint(count
):
61 if cfg_checkpoint_count
>0 and count
%cfg_checkpoint
_count
==0:
62 stderr_buffer
.write(b
"Checkpoint after %d commits\n" % count
)
67 def revnum_to_revref(rev
, old_marks
):
68 """Convert an hg revnum to a git-fast-import rev reference (an SHA1
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."""
75 # first revision: feed in full manifest
79 # take the changes from the first parent
80 f
=repo
.status(parents
[0],revision
)
81 modified
=f
.modified
+ f
.added
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
101 loglines
=logmessage
.split(b
'\n')
103 # from tail walk to top skipping empty lines
106 if len(loglines
[i
].strip())==0: continue
109 # walk further upwards to find first sob line, store in 'first'
112 m
=sob_re
.match(loglines
[i
])
116 # if the last non-empty line matches our Signed-Off-by regex: extract username
118 r
=fixup_user(first
.group(1),authors
)
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
)
132 def refresh_git_submodule(name
,subrepo_info
):
133 wr(b
'M 160000 %s %s' % (subrepo_info
[1],name
))
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",
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
))
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
])
162 b
"Warning: Could not find hg revision %s for %s in git %s\n"
163 % (subrepo_hash
, name
, gitRepoLocation
,)
167 def refresh_gitmodules(ctx
):
168 """Updates list of ctx submodules according to .hgsubstate file"""
169 remove_gitmodules(ctx
)
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
)
179 wr(b
'M 100644 inline .gitmodules')
182 def export_file_contents(ctx
,manifest
,files
,hgtags
,encoding
='',plugins
={}):
185 is_submodules_refreshed
=False
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)
195 filename
=file.decode(encoding
).encode('utf8')
198 if b
'.git' in filename
.split(b
'/'): # Even on Windows, the path separator is / here.
200 b
'Ignoring file %s which cannot be tracked by git\n' % filename
203 file_ctx
=ctx
.filectx(file)
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']:
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()
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.
241 if not name
: return name
242 if name
[0:1] == b
'.': return b
'_'+name
[1:]
245 if not auto_sanitize
:
246 return mapping
.get(name
,name
)
247 n
=mapping
.get(name
,name
)
248 p
=re
.compile(b
'([\\[ ~^:?\\\\*]|\.\.)')
250 if n
[-1:] in (b
'/', b
'.'): n
=n
[:-1]+b
'_'
251 n
=b
'/'.join([dot(s
) for s
in n
.split(b
'/')])
257 b
'Warning: sanitized %s [%s] to [%s]\n' % (what
.encode(), name
, n
)
261 def strip_leading_slash(filename
):
262 if filename
[0:1] == b
'/':
266 def export_commit(ui
,repo
,revision
,old_marks
,max,count
,authors
,
267 branchesmap
,sob
,brmap
,hgtags
,encoding
='',fn_encoding
='',
269 def get_branchname(name
):
272 n
=sanitize_name(name
, "branch", branchesmap
)
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
)
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']:
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))
308 wr(b
'author %s %d %s' % (author
,time
,timezone
))
309 wr(b
'committer %s %d %s' % (user
,time
,timezone
))
317 wr(b
'from %s' % revnum_to_revref(parents
[0], old_marks
))
318 if len(parents
) == 1:
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
)
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
:
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
)
342 return checkpoint(count
)
344 def export_note(ui
,repo
,revision
,count
,authors
,encoding
,is_first
):
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
))
356 wr(b
'from refs/notes/hg^0')
357 wr(b
'N inline :%d' % (revision
+1))
361 return checkpoint(count
)
363 def export_tags(ui
,repo
,old_marks
,mapping_cache
,count
,authors
,tagsmap
):
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
)))
375 rev
=int(mapping_cache
[hexlify(node
)])
377 ref
=revnum_to_revref(rev
, old_marks
)
380 b
'Failed to find reference for creating tag %s at r%d\n' % (tag
, rev
)
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
)
387 count
=checkpoint(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
)
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:
409 .encode('ascii', 'backslashreplace')
410 .decode('unicode-escape')
414 def parse_quoted_line(line
):
415 m
=quoted_regexp
.match(line
)
419 return (process_unicode_escape_sequences(m
.group(1)),
420 process_unicode_escape_sequences(m
.group(5)))
423 if not os
.path
.exists(filename
):
424 sys
.stderr
.write('Could not open mapping file [%s]\n' % (filename
))
426 f
=open(filename
,'rb')
429 for line
in f
.readlines():
432 if l
==1 and line
[0:1]==b
'#' and line
==b
'# quoted-escaped-strings':
434 elif line
==b
'' or line
[0:1]==b
'#':
436 m
=parse_raw_line(line
) if mapping_is_raw
else parse_quoted_line(line
)
438 sys
.stderr
.write('Invalid file format in [%s], line %d\n' % (filename
,l
))
440 # put key:value in cache, key without ^:
444 sys
.stderr
.write('Loaded %d %s\n' % (a
, name
))
447 def branchtip(repo
, heads
):
448 '''return the tipmost branch head in heads'''
450 for h
in reversed(heads
):
451 if 'close' not in repo
.changelog
.read(h
)[5]:
456 def verify_heads(ui
,repo
,cache
,force
,ignore_unnamed_heads
,branchesmap
):
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()]
463 # get list of hg's branches to verify, don't take all git has
466 sanitized_name
=sanitize_name(b
,"branch",branchesmap
)
467 sha1
=get_git_sha1(sanitized_name
)
468 c
=cache
.get(sanitized_name
)
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
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
484 for h
in repo
.filtered(b
'visible').heads():
485 branch
=get_branch(repo
[h
].branch())
486 if t
.get(branch
,False):
488 b
'Error: repository has an unnamed head: hg r%d\n'
489 % repo
.changelog
.rev(h
)
492 if not force
and not ignore_unnamed_heads
: return False
494 if unnamed_heads
and not force
and not ignore_unnamed_heads
: return False
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
='',
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
)
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
):
524 tip
=repo
.changelog
.count()
525 except AttributeError:
528 min=int(state_cache
.get(b
'tip',0))
530 if _max
<0 or max>tip
:
533 for rev
in range(0,max):
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):
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
))
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
,
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
)
572 if __name__
=='__main__':
573 def bail(parser
,opt
):
574 sys
.stderr
.write('Error: No %s option given\n' % opt
)
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()
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
)
647 submodule_mappings
=load_mapping('subrepo mappings',
648 options
.subrepo_map
,False)
651 if options
.authorfile
!=None:
652 a
=load_mapping('authors', options
.authorfile
, options
.raw_mappings
)
655 if options
.branchesfile
!=None:
656 b
=load_mapping('branches', options
.branchesfile
, options
.raw_mappings
)
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
)
669 if options
.encoding
!=None:
670 encoding
=options
.encoding
673 if options
.fn_encoding
!=None:
674 fn_encoding
=options
.fn_encoding
677 if options
.plugins
!=None:
678 plugins
+=options
.plugins
680 if options
.filter_contents
!=None:
681 plugins
+=['shell_filter_file_contents='+options
.filter_contents
]
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
))