remote-hg: small performance improvement
[git.git] / contrib / remote-helpers / git-remote-hg
blobf80236be63dbaeba92dcaaaaa7e77173d0d394c1
1 #!/usr/bin/env python
3 # Copyright (c) 2012 Felipe Contreras
6 # Inspired by Rocco Rutte's hg-fast-export
8 # Just copy to your ~/bin, or anywhere in your $PATH.
9 # Then you can clone with:
10 # git clone hg::/path/to/mercurial/repo/
12 # For remote repositories a local clone is stored in
13 # "$GIT_DIR/hg/origin/clone/.hg/".
15 from mercurial import hg, ui, bookmarks, context, util, encoding, node, error, extensions
17 import re
18 import sys
19 import os
20 import json
21 import shutil
22 import subprocess
23 import urllib
24 import atexit
25 import urlparse
28 # If you want to switch to hg-git compatibility mode:
29 # git config --global remote-hg.hg-git-compat true
31 # If you are not in hg-git-compat mode and want to disable the tracking of
32 # named branches:
33 # git config --global remote-hg.track-branches false
35 # If you don't want to force pushes (and thus risk creating new remote heads):
36 # git config --global remote-hg.force-push false
38 # If you want the equivalent of hg's clone/pull--insecure option:
39 # git config remote-hg.insecure true
41 # git:
42 # Sensible defaults for git.
43 # hg bookmarks are exported as git branches, hg branches are prefixed
44 # with 'branches/', HEAD is a special case.
46 # hg:
47 # Emulate hg-git.
48 # Only hg bookmarks are exported as git branches.
49 # Commits are modified to preserve hg information and allow bidirectionality.
52 NAME_RE = re.compile('^([^<>]+)')
53 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
54 EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
55 AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
56 RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
58 def die(msg, *args):
59 sys.stderr.write('ERROR: %s\n' % (msg % args))
60 sys.exit(1)
62 def warn(msg, *args):
63 sys.stderr.write('WARNING: %s\n' % (msg % args))
65 def gitmode(flags):
66 return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'
68 def gittz(tz):
69 return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
71 def hgmode(mode):
72 m = { '100755': 'x', '120000': 'l' }
73 return m.get(mode, '')
75 def hghex(node):
76 return hg.node.hex(node)
78 def hgref(ref):
79 return ref.replace('___', ' ')
81 def gitref(ref):
82 return ref.replace(' ', '___')
84 def get_config(config):
85 cmd = ['git', 'config', '--get', config]
86 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
87 output, _ = process.communicate()
88 return output
90 class Marks:
92 def __init__(self, path):
93 self.path = path
94 self.tips = {}
95 self.marks = {}
96 self.rev_marks = {}
97 self.last_mark = 0
99 self.load()
101 def load(self):
102 if not os.path.exists(self.path):
103 return
105 tmp = json.load(open(self.path))
107 self.tips = tmp['tips']
108 self.marks = tmp['marks']
109 self.last_mark = tmp['last-mark']
111 for rev, mark in self.marks.iteritems():
112 self.rev_marks[mark] = int(rev)
114 def dict(self):
115 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
117 def store(self):
118 json.dump(self.dict(), open(self.path, 'w'))
120 def __str__(self):
121 return str(self.dict())
123 def from_rev(self, rev):
124 return self.marks[str(rev)]
126 def to_rev(self, mark):
127 return self.rev_marks[mark]
129 def get_mark(self, rev):
130 self.last_mark += 1
131 self.marks[str(rev)] = self.last_mark
132 return self.last_mark
134 def new_mark(self, rev, mark):
135 self.marks[str(rev)] = mark
136 self.rev_marks[mark] = rev
137 self.last_mark = mark
139 def is_marked(self, rev):
140 return str(rev) in self.marks
142 def get_tip(self, branch):
143 return self.tips.get(branch, 0)
145 def set_tip(self, branch, tip):
146 self.tips[branch] = tip
148 class Parser:
150 def __init__(self, repo):
151 self.repo = repo
152 self.line = self.get_line()
154 def get_line(self):
155 return sys.stdin.readline().strip()
157 def __getitem__(self, i):
158 return self.line.split()[i]
160 def check(self, word):
161 return self.line.startswith(word)
163 def each_block(self, separator):
164 while self.line != separator:
165 yield self.line
166 self.line = self.get_line()
168 def __iter__(self):
169 return self.each_block('')
171 def next(self):
172 self.line = self.get_line()
173 if self.line == 'done':
174 self.line = None
176 def get_mark(self):
177 i = self.line.index(':') + 1
178 return int(self.line[i:])
180 def get_data(self):
181 if not self.check('data'):
182 return None
183 i = self.line.index(' ') + 1
184 size = int(self.line[i:])
185 return sys.stdin.read(size)
187 def get_author(self):
188 global bad_mail
190 ex = None
191 m = RAW_AUTHOR_RE.match(self.line)
192 if not m:
193 return None
194 _, name, email, date, tz = m.groups()
195 if name and 'ext:' in name:
196 m = re.match('^(.+?) ext:\((.+)\)$', name)
197 if m:
198 name = m.group(1)
199 ex = urllib.unquote(m.group(2))
201 if email != bad_mail:
202 if name:
203 user = '%s <%s>' % (name, email)
204 else:
205 user = '<%s>' % (email)
206 else:
207 user = name
209 if ex:
210 user += ex
212 tz = int(tz)
213 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
214 return (user, int(date), -tz)
216 def fix_file_path(path):
217 if not os.path.isabs(path):
218 return path
219 return os.path.relpath(path, '/')
221 def export_file(fc):
222 d = fc.data()
223 path = fix_file_path(fc.path())
224 print "M %s inline %s" % (gitmode(fc.flags()), path)
225 print "data %d" % len(d)
226 print d
228 def get_filechanges(repo, ctx, parent):
229 modified = set()
230 added = set()
231 removed = set()
233 # load earliest manifest first for caching reasons
234 prev = repo[parent].manifest().copy()
235 cur = ctx.manifest()
237 for fn in cur:
238 if fn in prev:
239 if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
240 modified.add(fn)
241 del prev[fn]
242 else:
243 added.add(fn)
244 removed |= set(prev.keys())
246 return added | modified, removed
248 def fixup_user_git(user):
249 name = mail = None
250 user = user.replace('"', '')
251 m = AUTHOR_RE.match(user)
252 if m:
253 name = m.group(1)
254 mail = m.group(2).strip()
255 else:
256 m = EMAIL_RE.match(user)
257 if m:
258 name = m.group(1)
259 mail = m.group(2)
260 else:
261 m = NAME_RE.match(user)
262 if m:
263 name = m.group(1).strip()
264 return (name, mail)
266 def fixup_user_hg(user):
267 def sanitize(name):
268 # stole this from hg-git
269 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
271 m = AUTHOR_HG_RE.match(user)
272 if m:
273 name = sanitize(m.group(1))
274 mail = sanitize(m.group(2))
275 ex = m.group(3)
276 if ex:
277 name += ' ext:(' + urllib.quote(ex) + ')'
278 else:
279 name = sanitize(user)
280 if '@' in user:
281 mail = name
282 else:
283 mail = None
285 return (name, mail)
287 def fixup_user(user):
288 global mode, bad_mail
290 if mode == 'git':
291 name, mail = fixup_user_git(user)
292 else:
293 name, mail = fixup_user_hg(user)
295 if not name:
296 name = bad_name
297 if not mail:
298 mail = bad_mail
300 return '%s <%s>' % (name, mail)
302 def get_repo(url, alias):
303 global dirname, peer
305 myui = ui.ui()
306 myui.setconfig('ui', 'interactive', 'off')
307 myui.fout = sys.stderr
309 try:
310 if get_config('remote-hg.insecure') == 'true\n':
311 myui.setconfig('web', 'cacerts', '')
312 except subprocess.CalledProcessError:
313 pass
315 try:
316 mod = extensions.load(myui, 'hgext.schemes', None)
317 mod.extsetup(myui)
318 except ImportError:
319 pass
321 if hg.islocal(url):
322 repo = hg.repository(myui, url)
323 else:
324 local_path = os.path.join(dirname, 'clone')
325 if not os.path.exists(local_path):
326 try:
327 peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
328 except:
329 die('Repository error')
330 repo = dstpeer.local()
331 else:
332 repo = hg.repository(myui, local_path)
333 try:
334 peer = hg.peer(myui, {}, url)
335 except:
336 die('Repository error')
337 repo.pull(peer, heads=None, force=True)
339 return repo
341 def rev_to_mark(rev):
342 global marks
343 return marks.from_rev(rev)
345 def mark_to_rev(mark):
346 global marks
347 return marks.to_rev(mark)
349 def export_ref(repo, name, kind, head):
350 global prefix, marks, mode
352 ename = '%s/%s' % (kind, name)
353 tip = marks.get_tip(ename)
355 # mercurial takes too much time checking this
356 if tip and tip == head.rev():
357 # nothing to do
358 return
359 revs = xrange(tip, head.rev() + 1)
360 count = 0
362 revs = [rev for rev in revs if not marks.is_marked(rev)]
364 for rev in revs:
366 c = repo[rev]
367 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
368 rev_branch = extra['branch']
370 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
371 if 'committer' in extra:
372 user, time, tz = extra['committer'].rsplit(' ', 2)
373 committer = "%s %s %s" % (user, time, gittz(int(tz)))
374 else:
375 committer = author
377 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
379 if len(parents) == 0:
380 modified = c.manifest().keys()
381 removed = []
382 else:
383 modified, removed = get_filechanges(repo, c, parents[0])
385 desc += '\n'
387 if mode == 'hg':
388 extra_msg = ''
390 if rev_branch != 'default':
391 extra_msg += 'branch : %s\n' % rev_branch
393 renames = []
394 for f in c.files():
395 if f not in c.manifest():
396 continue
397 rename = c.filectx(f).renamed()
398 if rename:
399 renames.append((rename[0], f))
401 for e in renames:
402 extra_msg += "rename : %s => %s\n" % e
404 for key, value in extra.iteritems():
405 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
406 continue
407 else:
408 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
410 if extra_msg:
411 desc += '\n--HG--\n' + extra_msg
413 if len(parents) == 0 and rev:
414 print 'reset %s/%s' % (prefix, ename)
416 print "commit %s/%s" % (prefix, ename)
417 print "mark :%d" % (marks.get_mark(rev))
418 print "author %s" % (author)
419 print "committer %s" % (committer)
420 print "data %d" % (len(desc))
421 print desc
423 if len(parents) > 0:
424 print "from :%s" % (rev_to_mark(parents[0]))
425 if len(parents) > 1:
426 print "merge :%s" % (rev_to_mark(parents[1]))
428 for f in modified:
429 export_file(c.filectx(f))
430 for f in removed:
431 print "D %s" % (fix_file_path(f))
432 print
434 count += 1
435 if (count % 100 == 0):
436 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
437 print "#############################################################"
439 # make sure the ref is updated
440 print "reset %s/%s" % (prefix, ename)
441 print "from :%u" % rev_to_mark(rev)
442 print
444 marks.set_tip(ename, rev)
446 def export_tag(repo, tag):
447 export_ref(repo, tag, 'tags', repo[hgref(tag)])
449 def export_bookmark(repo, bmark):
450 head = bmarks[hgref(bmark)]
451 export_ref(repo, bmark, 'bookmarks', head)
453 def export_branch(repo, branch):
454 tip = get_branch_tip(repo, branch)
455 head = repo[tip]
456 export_ref(repo, branch, 'branches', head)
458 def export_head(repo):
459 global g_head
460 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
462 def do_capabilities(parser):
463 global prefix, dirname
465 print "import"
466 print "export"
467 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
468 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
469 print "refspec refs/tags/*:%s/tags/*" % prefix
471 path = os.path.join(dirname, 'marks-git')
473 if os.path.exists(path):
474 print "*import-marks %s" % path
475 print "*export-marks %s" % path
477 print
479 def branch_tip(repo, branch):
480 # older versions of mercurial don't have this
481 if hasattr(repo, 'branchtip'):
482 return repo.branchtip(branch)
483 else:
484 return repo.branchtags()[branch]
486 def get_branch_tip(repo, branch):
487 global branches
489 heads = branches.get(hgref(branch), None)
490 if not heads:
491 return None
493 # verify there's only one head
494 if (len(heads) > 1):
495 warn("Branch '%s' has more than one head, consider merging" % branch)
496 return branch_tip(repo, hgref(branch))
498 return heads[0]
500 def list_head(repo, cur):
501 global g_head, bmarks
503 head = bookmarks.readcurrent(repo)
504 if head:
505 node = repo[head]
506 else:
507 # fake bookmark from current branch
508 head = cur
509 node = repo['.']
510 if not node:
511 node = repo['tip']
512 if not node:
513 return
514 if head == 'default':
515 head = 'master'
516 bmarks[head] = node
518 head = gitref(head)
519 print "@refs/heads/%s HEAD" % head
520 g_head = (head, node)
522 def do_list(parser):
523 global branches, bmarks, mode, track_branches
525 repo = parser.repo
526 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
527 bmarks[bmark] = repo[node]
529 cur = repo.dirstate.branch()
531 list_head(repo, cur)
533 if track_branches:
534 for branch in repo.branchmap():
535 heads = repo.branchheads(branch)
536 if len(heads):
537 branches[branch] = heads
539 for branch in branches:
540 print "? refs/heads/branches/%s" % gitref(branch)
542 for bmark in bmarks:
543 print "? refs/heads/%s" % gitref(bmark)
545 for tag, node in repo.tagslist():
546 if tag == 'tip':
547 continue
548 print "? refs/tags/%s" % gitref(tag)
550 print
552 def do_import(parser):
553 repo = parser.repo
555 path = os.path.join(dirname, 'marks-git')
557 print "feature done"
558 if os.path.exists(path):
559 print "feature import-marks=%s" % path
560 print "feature export-marks=%s" % path
561 sys.stdout.flush()
563 tmp = encoding.encoding
564 encoding.encoding = 'utf-8'
566 # lets get all the import lines
567 while parser.check('import'):
568 ref = parser[1]
570 if (ref == 'HEAD'):
571 export_head(repo)
572 elif ref.startswith('refs/heads/branches/'):
573 branch = ref[len('refs/heads/branches/'):]
574 export_branch(repo, branch)
575 elif ref.startswith('refs/heads/'):
576 bmark = ref[len('refs/heads/'):]
577 export_bookmark(repo, bmark)
578 elif ref.startswith('refs/tags/'):
579 tag = ref[len('refs/tags/'):]
580 export_tag(repo, tag)
582 parser.next()
584 encoding.encoding = tmp
586 print 'done'
588 def parse_blob(parser):
589 global blob_marks
591 parser.next()
592 mark = parser.get_mark()
593 parser.next()
594 data = parser.get_data()
595 blob_marks[mark] = data
596 parser.next()
598 def get_merge_files(repo, p1, p2, files):
599 for e in repo[p1].files():
600 if e not in files:
601 if e not in repo[p1].manifest():
602 continue
603 f = { 'ctx' : repo[p1][e] }
604 files[e] = f
606 def parse_commit(parser):
607 global marks, blob_marks, parsed_refs
608 global mode
610 from_mark = merge_mark = None
612 ref = parser[1]
613 parser.next()
615 commit_mark = parser.get_mark()
616 parser.next()
617 author = parser.get_author()
618 parser.next()
619 committer = parser.get_author()
620 parser.next()
621 data = parser.get_data()
622 parser.next()
623 if parser.check('from'):
624 from_mark = parser.get_mark()
625 parser.next()
626 if parser.check('merge'):
627 merge_mark = parser.get_mark()
628 parser.next()
629 if parser.check('merge'):
630 die('octopus merges are not supported yet')
632 files = {}
634 for line in parser:
635 if parser.check('M'):
636 t, m, mark_ref, path = line.split(' ', 3)
637 mark = int(mark_ref[1:])
638 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
639 elif parser.check('D'):
640 t, path = line.split(' ', 1)
641 f = { 'deleted' : True }
642 else:
643 die('Unknown file command: %s' % line)
644 files[path] = f
646 def getfilectx(repo, memctx, f):
647 of = files[f]
648 if 'deleted' in of:
649 raise IOError
650 if 'ctx' in of:
651 return of['ctx']
652 is_exec = of['mode'] == 'x'
653 is_link = of['mode'] == 'l'
654 rename = of.get('rename', None)
655 return context.memfilectx(f, of['data'],
656 is_link, is_exec, rename)
658 repo = parser.repo
660 user, date, tz = author
661 extra = {}
663 if committer != author:
664 extra['committer'] = "%s %u %u" % committer
666 if from_mark:
667 p1 = repo.changelog.node(mark_to_rev(from_mark))
668 else:
669 p1 = '\0' * 20
671 if merge_mark:
672 p2 = repo.changelog.node(mark_to_rev(merge_mark))
673 else:
674 p2 = '\0' * 20
677 # If files changed from any of the parents, hg wants to know, but in git if
678 # nothing changed from the first parent, nothing changed.
680 if merge_mark:
681 get_merge_files(repo, p1, p2, files)
683 # Check if the ref is supposed to be a named branch
684 if ref.startswith('refs/heads/branches/'):
685 branch = ref[len('refs/heads/branches/'):]
686 extra['branch'] = hgref(branch)
688 if mode == 'hg':
689 i = data.find('\n--HG--\n')
690 if i >= 0:
691 tmp = data[i + len('\n--HG--\n'):].strip()
692 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
693 if k == 'rename':
694 old, new = v.split(' => ', 1)
695 files[new]['rename'] = old
696 elif k == 'branch':
697 extra[k] = v
698 elif k == 'extra':
699 ek, ev = v.split(' : ', 1)
700 extra[ek] = urllib.unquote(ev)
701 data = data[:i]
703 ctx = context.memctx(repo, (p1, p2), data,
704 files.keys(), getfilectx,
705 user, (date, tz), extra)
707 tmp = encoding.encoding
708 encoding.encoding = 'utf-8'
710 node = repo.commitctx(ctx)
712 encoding.encoding = tmp
714 rev = repo[node].rev()
716 parsed_refs[ref] = node
717 marks.new_mark(rev, commit_mark)
719 def parse_reset(parser):
720 global parsed_refs
722 ref = parser[1]
723 parser.next()
724 # ugh
725 if parser.check('commit'):
726 parse_commit(parser)
727 return
728 if not parser.check('from'):
729 return
730 from_mark = parser.get_mark()
731 parser.next()
733 node = parser.repo.changelog.node(mark_to_rev(from_mark))
734 parsed_refs[ref] = node
736 def parse_tag(parser):
737 name = parser[1]
738 parser.next()
739 from_mark = parser.get_mark()
740 parser.next()
741 tagger = parser.get_author()
742 parser.next()
743 data = parser.get_data()
744 parser.next()
746 parsed_tags[name] = (tagger, data)
748 def write_tag(repo, tag, node, msg, author):
749 branch = repo[node].branch()
750 tip = branch_tip(repo, branch)
751 tip = repo[tip]
753 def getfilectx(repo, memctx, f):
754 try:
755 fctx = tip.filectx(f)
756 data = fctx.data()
757 except error.ManifestLookupError:
758 data = ""
759 content = data + "%s %s\n" % (hghex(node), tag)
760 return context.memfilectx(f, content, False, False, None)
762 p1 = tip.hex()
763 p2 = '\0' * 20
764 if not author:
765 author = (None, 0, 0)
766 user, date, tz = author
768 ctx = context.memctx(repo, (p1, p2), msg,
769 ['.hgtags'], getfilectx,
770 user, (date, tz), {'branch' : branch})
772 tmp = encoding.encoding
773 encoding.encoding = 'utf-8'
775 tagnode = repo.commitctx(ctx)
777 encoding.encoding = tmp
779 return tagnode
781 def do_export(parser):
782 global parsed_refs, bmarks, peer
784 p_bmarks = []
786 parser.next()
788 for line in parser.each_block('done'):
789 if parser.check('blob'):
790 parse_blob(parser)
791 elif parser.check('commit'):
792 parse_commit(parser)
793 elif parser.check('reset'):
794 parse_reset(parser)
795 elif parser.check('tag'):
796 parse_tag(parser)
797 elif parser.check('feature'):
798 pass
799 else:
800 die('unhandled export command: %s' % line)
802 for ref, node in parsed_refs.iteritems():
803 if ref.startswith('refs/heads/branches'):
804 branch = ref[len('refs/heads/branches/'):]
805 if branch in branches and node in branches[branch]:
806 # up to date
807 continue
808 print "ok %s" % ref
809 elif ref.startswith('refs/heads/'):
810 bmark = ref[len('refs/heads/'):]
811 p_bmarks.append((bmark, node))
812 continue
813 elif ref.startswith('refs/tags/'):
814 tag = ref[len('refs/tags/'):]
815 tag = hgref(tag)
816 author, msg = parsed_tags.get(tag, (None, None))
817 if mode == 'git':
818 if not msg:
819 msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
820 write_tag(parser.repo, tag, node, msg, author)
821 else:
822 fp = parser.repo.opener('localtags', 'a')
823 fp.write('%s %s\n' % (hghex(node), tag))
824 fp.close()
825 print "ok %s" % ref
826 else:
827 # transport-helper/fast-export bugs
828 continue
830 if peer:
831 parser.repo.push(peer, force=force_push)
833 # handle bookmarks
834 for bmark, node in p_bmarks:
835 ref = 'refs/heads/' + bmark
836 new = hghex(node)
838 if bmark in bmarks:
839 old = bmarks[bmark].hex()
840 else:
841 old = ''
843 if old == new:
844 continue
846 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
847 # fake bookmark
848 pass
849 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
850 # updated locally
851 pass
852 else:
853 print "error %s" % ref
854 continue
856 if peer:
857 rb = peer.listkeys('bookmarks')
858 old = rb.get(bmark, '')
859 if not peer.pushkey('bookmarks', bmark, old, new):
860 print "error %s" % ref
861 continue
863 print "ok %s" % ref
865 print
867 def fix_path(alias, repo, orig_url):
868 url = urlparse.urlparse(orig_url, 'file')
869 if url.scheme != 'file' or os.path.isabs(url.path):
870 return
871 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
872 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
873 subprocess.call(cmd)
875 def main(args):
876 global prefix, dirname, branches, bmarks
877 global marks, blob_marks, parsed_refs
878 global peer, mode, bad_mail, bad_name
879 global track_branches, force_push, is_tmp
880 global parsed_tags
882 alias = args[1]
883 url = args[2]
884 peer = None
886 hg_git_compat = False
887 track_branches = True
888 force_push = True
890 try:
891 if get_config('remote-hg.hg-git-compat') == 'true\n':
892 hg_git_compat = True
893 track_branches = False
894 if get_config('remote-hg.track-branches') == 'false\n':
895 track_branches = False
896 if get_config('remote-hg.force-push') == 'false\n':
897 force_push = False
898 except subprocess.CalledProcessError:
899 pass
901 if hg_git_compat:
902 mode = 'hg'
903 bad_mail = 'none@none'
904 bad_name = ''
905 else:
906 mode = 'git'
907 bad_mail = 'unknown'
908 bad_name = 'Unknown'
910 if alias[4:] == url:
911 is_tmp = True
912 alias = util.sha1(alias).hexdigest()
913 else:
914 is_tmp = False
916 gitdir = os.environ['GIT_DIR']
917 dirname = os.path.join(gitdir, 'hg', alias)
918 branches = {}
919 bmarks = {}
920 blob_marks = {}
921 parsed_refs = {}
922 marks = None
923 parsed_tags = {}
925 repo = get_repo(url, alias)
926 prefix = 'refs/hg/%s' % alias
928 if not is_tmp:
929 fix_path(alias, peer or repo, url)
931 if not os.path.exists(dirname):
932 os.makedirs(dirname)
934 marks_path = os.path.join(dirname, 'marks-hg')
935 marks = Marks(marks_path)
937 parser = Parser(repo)
938 for line in parser:
939 if parser.check('capabilities'):
940 do_capabilities(parser)
941 elif parser.check('list'):
942 do_list(parser)
943 elif parser.check('import'):
944 do_import(parser)
945 elif parser.check('export'):
946 do_export(parser)
947 else:
948 die('unhandled command: %s' % line)
949 sys.stdout.flush()
951 def bye():
952 if not marks:
953 return
954 if not is_tmp:
955 marks.store()
956 else:
957 shutil.rmtree(dirname)
959 atexit.register(bye)
960 sys.exit(main(sys.argv))