remote-hg: improve email sanitation
[git.git] / contrib / remote-helpers / git-remote-hg
blob0084709c8cd8c65c022879f8053413ff8c485cfd
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
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 if hg.islocal(url):
309 repo = hg.repository(myui, url)
310 else:
311 local_path = os.path.join(dirname, 'clone')
312 if not os.path.exists(local_path):
313 try:
314 peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
315 except:
316 die('Repository error')
317 repo = dstpeer.local()
318 else:
319 repo = hg.repository(myui, local_path)
320 try:
321 peer = hg.peer(myui, {}, url)
322 except:
323 die('Repository error')
324 repo.pull(peer, heads=None, force=True)
326 return repo
328 def rev_to_mark(rev):
329 global marks
330 return marks.from_rev(rev)
332 def mark_to_rev(mark):
333 global marks
334 return marks.to_rev(mark)
336 def export_ref(repo, name, kind, head):
337 global prefix, marks, mode
339 ename = '%s/%s' % (kind, name)
340 tip = marks.get_tip(ename)
342 # mercurial takes too much time checking this
343 if tip and tip == head.rev():
344 # nothing to do
345 return
346 revs = xrange(tip, head.rev() + 1)
347 count = 0
349 revs = [rev for rev in revs if not marks.is_marked(rev)]
351 for rev in revs:
353 c = repo[rev]
354 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
355 rev_branch = extra['branch']
357 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
358 if 'committer' in extra:
359 user, time, tz = extra['committer'].rsplit(' ', 2)
360 committer = "%s %s %s" % (user, time, gittz(int(tz)))
361 else:
362 committer = author
364 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
366 if len(parents) == 0:
367 modified = c.manifest().keys()
368 removed = []
369 else:
370 modified, removed = get_filechanges(repo, c, parents[0])
372 desc += '\n'
374 if mode == 'hg':
375 extra_msg = ''
377 if rev_branch != 'default':
378 extra_msg += 'branch : %s\n' % rev_branch
380 renames = []
381 for f in c.files():
382 if f not in c.manifest():
383 continue
384 rename = c.filectx(f).renamed()
385 if rename:
386 renames.append((rename[0], f))
388 for e in renames:
389 extra_msg += "rename : %s => %s\n" % e
391 for key, value in extra.iteritems():
392 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
393 continue
394 else:
395 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
397 if extra_msg:
398 desc += '\n--HG--\n' + extra_msg
400 if len(parents) == 0 and rev:
401 print 'reset %s/%s' % (prefix, ename)
403 print "commit %s/%s" % (prefix, ename)
404 print "mark :%d" % (marks.get_mark(rev))
405 print "author %s" % (author)
406 print "committer %s" % (committer)
407 print "data %d" % (len(desc))
408 print desc
410 if len(parents) > 0:
411 print "from :%s" % (rev_to_mark(parents[0]))
412 if len(parents) > 1:
413 print "merge :%s" % (rev_to_mark(parents[1]))
415 for f in modified:
416 export_file(c.filectx(f))
417 for f in removed:
418 print "D %s" % (fix_file_path(f))
419 print
421 count += 1
422 if (count % 100 == 0):
423 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
424 print "#############################################################"
426 # make sure the ref is updated
427 print "reset %s/%s" % (prefix, ename)
428 print "from :%u" % rev_to_mark(rev)
429 print
431 marks.set_tip(ename, rev)
433 def export_tag(repo, tag):
434 export_ref(repo, tag, 'tags', repo[tag])
436 def export_bookmark(repo, bmark):
437 head = bmarks[bmark]
438 export_ref(repo, bmark, 'bookmarks', head)
440 def export_branch(repo, branch):
441 tip = get_branch_tip(repo, branch)
442 head = repo[tip]
443 export_ref(repo, branch, 'branches', head)
445 def export_head(repo):
446 global g_head
447 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
449 def do_capabilities(parser):
450 global prefix, dirname
452 print "import"
453 print "export"
454 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
455 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
456 print "refspec refs/tags/*:%s/tags/*" % prefix
458 path = os.path.join(dirname, 'marks-git')
460 if os.path.exists(path):
461 print "*import-marks %s" % path
462 print "*export-marks %s" % path
464 print
466 def branch_tip(repo, branch):
467 # older versions of mercurial don't have this
468 if hasattr(repo, 'branchtip'):
469 return repo.branchtip(branch)
470 else:
471 return repo.branchtags()[branch]
473 def get_branch_tip(repo, branch):
474 global branches
476 heads = branches.get(branch, None)
477 if not heads:
478 return None
480 # verify there's only one head
481 if (len(heads) > 1):
482 warn("Branch '%s' has more than one head, consider merging" % branch)
483 return branch_tip(repo, branch)
485 return heads[0]
487 def list_head(repo, cur):
488 global g_head, bmarks
490 head = bookmarks.readcurrent(repo)
491 if head:
492 node = repo[head]
493 else:
494 # fake bookmark from current branch
495 head = cur
496 node = repo['.']
497 if not node:
498 node = repo['tip']
499 if not node:
500 return
501 if head == 'default':
502 head = 'master'
503 bmarks[head] = node
505 print "@refs/heads/%s HEAD" % head
506 g_head = (head, node)
508 def do_list(parser):
509 global branches, bmarks, mode, track_branches
511 repo = parser.repo
512 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
513 bmarks[bmark] = repo[node]
515 cur = repo.dirstate.branch()
517 list_head(repo, cur)
519 if track_branches:
520 for branch in repo.branchmap():
521 heads = repo.branchheads(branch)
522 if len(heads):
523 branches[branch] = heads
525 for branch in branches:
526 print "? refs/heads/branches/%s" % branch
528 for bmark in bmarks:
529 print "? refs/heads/%s" % bmark
531 for tag, node in repo.tagslist():
532 if tag == 'tip':
533 continue
534 print "? refs/tags/%s" % tag
536 print
538 def do_import(parser):
539 repo = parser.repo
541 path = os.path.join(dirname, 'marks-git')
543 print "feature done"
544 if os.path.exists(path):
545 print "feature import-marks=%s" % path
546 print "feature export-marks=%s" % path
547 sys.stdout.flush()
549 tmp = encoding.encoding
550 encoding.encoding = 'utf-8'
552 # lets get all the import lines
553 while parser.check('import'):
554 ref = parser[1]
556 if (ref == 'HEAD'):
557 export_head(repo)
558 elif ref.startswith('refs/heads/branches/'):
559 branch = ref[len('refs/heads/branches/'):]
560 export_branch(repo, branch)
561 elif ref.startswith('refs/heads/'):
562 bmark = ref[len('refs/heads/'):]
563 export_bookmark(repo, bmark)
564 elif ref.startswith('refs/tags/'):
565 tag = ref[len('refs/tags/'):]
566 export_tag(repo, tag)
568 parser.next()
570 encoding.encoding = tmp
572 print 'done'
574 def parse_blob(parser):
575 global blob_marks
577 parser.next()
578 mark = parser.get_mark()
579 parser.next()
580 data = parser.get_data()
581 blob_marks[mark] = data
582 parser.next()
584 def get_merge_files(repo, p1, p2, files):
585 for e in repo[p1].files():
586 if e not in files:
587 if e not in repo[p1].manifest():
588 continue
589 f = { 'ctx' : repo[p1][e] }
590 files[e] = f
592 def parse_commit(parser):
593 global marks, blob_marks, parsed_refs
594 global mode
596 from_mark = merge_mark = None
598 ref = parser[1]
599 parser.next()
601 commit_mark = parser.get_mark()
602 parser.next()
603 author = parser.get_author()
604 parser.next()
605 committer = parser.get_author()
606 parser.next()
607 data = parser.get_data()
608 parser.next()
609 if parser.check('from'):
610 from_mark = parser.get_mark()
611 parser.next()
612 if parser.check('merge'):
613 merge_mark = parser.get_mark()
614 parser.next()
615 if parser.check('merge'):
616 die('octopus merges are not supported yet')
618 files = {}
620 for line in parser:
621 if parser.check('M'):
622 t, m, mark_ref, path = line.split(' ', 3)
623 mark = int(mark_ref[1:])
624 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
625 elif parser.check('D'):
626 t, path = line.split(' ', 1)
627 f = { 'deleted' : True }
628 else:
629 die('Unknown file command: %s' % line)
630 files[path] = f
632 def getfilectx(repo, memctx, f):
633 of = files[f]
634 if 'deleted' in of:
635 raise IOError
636 if 'ctx' in of:
637 return of['ctx']
638 is_exec = of['mode'] == 'x'
639 is_link = of['mode'] == 'l'
640 rename = of.get('rename', None)
641 return context.memfilectx(f, of['data'],
642 is_link, is_exec, rename)
644 repo = parser.repo
646 user, date, tz = author
647 extra = {}
649 if committer != author:
650 extra['committer'] = "%s %u %u" % committer
652 if from_mark:
653 p1 = repo.changelog.node(mark_to_rev(from_mark))
654 else:
655 p1 = '\0' * 20
657 if merge_mark:
658 p2 = repo.changelog.node(mark_to_rev(merge_mark))
659 else:
660 p2 = '\0' * 20
663 # If files changed from any of the parents, hg wants to know, but in git if
664 # nothing changed from the first parent, nothing changed.
666 if merge_mark:
667 get_merge_files(repo, p1, p2, files)
669 # Check if the ref is supposed to be a named branch
670 if ref.startswith('refs/heads/branches/'):
671 extra['branch'] = ref[len('refs/heads/branches/'):]
673 if mode == 'hg':
674 i = data.find('\n--HG--\n')
675 if i >= 0:
676 tmp = data[i + len('\n--HG--\n'):].strip()
677 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
678 if k == 'rename':
679 old, new = v.split(' => ', 1)
680 files[new]['rename'] = old
681 elif k == 'branch':
682 extra[k] = v
683 elif k == 'extra':
684 ek, ev = v.split(' : ', 1)
685 extra[ek] = urllib.unquote(ev)
686 data = data[:i]
688 ctx = context.memctx(repo, (p1, p2), data,
689 files.keys(), getfilectx,
690 user, (date, tz), extra)
692 tmp = encoding.encoding
693 encoding.encoding = 'utf-8'
695 node = repo.commitctx(ctx)
697 encoding.encoding = tmp
699 rev = repo[node].rev()
701 parsed_refs[ref] = node
702 marks.new_mark(rev, commit_mark)
704 def parse_reset(parser):
705 global parsed_refs
707 ref = parser[1]
708 parser.next()
709 # ugh
710 if parser.check('commit'):
711 parse_commit(parser)
712 return
713 if not parser.check('from'):
714 return
715 from_mark = parser.get_mark()
716 parser.next()
718 node = parser.repo.changelog.node(mark_to_rev(from_mark))
719 parsed_refs[ref] = node
721 def parse_tag(parser):
722 name = parser[1]
723 parser.next()
724 from_mark = parser.get_mark()
725 parser.next()
726 tagger = parser.get_author()
727 parser.next()
728 data = parser.get_data()
729 parser.next()
731 parsed_tags[name] = (tagger, data)
733 def write_tag(repo, tag, node, msg, author):
734 branch = repo[node].branch()
735 tip = branch_tip(repo, branch)
736 tip = repo[tip]
738 def getfilectx(repo, memctx, f):
739 try:
740 fctx = tip.filectx(f)
741 data = fctx.data()
742 except error.ManifestLookupError:
743 data = ""
744 content = data + "%s %s\n" % (hghex(node), tag)
745 return context.memfilectx(f, content, False, False, None)
747 p1 = tip.hex()
748 p2 = '\0' * 20
749 if not author:
750 author = (None, 0, 0)
751 user, date, tz = author
753 ctx = context.memctx(repo, (p1, p2), msg,
754 ['.hgtags'], getfilectx,
755 user, (date, tz), {'branch' : branch})
757 tmp = encoding.encoding
758 encoding.encoding = 'utf-8'
760 tagnode = repo.commitctx(ctx)
762 encoding.encoding = tmp
764 return tagnode
766 def do_export(parser):
767 global parsed_refs, bmarks, peer
769 p_bmarks = []
771 parser.next()
773 for line in parser.each_block('done'):
774 if parser.check('blob'):
775 parse_blob(parser)
776 elif parser.check('commit'):
777 parse_commit(parser)
778 elif parser.check('reset'):
779 parse_reset(parser)
780 elif parser.check('tag'):
781 parse_tag(parser)
782 elif parser.check('feature'):
783 pass
784 else:
785 die('unhandled export command: %s' % line)
787 for ref, node in parsed_refs.iteritems():
788 if ref.startswith('refs/heads/branches'):
789 branch = ref[len('refs/heads/branches/'):]
790 if branch in branches and node in branches[branch]:
791 # up to date
792 continue
793 print "ok %s" % ref
794 elif ref.startswith('refs/heads/'):
795 bmark = ref[len('refs/heads/'):]
796 p_bmarks.append((bmark, node))
797 continue
798 elif ref.startswith('refs/tags/'):
799 tag = ref[len('refs/tags/'):]
800 author, msg = parsed_tags.get(tag, (None, None))
801 if mode == 'git':
802 if not msg:
803 msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
804 write_tag(parser.repo, tag, node, msg, author)
805 else:
806 fp = parser.repo.opener('localtags', 'a')
807 fp.write('%s %s\n' % (hghex(node), tag))
808 fp.close()
809 print "ok %s" % ref
810 else:
811 # transport-helper/fast-export bugs
812 continue
814 if peer:
815 parser.repo.push(peer, force=force_push)
817 # handle bookmarks
818 for bmark, node in p_bmarks:
819 ref = 'refs/heads/' + bmark
820 new = hghex(node)
822 if bmark in bmarks:
823 old = bmarks[bmark].hex()
824 else:
825 old = ''
827 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
828 # fake bookmark
829 pass
830 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
831 # updated locally
832 pass
833 else:
834 print "error %s" % ref
835 continue
837 if peer:
838 rb = peer.listkeys('bookmarks')
839 old = rb.get(bmark, '')
840 if not peer.pushkey('bookmarks', bmark, old, new):
841 print "error %s" % ref
842 continue
844 print "ok %s" % ref
846 print
848 def fix_path(alias, repo, orig_url):
849 url = urlparse.urlparse(orig_url, 'file')
850 if url.scheme != 'file' or os.path.isabs(url.path):
851 return
852 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
853 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
854 subprocess.call(cmd)
856 def main(args):
857 global prefix, dirname, branches, bmarks
858 global marks, blob_marks, parsed_refs
859 global peer, mode, bad_mail, bad_name
860 global track_branches, force_push, is_tmp
861 global parsed_tags
863 alias = args[1]
864 url = args[2]
865 peer = None
867 hg_git_compat = False
868 track_branches = True
869 force_push = True
871 try:
872 if get_config('remote-hg.hg-git-compat') == 'true\n':
873 hg_git_compat = True
874 track_branches = False
875 if get_config('remote-hg.track-branches') == 'false\n':
876 track_branches = False
877 if get_config('remote-hg.force-push') == 'false\n':
878 force_push = False
879 except subprocess.CalledProcessError:
880 pass
882 if hg_git_compat:
883 mode = 'hg'
884 bad_mail = 'none@none'
885 bad_name = ''
886 else:
887 mode = 'git'
888 bad_mail = 'unknown'
889 bad_name = 'Unknown'
891 if alias[4:] == url:
892 is_tmp = True
893 alias = util.sha1(alias).hexdigest()
894 else:
895 is_tmp = False
897 gitdir = os.environ['GIT_DIR']
898 dirname = os.path.join(gitdir, 'hg', alias)
899 branches = {}
900 bmarks = {}
901 blob_marks = {}
902 parsed_refs = {}
903 marks = None
904 parsed_tags = {}
906 repo = get_repo(url, alias)
907 prefix = 'refs/hg/%s' % alias
909 if not is_tmp:
910 fix_path(alias, peer or repo, url)
912 if not os.path.exists(dirname):
913 os.makedirs(dirname)
915 marks_path = os.path.join(dirname, 'marks-hg')
916 marks = Marks(marks_path)
918 parser = Parser(repo)
919 for line in parser:
920 if parser.check('capabilities'):
921 do_capabilities(parser)
922 elif parser.check('list'):
923 do_list(parser)
924 elif parser.check('import'):
925 do_import(parser)
926 elif parser.check('export'):
927 do_export(parser)
928 else:
929 die('unhandled command: %s' % line)
930 sys.stdout.flush()
932 def bye():
933 if not marks:
934 return
935 if not is_tmp:
936 marks.store()
937 else:
938 shutil.rmtree(dirname)
940 atexit.register(bye)
941 sys.exit(main(sys.argv))