remote-hg: add support for schemes extension
[git.git] / contrib / remote-helpers / git-remote-hg
blob4f6c7b7d891e96659d81a3f3512f09512b71f0f9
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 get_config(config):
79 cmd = ['git', 'config', '--get', config]
80 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
81 output, _ = process.communicate()
82 return output
84 class Marks:
86 def __init__(self, path):
87 self.path = path
88 self.tips = {}
89 self.marks = {}
90 self.rev_marks = {}
91 self.last_mark = 0
93 self.load()
95 def load(self):
96 if not os.path.exists(self.path):
97 return
99 tmp = json.load(open(self.path))
101 self.tips = tmp['tips']
102 self.marks = tmp['marks']
103 self.last_mark = tmp['last-mark']
105 for rev, mark in self.marks.iteritems():
106 self.rev_marks[mark] = int(rev)
108 def dict(self):
109 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
111 def store(self):
112 json.dump(self.dict(), open(self.path, 'w'))
114 def __str__(self):
115 return str(self.dict())
117 def from_rev(self, rev):
118 return self.marks[str(rev)]
120 def to_rev(self, mark):
121 return self.rev_marks[mark]
123 def get_mark(self, rev):
124 self.last_mark += 1
125 self.marks[str(rev)] = self.last_mark
126 return self.last_mark
128 def new_mark(self, rev, mark):
129 self.marks[str(rev)] = mark
130 self.rev_marks[mark] = rev
131 self.last_mark = mark
133 def is_marked(self, rev):
134 return str(rev) in self.marks
136 def get_tip(self, branch):
137 return self.tips.get(branch, 0)
139 def set_tip(self, branch, tip):
140 self.tips[branch] = tip
142 class Parser:
144 def __init__(self, repo):
145 self.repo = repo
146 self.line = self.get_line()
148 def get_line(self):
149 return sys.stdin.readline().strip()
151 def __getitem__(self, i):
152 return self.line.split()[i]
154 def check(self, word):
155 return self.line.startswith(word)
157 def each_block(self, separator):
158 while self.line != separator:
159 yield self.line
160 self.line = self.get_line()
162 def __iter__(self):
163 return self.each_block('')
165 def next(self):
166 self.line = self.get_line()
167 if self.line == 'done':
168 self.line = None
170 def get_mark(self):
171 i = self.line.index(':') + 1
172 return int(self.line[i:])
174 def get_data(self):
175 if not self.check('data'):
176 return None
177 i = self.line.index(' ') + 1
178 size = int(self.line[i:])
179 return sys.stdin.read(size)
181 def get_author(self):
182 global bad_mail
184 ex = None
185 m = RAW_AUTHOR_RE.match(self.line)
186 if not m:
187 return None
188 _, name, email, date, tz = m.groups()
189 if name and 'ext:' in name:
190 m = re.match('^(.+?) ext:\((.+)\)$', name)
191 if m:
192 name = m.group(1)
193 ex = urllib.unquote(m.group(2))
195 if email != bad_mail:
196 if name:
197 user = '%s <%s>' % (name, email)
198 else:
199 user = '<%s>' % (email)
200 else:
201 user = name
203 if ex:
204 user += ex
206 tz = int(tz)
207 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
208 return (user, int(date), -tz)
210 def fix_file_path(path):
211 if not os.path.isabs(path):
212 return path
213 return os.path.relpath(path, '/')
215 def export_file(fc):
216 d = fc.data()
217 path = fix_file_path(fc.path())
218 print "M %s inline %s" % (gitmode(fc.flags()), path)
219 print "data %d" % len(d)
220 print d
222 def get_filechanges(repo, ctx, parent):
223 modified = set()
224 added = set()
225 removed = set()
227 cur = ctx.manifest()
228 prev = repo[parent].manifest().copy()
230 for fn in cur:
231 if fn in prev:
232 if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
233 modified.add(fn)
234 del prev[fn]
235 else:
236 added.add(fn)
237 removed |= set(prev.keys())
239 return added | modified, removed
241 def fixup_user_git(user):
242 name = mail = None
243 user = user.replace('"', '')
244 m = AUTHOR_RE.match(user)
245 if m:
246 name = m.group(1)
247 mail = m.group(2).strip()
248 else:
249 m = EMAIL_RE.match(user)
250 if m:
251 name = m.group(1)
252 mail = m.group(2)
253 else:
254 m = NAME_RE.match(user)
255 if m:
256 name = m.group(1).strip()
257 return (name, mail)
259 def fixup_user_hg(user):
260 def sanitize(name):
261 # stole this from hg-git
262 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
264 m = AUTHOR_HG_RE.match(user)
265 if m:
266 name = sanitize(m.group(1))
267 mail = sanitize(m.group(2))
268 ex = m.group(3)
269 if ex:
270 name += ' ext:(' + urllib.quote(ex) + ')'
271 else:
272 name = sanitize(user)
273 if '@' in user:
274 mail = name
275 else:
276 mail = None
278 return (name, mail)
280 def fixup_user(user):
281 global mode, bad_mail
283 if mode == 'git':
284 name, mail = fixup_user_git(user)
285 else:
286 name, mail = fixup_user_hg(user)
288 if not name:
289 name = bad_name
290 if not mail:
291 mail = bad_mail
293 return '%s <%s>' % (name, mail)
295 def get_repo(url, alias):
296 global dirname, peer
298 myui = ui.ui()
299 myui.setconfig('ui', 'interactive', 'off')
300 myui.fout = sys.stderr
302 try:
303 if get_config('remote-hg.insecure') == 'true\n':
304 myui.setconfig('web', 'cacerts', '')
305 except subprocess.CalledProcessError:
306 pass
308 try:
309 mod = extensions.load(myui, 'hgext.schemes', None)
310 mod.extsetup(myui)
311 except ImportError:
312 pass
314 if hg.islocal(url):
315 repo = hg.repository(myui, url)
316 else:
317 local_path = os.path.join(dirname, 'clone')
318 if not os.path.exists(local_path):
319 try:
320 peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
321 except:
322 die('Repository error')
323 repo = dstpeer.local()
324 else:
325 repo = hg.repository(myui, local_path)
326 try:
327 peer = hg.peer(myui, {}, url)
328 except:
329 die('Repository error')
330 repo.pull(peer, heads=None, force=True)
332 return repo
334 def rev_to_mark(rev):
335 global marks
336 return marks.from_rev(rev)
338 def mark_to_rev(mark):
339 global marks
340 return marks.to_rev(mark)
342 def export_ref(repo, name, kind, head):
343 global prefix, marks, mode
345 ename = '%s/%s' % (kind, name)
346 tip = marks.get_tip(ename)
348 # mercurial takes too much time checking this
349 if tip and tip == head.rev():
350 # nothing to do
351 return
352 revs = xrange(tip, head.rev() + 1)
353 count = 0
355 revs = [rev for rev in revs if not marks.is_marked(rev)]
357 for rev in revs:
359 c = repo[rev]
360 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
361 rev_branch = extra['branch']
363 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
364 if 'committer' in extra:
365 user, time, tz = extra['committer'].rsplit(' ', 2)
366 committer = "%s %s %s" % (user, time, gittz(int(tz)))
367 else:
368 committer = author
370 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
372 if len(parents) == 0:
373 modified = c.manifest().keys()
374 removed = []
375 else:
376 modified, removed = get_filechanges(repo, c, parents[0])
378 desc += '\n'
380 if mode == 'hg':
381 extra_msg = ''
383 if rev_branch != 'default':
384 extra_msg += 'branch : %s\n' % rev_branch
386 renames = []
387 for f in c.files():
388 if f not in c.manifest():
389 continue
390 rename = c.filectx(f).renamed()
391 if rename:
392 renames.append((rename[0], f))
394 for e in renames:
395 extra_msg += "rename : %s => %s\n" % e
397 for key, value in extra.iteritems():
398 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
399 continue
400 else:
401 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
403 if extra_msg:
404 desc += '\n--HG--\n' + extra_msg
406 if len(parents) == 0 and rev:
407 print 'reset %s/%s' % (prefix, ename)
409 print "commit %s/%s" % (prefix, ename)
410 print "mark :%d" % (marks.get_mark(rev))
411 print "author %s" % (author)
412 print "committer %s" % (committer)
413 print "data %d" % (len(desc))
414 print desc
416 if len(parents) > 0:
417 print "from :%s" % (rev_to_mark(parents[0]))
418 if len(parents) > 1:
419 print "merge :%s" % (rev_to_mark(parents[1]))
421 for f in modified:
422 export_file(c.filectx(f))
423 for f in removed:
424 print "D %s" % (fix_file_path(f))
425 print
427 count += 1
428 if (count % 100 == 0):
429 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
430 print "#############################################################"
432 # make sure the ref is updated
433 print "reset %s/%s" % (prefix, ename)
434 print "from :%u" % rev_to_mark(rev)
435 print
437 marks.set_tip(ename, rev)
439 def export_tag(repo, tag):
440 export_ref(repo, tag, 'tags', repo[tag])
442 def export_bookmark(repo, bmark):
443 head = bmarks[bmark]
444 export_ref(repo, bmark, 'bookmarks', head)
446 def export_branch(repo, branch):
447 tip = get_branch_tip(repo, branch)
448 head = repo[tip]
449 export_ref(repo, branch, 'branches', head)
451 def export_head(repo):
452 global g_head
453 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
455 def do_capabilities(parser):
456 global prefix, dirname
458 print "import"
459 print "export"
460 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
461 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
462 print "refspec refs/tags/*:%s/tags/*" % prefix
464 path = os.path.join(dirname, 'marks-git')
466 if os.path.exists(path):
467 print "*import-marks %s" % path
468 print "*export-marks %s" % path
470 print
472 def branch_tip(repo, branch):
473 # older versions of mercurial don't have this
474 if hasattr(repo, 'branchtip'):
475 return repo.branchtip(branch)
476 else:
477 return repo.branchtags()[branch]
479 def get_branch_tip(repo, branch):
480 global branches
482 heads = branches.get(branch, None)
483 if not heads:
484 return None
486 # verify there's only one head
487 if (len(heads) > 1):
488 warn("Branch '%s' has more than one head, consider merging" % branch)
489 return branch_tip(repo, branch)
491 return heads[0]
493 def list_head(repo, cur):
494 global g_head, bmarks
496 head = bookmarks.readcurrent(repo)
497 if head:
498 node = repo[head]
499 else:
500 # fake bookmark from current branch
501 head = cur
502 node = repo['.']
503 if not node:
504 node = repo['tip']
505 if not node:
506 return
507 if head == 'default':
508 head = 'master'
509 bmarks[head] = node
511 print "@refs/heads/%s HEAD" % head
512 g_head = (head, node)
514 def do_list(parser):
515 global branches, bmarks, mode, track_branches
517 repo = parser.repo
518 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
519 bmarks[bmark] = repo[node]
521 cur = repo.dirstate.branch()
523 list_head(repo, cur)
525 if track_branches:
526 for branch in repo.branchmap():
527 heads = repo.branchheads(branch)
528 if len(heads):
529 branches[branch] = heads
531 for branch in branches:
532 print "? refs/heads/branches/%s" % branch
534 for bmark in bmarks:
535 print "? refs/heads/%s" % bmark
537 for tag, node in repo.tagslist():
538 if tag == 'tip':
539 continue
540 print "? refs/tags/%s" % tag
542 print
544 def do_import(parser):
545 repo = parser.repo
547 path = os.path.join(dirname, 'marks-git')
549 print "feature done"
550 if os.path.exists(path):
551 print "feature import-marks=%s" % path
552 print "feature export-marks=%s" % path
553 sys.stdout.flush()
555 tmp = encoding.encoding
556 encoding.encoding = 'utf-8'
558 # lets get all the import lines
559 while parser.check('import'):
560 ref = parser[1]
562 if (ref == 'HEAD'):
563 export_head(repo)
564 elif ref.startswith('refs/heads/branches/'):
565 branch = ref[len('refs/heads/branches/'):]
566 export_branch(repo, branch)
567 elif ref.startswith('refs/heads/'):
568 bmark = ref[len('refs/heads/'):]
569 export_bookmark(repo, bmark)
570 elif ref.startswith('refs/tags/'):
571 tag = ref[len('refs/tags/'):]
572 export_tag(repo, tag)
574 parser.next()
576 encoding.encoding = tmp
578 print 'done'
580 def parse_blob(parser):
581 global blob_marks
583 parser.next()
584 mark = parser.get_mark()
585 parser.next()
586 data = parser.get_data()
587 blob_marks[mark] = data
588 parser.next()
590 def get_merge_files(repo, p1, p2, files):
591 for e in repo[p1].files():
592 if e not in files:
593 if e not in repo[p1].manifest():
594 continue
595 f = { 'ctx' : repo[p1][e] }
596 files[e] = f
598 def parse_commit(parser):
599 global marks, blob_marks, parsed_refs
600 global mode
602 from_mark = merge_mark = None
604 ref = parser[1]
605 parser.next()
607 commit_mark = parser.get_mark()
608 parser.next()
609 author = parser.get_author()
610 parser.next()
611 committer = parser.get_author()
612 parser.next()
613 data = parser.get_data()
614 parser.next()
615 if parser.check('from'):
616 from_mark = parser.get_mark()
617 parser.next()
618 if parser.check('merge'):
619 merge_mark = parser.get_mark()
620 parser.next()
621 if parser.check('merge'):
622 die('octopus merges are not supported yet')
624 files = {}
626 for line in parser:
627 if parser.check('M'):
628 t, m, mark_ref, path = line.split(' ', 3)
629 mark = int(mark_ref[1:])
630 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
631 elif parser.check('D'):
632 t, path = line.split(' ', 1)
633 f = { 'deleted' : True }
634 else:
635 die('Unknown file command: %s' % line)
636 files[path] = f
638 def getfilectx(repo, memctx, f):
639 of = files[f]
640 if 'deleted' in of:
641 raise IOError
642 if 'ctx' in of:
643 return of['ctx']
644 is_exec = of['mode'] == 'x'
645 is_link = of['mode'] == 'l'
646 rename = of.get('rename', None)
647 return context.memfilectx(f, of['data'],
648 is_link, is_exec, rename)
650 repo = parser.repo
652 user, date, tz = author
653 extra = {}
655 if committer != author:
656 extra['committer'] = "%s %u %u" % committer
658 if from_mark:
659 p1 = repo.changelog.node(mark_to_rev(from_mark))
660 else:
661 p1 = '\0' * 20
663 if merge_mark:
664 p2 = repo.changelog.node(mark_to_rev(merge_mark))
665 else:
666 p2 = '\0' * 20
669 # If files changed from any of the parents, hg wants to know, but in git if
670 # nothing changed from the first parent, nothing changed.
672 if merge_mark:
673 get_merge_files(repo, p1, p2, files)
675 # Check if the ref is supposed to be a named branch
676 if ref.startswith('refs/heads/branches/'):
677 extra['branch'] = ref[len('refs/heads/branches/'):]
679 if mode == 'hg':
680 i = data.find('\n--HG--\n')
681 if i >= 0:
682 tmp = data[i + len('\n--HG--\n'):].strip()
683 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
684 if k == 'rename':
685 old, new = v.split(' => ', 1)
686 files[new]['rename'] = old
687 elif k == 'branch':
688 extra[k] = v
689 elif k == 'extra':
690 ek, ev = v.split(' : ', 1)
691 extra[ek] = urllib.unquote(ev)
692 data = data[:i]
694 ctx = context.memctx(repo, (p1, p2), data,
695 files.keys(), getfilectx,
696 user, (date, tz), extra)
698 tmp = encoding.encoding
699 encoding.encoding = 'utf-8'
701 node = repo.commitctx(ctx)
703 encoding.encoding = tmp
705 rev = repo[node].rev()
707 parsed_refs[ref] = node
708 marks.new_mark(rev, commit_mark)
710 def parse_reset(parser):
711 global parsed_refs
713 ref = parser[1]
714 parser.next()
715 # ugh
716 if parser.check('commit'):
717 parse_commit(parser)
718 return
719 if not parser.check('from'):
720 return
721 from_mark = parser.get_mark()
722 parser.next()
724 node = parser.repo.changelog.node(mark_to_rev(from_mark))
725 parsed_refs[ref] = node
727 def parse_tag(parser):
728 name = parser[1]
729 parser.next()
730 from_mark = parser.get_mark()
731 parser.next()
732 tagger = parser.get_author()
733 parser.next()
734 data = parser.get_data()
735 parser.next()
737 parsed_tags[name] = (tagger, data)
739 def write_tag(repo, tag, node, msg, author):
740 branch = repo[node].branch()
741 tip = branch_tip(repo, branch)
742 tip = repo[tip]
744 def getfilectx(repo, memctx, f):
745 try:
746 fctx = tip.filectx(f)
747 data = fctx.data()
748 except error.ManifestLookupError:
749 data = ""
750 content = data + "%s %s\n" % (hghex(node), tag)
751 return context.memfilectx(f, content, False, False, None)
753 p1 = tip.hex()
754 p2 = '\0' * 20
755 if not author:
756 author = (None, 0, 0)
757 user, date, tz = author
759 ctx = context.memctx(repo, (p1, p2), msg,
760 ['.hgtags'], getfilectx,
761 user, (date, tz), {'branch' : branch})
763 tmp = encoding.encoding
764 encoding.encoding = 'utf-8'
766 tagnode = repo.commitctx(ctx)
768 encoding.encoding = tmp
770 return tagnode
772 def do_export(parser):
773 global parsed_refs, bmarks, peer
775 p_bmarks = []
777 parser.next()
779 for line in parser.each_block('done'):
780 if parser.check('blob'):
781 parse_blob(parser)
782 elif parser.check('commit'):
783 parse_commit(parser)
784 elif parser.check('reset'):
785 parse_reset(parser)
786 elif parser.check('tag'):
787 parse_tag(parser)
788 elif parser.check('feature'):
789 pass
790 else:
791 die('unhandled export command: %s' % line)
793 for ref, node in parsed_refs.iteritems():
794 if ref.startswith('refs/heads/branches'):
795 branch = ref[len('refs/heads/branches/'):]
796 if branch in branches and node in branches[branch]:
797 # up to date
798 continue
799 print "ok %s" % ref
800 elif ref.startswith('refs/heads/'):
801 bmark = ref[len('refs/heads/'):]
802 p_bmarks.append((bmark, node))
803 continue
804 elif ref.startswith('refs/tags/'):
805 tag = ref[len('refs/tags/'):]
806 author, msg = parsed_tags.get(tag, (None, None))
807 if mode == 'git':
808 if not msg:
809 msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
810 write_tag(parser.repo, tag, node, msg, author)
811 else:
812 fp = parser.repo.opener('localtags', 'a')
813 fp.write('%s %s\n' % (hghex(node), tag))
814 fp.close()
815 print "ok %s" % ref
816 else:
817 # transport-helper/fast-export bugs
818 continue
820 if peer:
821 parser.repo.push(peer, force=force_push)
823 # handle bookmarks
824 for bmark, node in p_bmarks:
825 ref = 'refs/heads/' + bmark
826 new = hghex(node)
828 if bmark in bmarks:
829 old = bmarks[bmark].hex()
830 else:
831 old = ''
833 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
834 # fake bookmark
835 pass
836 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
837 # updated locally
838 pass
839 else:
840 print "error %s" % ref
841 continue
843 if peer:
844 rb = peer.listkeys('bookmarks')
845 old = rb.get(bmark, '')
846 if not peer.pushkey('bookmarks', bmark, old, new):
847 print "error %s" % ref
848 continue
850 print "ok %s" % ref
852 print
854 def fix_path(alias, repo, orig_url):
855 url = urlparse.urlparse(orig_url, 'file')
856 if url.scheme != 'file' or os.path.isabs(url.path):
857 return
858 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
859 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
860 subprocess.call(cmd)
862 def main(args):
863 global prefix, dirname, branches, bmarks
864 global marks, blob_marks, parsed_refs
865 global peer, mode, bad_mail, bad_name
866 global track_branches, force_push, is_tmp
867 global parsed_tags
869 alias = args[1]
870 url = args[2]
871 peer = None
873 hg_git_compat = False
874 track_branches = True
875 force_push = True
877 try:
878 if get_config('remote-hg.hg-git-compat') == 'true\n':
879 hg_git_compat = True
880 track_branches = False
881 if get_config('remote-hg.track-branches') == 'false\n':
882 track_branches = False
883 if get_config('remote-hg.force-push') == 'false\n':
884 force_push = False
885 except subprocess.CalledProcessError:
886 pass
888 if hg_git_compat:
889 mode = 'hg'
890 bad_mail = 'none@none'
891 bad_name = ''
892 else:
893 mode = 'git'
894 bad_mail = 'unknown'
895 bad_name = 'Unknown'
897 if alias[4:] == url:
898 is_tmp = True
899 alias = util.sha1(alias).hexdigest()
900 else:
901 is_tmp = False
903 gitdir = os.environ['GIT_DIR']
904 dirname = os.path.join(gitdir, 'hg', alias)
905 branches = {}
906 bmarks = {}
907 blob_marks = {}
908 parsed_refs = {}
909 marks = None
910 parsed_tags = {}
912 repo = get_repo(url, alias)
913 prefix = 'refs/hg/%s' % alias
915 if not is_tmp:
916 fix_path(alias, peer or repo, url)
918 if not os.path.exists(dirname):
919 os.makedirs(dirname)
921 marks_path = os.path.join(dirname, 'marks-hg')
922 marks = Marks(marks_path)
924 parser = Parser(repo)
925 for line in parser:
926 if parser.check('capabilities'):
927 do_capabilities(parser)
928 elif parser.check('list'):
929 do_list(parser)
930 elif parser.check('import'):
931 do_import(parser)
932 elif parser.check('export'):
933 do_export(parser)
934 else:
935 die('unhandled command: %s' % line)
936 sys.stdout.flush()
938 def bye():
939 if not marks:
940 return
941 if not is_tmp:
942 marks.store()
943 else:
944 shutil.rmtree(dirname)
946 atexit.register(bye)
947 sys.exit(main(sys.argv))