remote-hg: allow refs with spaces
[git/mjg.git] / contrib / remote-helpers / git-remote-hg
blob6f4afd729c9410950abad08b4937acfa0a1b9f78
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 cur = ctx.manifest()
234 prev = repo[parent].manifest().copy()
236 for fn in cur:
237 if fn in prev:
238 if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
239 modified.add(fn)
240 del prev[fn]
241 else:
242 added.add(fn)
243 removed |= set(prev.keys())
245 return added | modified, removed
247 def fixup_user_git(user):
248 name = mail = None
249 user = user.replace('"', '')
250 m = AUTHOR_RE.match(user)
251 if m:
252 name = m.group(1)
253 mail = m.group(2).strip()
254 else:
255 m = EMAIL_RE.match(user)
256 if m:
257 name = m.group(1)
258 mail = m.group(2)
259 else:
260 m = NAME_RE.match(user)
261 if m:
262 name = m.group(1).strip()
263 return (name, mail)
265 def fixup_user_hg(user):
266 def sanitize(name):
267 # stole this from hg-git
268 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
270 m = AUTHOR_HG_RE.match(user)
271 if m:
272 name = sanitize(m.group(1))
273 mail = sanitize(m.group(2))
274 ex = m.group(3)
275 if ex:
276 name += ' ext:(' + urllib.quote(ex) + ')'
277 else:
278 name = sanitize(user)
279 if '@' in user:
280 mail = name
281 else:
282 mail = None
284 return (name, mail)
286 def fixup_user(user):
287 global mode, bad_mail
289 if mode == 'git':
290 name, mail = fixup_user_git(user)
291 else:
292 name, mail = fixup_user_hg(user)
294 if not name:
295 name = bad_name
296 if not mail:
297 mail = bad_mail
299 return '%s <%s>' % (name, mail)
301 def get_repo(url, alias):
302 global dirname, peer
304 myui = ui.ui()
305 myui.setconfig('ui', 'interactive', 'off')
306 myui.fout = sys.stderr
308 try:
309 if get_config('remote-hg.insecure') == 'true\n':
310 myui.setconfig('web', 'cacerts', '')
311 except subprocess.CalledProcessError:
312 pass
314 try:
315 mod = extensions.load(myui, 'hgext.schemes', None)
316 mod.extsetup(myui)
317 except ImportError:
318 pass
320 if hg.islocal(url):
321 repo = hg.repository(myui, url)
322 else:
323 local_path = os.path.join(dirname, 'clone')
324 if not os.path.exists(local_path):
325 try:
326 peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
327 except:
328 die('Repository error')
329 repo = dstpeer.local()
330 else:
331 repo = hg.repository(myui, local_path)
332 try:
333 peer = hg.peer(myui, {}, url)
334 except:
335 die('Repository error')
336 repo.pull(peer, heads=None, force=True)
338 return repo
340 def rev_to_mark(rev):
341 global marks
342 return marks.from_rev(rev)
344 def mark_to_rev(mark):
345 global marks
346 return marks.to_rev(mark)
348 def export_ref(repo, name, kind, head):
349 global prefix, marks, mode
351 ename = '%s/%s' % (kind, name)
352 tip = marks.get_tip(ename)
354 # mercurial takes too much time checking this
355 if tip and tip == head.rev():
356 # nothing to do
357 return
358 revs = xrange(tip, head.rev() + 1)
359 count = 0
361 revs = [rev for rev in revs if not marks.is_marked(rev)]
363 for rev in revs:
365 c = repo[rev]
366 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
367 rev_branch = extra['branch']
369 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
370 if 'committer' in extra:
371 user, time, tz = extra['committer'].rsplit(' ', 2)
372 committer = "%s %s %s" % (user, time, gittz(int(tz)))
373 else:
374 committer = author
376 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
378 if len(parents) == 0:
379 modified = c.manifest().keys()
380 removed = []
381 else:
382 modified, removed = get_filechanges(repo, c, parents[0])
384 desc += '\n'
386 if mode == 'hg':
387 extra_msg = ''
389 if rev_branch != 'default':
390 extra_msg += 'branch : %s\n' % rev_branch
392 renames = []
393 for f in c.files():
394 if f not in c.manifest():
395 continue
396 rename = c.filectx(f).renamed()
397 if rename:
398 renames.append((rename[0], f))
400 for e in renames:
401 extra_msg += "rename : %s => %s\n" % e
403 for key, value in extra.iteritems():
404 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
405 continue
406 else:
407 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
409 if extra_msg:
410 desc += '\n--HG--\n' + extra_msg
412 if len(parents) == 0 and rev:
413 print 'reset %s/%s' % (prefix, ename)
415 print "commit %s/%s" % (prefix, ename)
416 print "mark :%d" % (marks.get_mark(rev))
417 print "author %s" % (author)
418 print "committer %s" % (committer)
419 print "data %d" % (len(desc))
420 print desc
422 if len(parents) > 0:
423 print "from :%s" % (rev_to_mark(parents[0]))
424 if len(parents) > 1:
425 print "merge :%s" % (rev_to_mark(parents[1]))
427 for f in modified:
428 export_file(c.filectx(f))
429 for f in removed:
430 print "D %s" % (fix_file_path(f))
431 print
433 count += 1
434 if (count % 100 == 0):
435 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
436 print "#############################################################"
438 # make sure the ref is updated
439 print "reset %s/%s" % (prefix, ename)
440 print "from :%u" % rev_to_mark(rev)
441 print
443 marks.set_tip(ename, rev)
445 def export_tag(repo, tag):
446 export_ref(repo, tag, 'tags', repo[hgref(tag)])
448 def export_bookmark(repo, bmark):
449 head = bmarks[hgref(bmark)]
450 export_ref(repo, bmark, 'bookmarks', head)
452 def export_branch(repo, branch):
453 tip = get_branch_tip(repo, branch)
454 head = repo[tip]
455 export_ref(repo, branch, 'branches', head)
457 def export_head(repo):
458 global g_head
459 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
461 def do_capabilities(parser):
462 global prefix, dirname
464 print "import"
465 print "export"
466 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
467 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
468 print "refspec refs/tags/*:%s/tags/*" % prefix
470 path = os.path.join(dirname, 'marks-git')
472 if os.path.exists(path):
473 print "*import-marks %s" % path
474 print "*export-marks %s" % path
476 print
478 def branch_tip(repo, branch):
479 # older versions of mercurial don't have this
480 if hasattr(repo, 'branchtip'):
481 return repo.branchtip(branch)
482 else:
483 return repo.branchtags()[branch]
485 def get_branch_tip(repo, branch):
486 global branches
488 heads = branches.get(hgref(branch), None)
489 if not heads:
490 return None
492 # verify there's only one head
493 if (len(heads) > 1):
494 warn("Branch '%s' has more than one head, consider merging" % branch)
495 return branch_tip(repo, hgref(branch))
497 return heads[0]
499 def list_head(repo, cur):
500 global g_head, bmarks
502 head = bookmarks.readcurrent(repo)
503 if head:
504 node = repo[head]
505 else:
506 # fake bookmark from current branch
507 head = cur
508 node = repo['.']
509 if not node:
510 node = repo['tip']
511 if not node:
512 return
513 if head == 'default':
514 head = 'master'
515 bmarks[head] = node
517 head = gitref(head)
518 print "@refs/heads/%s HEAD" % head
519 g_head = (head, node)
521 def do_list(parser):
522 global branches, bmarks, mode, track_branches
524 repo = parser.repo
525 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
526 bmarks[bmark] = repo[node]
528 cur = repo.dirstate.branch()
530 list_head(repo, cur)
532 if track_branches:
533 for branch in repo.branchmap():
534 heads = repo.branchheads(branch)
535 if len(heads):
536 branches[branch] = heads
538 for branch in branches:
539 print "? refs/heads/branches/%s" % gitref(branch)
541 for bmark in bmarks:
542 print "? refs/heads/%s" % gitref(bmark)
544 for tag, node in repo.tagslist():
545 if tag == 'tip':
546 continue
547 print "? refs/tags/%s" % gitref(tag)
549 print
551 def do_import(parser):
552 repo = parser.repo
554 path = os.path.join(dirname, 'marks-git')
556 print "feature done"
557 if os.path.exists(path):
558 print "feature import-marks=%s" % path
559 print "feature export-marks=%s" % path
560 sys.stdout.flush()
562 tmp = encoding.encoding
563 encoding.encoding = 'utf-8'
565 # lets get all the import lines
566 while parser.check('import'):
567 ref = parser[1]
569 if (ref == 'HEAD'):
570 export_head(repo)
571 elif ref.startswith('refs/heads/branches/'):
572 branch = ref[len('refs/heads/branches/'):]
573 export_branch(repo, branch)
574 elif ref.startswith('refs/heads/'):
575 bmark = ref[len('refs/heads/'):]
576 export_bookmark(repo, bmark)
577 elif ref.startswith('refs/tags/'):
578 tag = ref[len('refs/tags/'):]
579 export_tag(repo, tag)
581 parser.next()
583 encoding.encoding = tmp
585 print 'done'
587 def parse_blob(parser):
588 global blob_marks
590 parser.next()
591 mark = parser.get_mark()
592 parser.next()
593 data = parser.get_data()
594 blob_marks[mark] = data
595 parser.next()
597 def get_merge_files(repo, p1, p2, files):
598 for e in repo[p1].files():
599 if e not in files:
600 if e not in repo[p1].manifest():
601 continue
602 f = { 'ctx' : repo[p1][e] }
603 files[e] = f
605 def parse_commit(parser):
606 global marks, blob_marks, parsed_refs
607 global mode
609 from_mark = merge_mark = None
611 ref = parser[1]
612 parser.next()
614 commit_mark = parser.get_mark()
615 parser.next()
616 author = parser.get_author()
617 parser.next()
618 committer = parser.get_author()
619 parser.next()
620 data = parser.get_data()
621 parser.next()
622 if parser.check('from'):
623 from_mark = parser.get_mark()
624 parser.next()
625 if parser.check('merge'):
626 merge_mark = parser.get_mark()
627 parser.next()
628 if parser.check('merge'):
629 die('octopus merges are not supported yet')
631 files = {}
633 for line in parser:
634 if parser.check('M'):
635 t, m, mark_ref, path = line.split(' ', 3)
636 mark = int(mark_ref[1:])
637 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
638 elif parser.check('D'):
639 t, path = line.split(' ', 1)
640 f = { 'deleted' : True }
641 else:
642 die('Unknown file command: %s' % line)
643 files[path] = f
645 def getfilectx(repo, memctx, f):
646 of = files[f]
647 if 'deleted' in of:
648 raise IOError
649 if 'ctx' in of:
650 return of['ctx']
651 is_exec = of['mode'] == 'x'
652 is_link = of['mode'] == 'l'
653 rename = of.get('rename', None)
654 return context.memfilectx(f, of['data'],
655 is_link, is_exec, rename)
657 repo = parser.repo
659 user, date, tz = author
660 extra = {}
662 if committer != author:
663 extra['committer'] = "%s %u %u" % committer
665 if from_mark:
666 p1 = repo.changelog.node(mark_to_rev(from_mark))
667 else:
668 p1 = '\0' * 20
670 if merge_mark:
671 p2 = repo.changelog.node(mark_to_rev(merge_mark))
672 else:
673 p2 = '\0' * 20
676 # If files changed from any of the parents, hg wants to know, but in git if
677 # nothing changed from the first parent, nothing changed.
679 if merge_mark:
680 get_merge_files(repo, p1, p2, files)
682 # Check if the ref is supposed to be a named branch
683 if ref.startswith('refs/heads/branches/'):
684 branch = ref[len('refs/heads/branches/'):]
685 extra['branch'] = hgref(branch)
687 if mode == 'hg':
688 i = data.find('\n--HG--\n')
689 if i >= 0:
690 tmp = data[i + len('\n--HG--\n'):].strip()
691 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
692 if k == 'rename':
693 old, new = v.split(' => ', 1)
694 files[new]['rename'] = old
695 elif k == 'branch':
696 extra[k] = v
697 elif k == 'extra':
698 ek, ev = v.split(' : ', 1)
699 extra[ek] = urllib.unquote(ev)
700 data = data[:i]
702 ctx = context.memctx(repo, (p1, p2), data,
703 files.keys(), getfilectx,
704 user, (date, tz), extra)
706 tmp = encoding.encoding
707 encoding.encoding = 'utf-8'
709 node = repo.commitctx(ctx)
711 encoding.encoding = tmp
713 rev = repo[node].rev()
715 parsed_refs[ref] = node
716 marks.new_mark(rev, commit_mark)
718 def parse_reset(parser):
719 global parsed_refs
721 ref = parser[1]
722 parser.next()
723 # ugh
724 if parser.check('commit'):
725 parse_commit(parser)
726 return
727 if not parser.check('from'):
728 return
729 from_mark = parser.get_mark()
730 parser.next()
732 node = parser.repo.changelog.node(mark_to_rev(from_mark))
733 parsed_refs[ref] = node
735 def parse_tag(parser):
736 name = parser[1]
737 parser.next()
738 from_mark = parser.get_mark()
739 parser.next()
740 tagger = parser.get_author()
741 parser.next()
742 data = parser.get_data()
743 parser.next()
745 parsed_tags[name] = (tagger, data)
747 def write_tag(repo, tag, node, msg, author):
748 branch = repo[node].branch()
749 tip = branch_tip(repo, branch)
750 tip = repo[tip]
752 def getfilectx(repo, memctx, f):
753 try:
754 fctx = tip.filectx(f)
755 data = fctx.data()
756 except error.ManifestLookupError:
757 data = ""
758 content = data + "%s %s\n" % (hghex(node), tag)
759 return context.memfilectx(f, content, False, False, None)
761 p1 = tip.hex()
762 p2 = '\0' * 20
763 if not author:
764 author = (None, 0, 0)
765 user, date, tz = author
767 ctx = context.memctx(repo, (p1, p2), msg,
768 ['.hgtags'], getfilectx,
769 user, (date, tz), {'branch' : branch})
771 tmp = encoding.encoding
772 encoding.encoding = 'utf-8'
774 tagnode = repo.commitctx(ctx)
776 encoding.encoding = tmp
778 return tagnode
780 def do_export(parser):
781 global parsed_refs, bmarks, peer
783 p_bmarks = []
785 parser.next()
787 for line in parser.each_block('done'):
788 if parser.check('blob'):
789 parse_blob(parser)
790 elif parser.check('commit'):
791 parse_commit(parser)
792 elif parser.check('reset'):
793 parse_reset(parser)
794 elif parser.check('tag'):
795 parse_tag(parser)
796 elif parser.check('feature'):
797 pass
798 else:
799 die('unhandled export command: %s' % line)
801 for ref, node in parsed_refs.iteritems():
802 if ref.startswith('refs/heads/branches'):
803 branch = ref[len('refs/heads/branches/'):]
804 if branch in branches and node in branches[branch]:
805 # up to date
806 continue
807 print "ok %s" % ref
808 elif ref.startswith('refs/heads/'):
809 bmark = ref[len('refs/heads/'):]
810 p_bmarks.append((bmark, node))
811 continue
812 elif ref.startswith('refs/tags/'):
813 tag = ref[len('refs/tags/'):]
814 tag = hgref(tag)
815 author, msg = parsed_tags.get(tag, (None, None))
816 if mode == 'git':
817 if not msg:
818 msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
819 write_tag(parser.repo, tag, node, msg, author)
820 else:
821 fp = parser.repo.opener('localtags', 'a')
822 fp.write('%s %s\n' % (hghex(node), tag))
823 fp.close()
824 print "ok %s" % ref
825 else:
826 # transport-helper/fast-export bugs
827 continue
829 if peer:
830 parser.repo.push(peer, force=force_push)
832 # handle bookmarks
833 for bmark, node in p_bmarks:
834 ref = 'refs/heads/' + bmark
835 new = hghex(node)
837 if bmark in bmarks:
838 old = bmarks[bmark].hex()
839 else:
840 old = ''
842 if old == new:
843 continue
845 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
846 # fake bookmark
847 pass
848 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
849 # updated locally
850 pass
851 else:
852 print "error %s" % ref
853 continue
855 if peer:
856 rb = peer.listkeys('bookmarks')
857 old = rb.get(bmark, '')
858 if not peer.pushkey('bookmarks', bmark, old, new):
859 print "error %s" % ref
860 continue
862 print "ok %s" % ref
864 print
866 def fix_path(alias, repo, orig_url):
867 url = urlparse.urlparse(orig_url, 'file')
868 if url.scheme != 'file' or os.path.isabs(url.path):
869 return
870 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
871 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
872 subprocess.call(cmd)
874 def main(args):
875 global prefix, dirname, branches, bmarks
876 global marks, blob_marks, parsed_refs
877 global peer, mode, bad_mail, bad_name
878 global track_branches, force_push, is_tmp
879 global parsed_tags
881 alias = args[1]
882 url = args[2]
883 peer = None
885 hg_git_compat = False
886 track_branches = True
887 force_push = True
889 try:
890 if get_config('remote-hg.hg-git-compat') == 'true\n':
891 hg_git_compat = True
892 track_branches = False
893 if get_config('remote-hg.track-branches') == 'false\n':
894 track_branches = False
895 if get_config('remote-hg.force-push') == 'false\n':
896 force_push = False
897 except subprocess.CalledProcessError:
898 pass
900 if hg_git_compat:
901 mode = 'hg'
902 bad_mail = 'none@none'
903 bad_name = ''
904 else:
905 mode = 'git'
906 bad_mail = 'unknown'
907 bad_name = 'Unknown'
909 if alias[4:] == url:
910 is_tmp = True
911 alias = util.sha1(alias).hexdigest()
912 else:
913 is_tmp = False
915 gitdir = os.environ['GIT_DIR']
916 dirname = os.path.join(gitdir, 'hg', alias)
917 branches = {}
918 bmarks = {}
919 blob_marks = {}
920 parsed_refs = {}
921 marks = None
922 parsed_tags = {}
924 repo = get_repo(url, alias)
925 prefix = 'refs/hg/%s' % alias
927 if not is_tmp:
928 fix_path(alias, peer or repo, url)
930 if not os.path.exists(dirname):
931 os.makedirs(dirname)
933 marks_path = os.path.join(dirname, 'marks-hg')
934 marks = Marks(marks_path)
936 parser = Parser(repo)
937 for line in parser:
938 if parser.check('capabilities'):
939 do_capabilities(parser)
940 elif parser.check('list'):
941 do_list(parser)
942 elif parser.check('import'):
943 do_import(parser)
944 elif parser.check('export'):
945 do_export(parser)
946 else:
947 die('unhandled command: %s' % line)
948 sys.stdout.flush()
950 def bye():
951 if not marks:
952 return
953 if not is_tmp:
954 marks.store()
955 else:
956 shutil.rmtree(dirname)
958 atexit.register(bye)
959 sys.exit(main(sys.argv))