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
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
98 loglines
=logmessage
.split(b
'\n')
100 # from tail walk to top skipping empty lines
103 if len(loglines
[i
].strip())==0: continue
106 # walk further upwards to find first sob line, store in 'first'
109 m
=sob_re
.match(loglines
[i
])
113 # if the last non-empty line matches our Signed-Off-by regex: extract username
115 r
=fixup_user(first
.group(1),authors
)
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
)
129 def refresh_git_submodule(name
,subrepo_info
):
130 wr(b
'M 160000 %s %s' % (subrepo_info
[1],name
))
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",
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
))
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
])
159 b
"Warning: Could not find hg revision %s for %s in git %s\n"
160 % (subrepo_hash
, name
, gitRepoLocation
,)
164 def refresh_gitmodules(ctx
):
165 """Updates list of ctx submodules according to .hgsubstate file"""
166 remove_gitmodules(ctx
)
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
)
176 wr(b
'M 100644 inline .gitmodules')
179 def export_file_contents(ctx
,manifest
,files
,hgtags
,encoding
='',plugins
={}):
182 is_submodules_refreshed
=False
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)
192 filename
=file.decode(encoding
).encode('utf8')
195 if b
'.git' in filename
.split(b
'/'): # Even on Windows, the path separator is / here.
197 b
'Ignoring file %s which cannot be tracked by git\n' % filename
200 file_ctx
=ctx
.filectx(file)
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']:
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()
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.
238 if not name
: return name
239 if name
[0:1] == b
'.': return b
'_'+name
[1:]
242 if not auto_sanitize
:
243 return mapping
.get(name
,name
)
244 n
=mapping
.get(name
,name
)
245 p
=re
.compile(b
'([\\[ ~^:?\\\\*]|\.\.)')
247 if n
[-1:] in (b
'/', b
'.'): n
=n
[:-1]+b
'_'
248 n
=b
'/'.join([dot(s
) for s
in n
.split(b
'/')])
254 b
'Warning: sanitized %s [%s] to [%s]\n' % (what
.encode(), name
, n
)
258 def strip_leading_slash(filename
):
259 if filename
[0:1] == b
'/':
263 def export_commit(ui
,repo
,revision
,old_marks
,max,count
,authors
,
264 branchesmap
,sob
,brmap
,hgtags
,encoding
='',fn_encoding
='',
266 def get_branchname(name
):
269 n
=sanitize_name(name
, "branch", branchesmap
)
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
)
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']:
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))
305 wr(b
'author %s %d %s' % (author
,time
,timezone
))
306 wr(b
'committer %s %d %s' % (user
,time
,timezone
))
314 wr(b
'from %s' % revnum_to_revref(parents
[0], old_marks
))
315 if len(parents
) == 1:
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
)
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
:
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
)
339 return checkpoint(count
)
341 def export_note(ui
,repo
,revision
,count
,authors
,encoding
,is_first
):
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
))
353 wr(b
'from refs/notes/hg^0')
354 wr(b
'N inline :%d' % (revision
+1))
358 return checkpoint(count
)
360 def export_tags(ui
,repo
,old_marks
,mapping_cache
,count
,authors
,tagsmap
):
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
)))
372 rev
=int(mapping_cache
[hexlify(node
)])
374 ref
=revnum_to_revref(rev
, old_marks
)
377 b
'Failed to find reference for creating tag %s at r%d\n' % (tag
, rev
)
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
)
384 count
=checkpoint(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
)
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:
406 .encode('ascii', 'backslashreplace')
407 .decode('unicode-escape')
411 def parse_quoted_line(line
):
412 m
=quoted_regexp
.match(line
)
416 return (process_unicode_escape_sequences(m
.group(1)),
417 process_unicode_escape_sequences(m
.group(5)))
420 if not os
.path
.exists(filename
):
421 sys
.stderr
.write('Could not open mapping file [%s]\n' % (filename
))
423 f
=open(filename
,'rb')
426 for line
in f
.readlines():
429 if l
==1 and line
[0:1]==b
'#' and line
==b
'# quoted-escaped-strings':
431 elif line
==b
'' or line
[0:1]==b
'#':
433 m
=parse_raw_line(line
) if mapping_is_raw
else parse_quoted_line(line
)
435 sys
.stderr
.write('Invalid file format in [%s], line %d\n' % (filename
,l
))
437 # put key:value in cache, key without ^:
441 sys
.stderr
.write('Loaded %d %s\n' % (a
, name
))
444 def branchtip(repo
, heads
):
445 '''return the tipmost branch head in heads'''
447 for h
in reversed(heads
):
448 if 'close' not in repo
.changelog
.read(h
)[5]:
453 def verify_heads(ui
,repo
,cache
,force
,ignore_unnamed_heads
,branchesmap
):
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()]
460 # get list of hg's branches to verify, don't take all git has
463 sanitized_name
=sanitize_name(b
,"branch",branchesmap
)
464 sha1
=get_git_sha1(sanitized_name
)
465 c
=cache
.get(sanitized_name
)
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
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
481 for h
in repo
.filtered(b
'visible').heads():
482 branch
=get_branch(repo
[h
].branch())
483 if t
.get(branch
,False):
485 b
'Error: repository has an unnamed head: hg r%d\n'
486 % repo
.changelog
.rev(h
)
489 if not force
and not ignore_unnamed_heads
: return False
491 if unnamed_heads
and not force
and not ignore_unnamed_heads
: return False
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
='',
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
)
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
):
521 tip
=repo
.changelog
.count()
522 except AttributeError:
525 min=int(state_cache
.get(b
'tip',0))
527 if _max
<0 or max>tip
:
530 for rev
in range(0,max):
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):
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
))
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
,
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
)
569 if __name__
=='__main__':
570 def bail(parser
,opt
):
571 sys
.stderr
.write('Error: No %s option given\n' % opt
)
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()
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
)
644 submodule_mappings
=load_mapping('subrepo mappings',
645 options
.subrepo_map
,False)
648 if options
.authorfile
!=None:
649 a
=load_mapping('authors', options
.authorfile
, options
.raw_mappings
)
652 if options
.branchesfile
!=None:
653 b
=load_mapping('branches', options
.branchesfile
, options
.raw_mappings
)
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
)
666 if options
.encoding
!=None:
667 encoding
=options
.encoding
670 if options
.fn_encoding
!=None:
671 fn_encoding
=options
.fn_encoding
674 if options
.plugins
!=None:
675 plugins
+=options
.plugins
677 if options
.filter_contents
!=None:
678 plugins
+=['shell_filter_file_contents='+options
.filter_contents
]
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
))