remote-hg: use marks instead of inlined files
[git/mjg.git] / contrib / remote-helpers / git-remote-hg
blobd0e552c0a24ba1ef50ba5e0948f2f83bf750329c
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 next_mark(self):
130 self.last_mark += 1
131 return self.last_mark
133 def get_mark(self, rev):
134 self.last_mark += 1
135 self.marks[str(rev)] = self.last_mark
136 return self.last_mark
138 def new_mark(self, rev, mark):
139 self.marks[str(rev)] = mark
140 self.rev_marks[mark] = rev
141 self.last_mark = mark
143 def is_marked(self, rev):
144 return str(rev) in self.marks
146 def get_tip(self, branch):
147 return self.tips.get(branch, 0)
149 def set_tip(self, branch, tip):
150 self.tips[branch] = tip
152 class Parser:
154 def __init__(self, repo):
155 self.repo = repo
156 self.line = self.get_line()
158 def get_line(self):
159 return sys.stdin.readline().strip()
161 def __getitem__(self, i):
162 return self.line.split()[i]
164 def check(self, word):
165 return self.line.startswith(word)
167 def each_block(self, separator):
168 while self.line != separator:
169 yield self.line
170 self.line = self.get_line()
172 def __iter__(self):
173 return self.each_block('')
175 def next(self):
176 self.line = self.get_line()
177 if self.line == 'done':
178 self.line = None
180 def get_mark(self):
181 i = self.line.index(':') + 1
182 return int(self.line[i:])
184 def get_data(self):
185 if not self.check('data'):
186 return None
187 i = self.line.index(' ') + 1
188 size = int(self.line[i:])
189 return sys.stdin.read(size)
191 def get_author(self):
192 global bad_mail
194 ex = None
195 m = RAW_AUTHOR_RE.match(self.line)
196 if not m:
197 return None
198 _, name, email, date, tz = m.groups()
199 if name and 'ext:' in name:
200 m = re.match('^(.+?) ext:\((.+)\)$', name)
201 if m:
202 name = m.group(1)
203 ex = urllib.unquote(m.group(2))
205 if email != bad_mail:
206 if name:
207 user = '%s <%s>' % (name, email)
208 else:
209 user = '<%s>' % (email)
210 else:
211 user = name
213 if ex:
214 user += ex
216 tz = int(tz)
217 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
218 return (user, int(date), -tz)
220 def fix_file_path(path):
221 if not os.path.isabs(path):
222 return path
223 return os.path.relpath(path, '/')
225 def export_files(files):
226 global marks, filenodes
228 final = []
229 for f in files:
230 fid = node.hex(f.filenode())
232 if fid in filenodes:
233 mark = filenodes[fid]
234 else:
235 mark = marks.next_mark()
236 filenodes[fid] = mark
237 d = f.data()
239 print "blob"
240 print "mark :%u" % mark
241 print "data %d" % len(d)
242 print d
244 path = fix_file_path(f.path())
245 final.append((gitmode(f.flags()), mark, path))
247 return final
249 def get_filechanges(repo, ctx, parent):
250 modified = set()
251 added = set()
252 removed = set()
254 # load earliest manifest first for caching reasons
255 prev = repo[parent].manifest().copy()
256 cur = ctx.manifest()
258 for fn in cur:
259 if fn in prev:
260 if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
261 modified.add(fn)
262 del prev[fn]
263 else:
264 added.add(fn)
265 removed |= set(prev.keys())
267 return added | modified, removed
269 def fixup_user_git(user):
270 name = mail = None
271 user = user.replace('"', '')
272 m = AUTHOR_RE.match(user)
273 if m:
274 name = m.group(1)
275 mail = m.group(2).strip()
276 else:
277 m = EMAIL_RE.match(user)
278 if m:
279 name = m.group(1)
280 mail = m.group(2)
281 else:
282 m = NAME_RE.match(user)
283 if m:
284 name = m.group(1).strip()
285 return (name, mail)
287 def fixup_user_hg(user):
288 def sanitize(name):
289 # stole this from hg-git
290 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
292 m = AUTHOR_HG_RE.match(user)
293 if m:
294 name = sanitize(m.group(1))
295 mail = sanitize(m.group(2))
296 ex = m.group(3)
297 if ex:
298 name += ' ext:(' + urllib.quote(ex) + ')'
299 else:
300 name = sanitize(user)
301 if '@' in user:
302 mail = name
303 else:
304 mail = None
306 return (name, mail)
308 def fixup_user(user):
309 global mode, bad_mail
311 if mode == 'git':
312 name, mail = fixup_user_git(user)
313 else:
314 name, mail = fixup_user_hg(user)
316 if not name:
317 name = bad_name
318 if not mail:
319 mail = bad_mail
321 return '%s <%s>' % (name, mail)
323 def get_repo(url, alias):
324 global dirname, peer
326 myui = ui.ui()
327 myui.setconfig('ui', 'interactive', 'off')
328 myui.fout = sys.stderr
330 try:
331 if get_config('remote-hg.insecure') == 'true\n':
332 myui.setconfig('web', 'cacerts', '')
333 except subprocess.CalledProcessError:
334 pass
336 try:
337 mod = extensions.load(myui, 'hgext.schemes', None)
338 mod.extsetup(myui)
339 except ImportError:
340 pass
342 if hg.islocal(url):
343 repo = hg.repository(myui, url)
344 else:
345 local_path = os.path.join(dirname, 'clone')
346 if not os.path.exists(local_path):
347 try:
348 peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
349 except:
350 die('Repository error')
351 repo = dstpeer.local()
352 else:
353 repo = hg.repository(myui, local_path)
354 try:
355 peer = hg.peer(myui, {}, url)
356 except:
357 die('Repository error')
358 repo.pull(peer, heads=None, force=True)
360 return repo
362 def rev_to_mark(rev):
363 global marks
364 return marks.from_rev(rev)
366 def mark_to_rev(mark):
367 global marks
368 return marks.to_rev(mark)
370 def export_ref(repo, name, kind, head):
371 global prefix, marks, mode
373 ename = '%s/%s' % (kind, name)
374 tip = marks.get_tip(ename)
376 # mercurial takes too much time checking this
377 if tip and tip == head.rev():
378 # nothing to do
379 return
380 revs = xrange(tip, head.rev() + 1)
381 count = 0
383 revs = [rev for rev in revs if not marks.is_marked(rev)]
385 for rev in revs:
387 c = repo[rev]
388 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
389 rev_branch = extra['branch']
391 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
392 if 'committer' in extra:
393 user, time, tz = extra['committer'].rsplit(' ', 2)
394 committer = "%s %s %s" % (user, time, gittz(int(tz)))
395 else:
396 committer = author
398 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
400 if len(parents) == 0:
401 modified = c.manifest().keys()
402 removed = []
403 else:
404 modified, removed = get_filechanges(repo, c, parents[0])
406 desc += '\n'
408 if mode == 'hg':
409 extra_msg = ''
411 if rev_branch != 'default':
412 extra_msg += 'branch : %s\n' % rev_branch
414 renames = []
415 for f in c.files():
416 if f not in c.manifest():
417 continue
418 rename = c.filectx(f).renamed()
419 if rename:
420 renames.append((rename[0], f))
422 for e in renames:
423 extra_msg += "rename : %s => %s\n" % e
425 for key, value in extra.iteritems():
426 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
427 continue
428 else:
429 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
431 if extra_msg:
432 desc += '\n--HG--\n' + extra_msg
434 if len(parents) == 0 and rev:
435 print 'reset %s/%s' % (prefix, ename)
437 modified_final = export_files(c.filectx(f) for f in modified)
439 print "commit %s/%s" % (prefix, ename)
440 print "mark :%d" % (marks.get_mark(rev))
441 print "author %s" % (author)
442 print "committer %s" % (committer)
443 print "data %d" % (len(desc))
444 print desc
446 if len(parents) > 0:
447 print "from :%s" % (rev_to_mark(parents[0]))
448 if len(parents) > 1:
449 print "merge :%s" % (rev_to_mark(parents[1]))
451 for f in modified_final:
452 print "M %s :%u %s" % f
453 for f in removed:
454 print "D %s" % (fix_file_path(f))
455 print
457 count += 1
458 if (count % 100 == 0):
459 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
460 print "#############################################################"
462 # make sure the ref is updated
463 print "reset %s/%s" % (prefix, ename)
464 print "from :%u" % rev_to_mark(rev)
465 print
467 marks.set_tip(ename, rev)
469 def export_tag(repo, tag):
470 export_ref(repo, tag, 'tags', repo[hgref(tag)])
472 def export_bookmark(repo, bmark):
473 head = bmarks[hgref(bmark)]
474 export_ref(repo, bmark, 'bookmarks', head)
476 def export_branch(repo, branch):
477 tip = get_branch_tip(repo, branch)
478 head = repo[tip]
479 export_ref(repo, branch, 'branches', head)
481 def export_head(repo):
482 global g_head
483 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
485 def do_capabilities(parser):
486 global prefix, dirname
488 print "import"
489 print "export"
490 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
491 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
492 print "refspec refs/tags/*:%s/tags/*" % prefix
494 path = os.path.join(dirname, 'marks-git')
496 if os.path.exists(path):
497 print "*import-marks %s" % path
498 print "*export-marks %s" % path
500 print
502 def branch_tip(repo, branch):
503 # older versions of mercurial don't have this
504 if hasattr(repo, 'branchtip'):
505 return repo.branchtip(branch)
506 else:
507 return repo.branchtags()[branch]
509 def get_branch_tip(repo, branch):
510 global branches
512 heads = branches.get(hgref(branch), None)
513 if not heads:
514 return None
516 # verify there's only one head
517 if (len(heads) > 1):
518 warn("Branch '%s' has more than one head, consider merging" % branch)
519 return branch_tip(repo, hgref(branch))
521 return heads[0]
523 def list_head(repo, cur):
524 global g_head, bmarks
526 head = bookmarks.readcurrent(repo)
527 if head:
528 node = repo[head]
529 else:
530 # fake bookmark from current branch
531 head = cur
532 node = repo['.']
533 if not node:
534 node = repo['tip']
535 if not node:
536 return
537 if head == 'default':
538 head = 'master'
539 bmarks[head] = node
541 head = gitref(head)
542 print "@refs/heads/%s HEAD" % head
543 g_head = (head, node)
545 def do_list(parser):
546 global branches, bmarks, mode, track_branches
548 repo = parser.repo
549 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
550 bmarks[bmark] = repo[node]
552 cur = repo.dirstate.branch()
554 list_head(repo, cur)
556 if track_branches:
557 for branch in repo.branchmap():
558 heads = repo.branchheads(branch)
559 if len(heads):
560 branches[branch] = heads
562 for branch in branches:
563 print "? refs/heads/branches/%s" % gitref(branch)
565 for bmark in bmarks:
566 print "? refs/heads/%s" % gitref(bmark)
568 for tag, node in repo.tagslist():
569 if tag == 'tip':
570 continue
571 print "? refs/tags/%s" % gitref(tag)
573 print
575 def do_import(parser):
576 repo = parser.repo
578 path = os.path.join(dirname, 'marks-git')
580 print "feature done"
581 if os.path.exists(path):
582 print "feature import-marks=%s" % path
583 print "feature export-marks=%s" % path
584 sys.stdout.flush()
586 tmp = encoding.encoding
587 encoding.encoding = 'utf-8'
589 # lets get all the import lines
590 while parser.check('import'):
591 ref = parser[1]
593 if (ref == 'HEAD'):
594 export_head(repo)
595 elif ref.startswith('refs/heads/branches/'):
596 branch = ref[len('refs/heads/branches/'):]
597 export_branch(repo, branch)
598 elif ref.startswith('refs/heads/'):
599 bmark = ref[len('refs/heads/'):]
600 export_bookmark(repo, bmark)
601 elif ref.startswith('refs/tags/'):
602 tag = ref[len('refs/tags/'):]
603 export_tag(repo, tag)
605 parser.next()
607 encoding.encoding = tmp
609 print 'done'
611 def parse_blob(parser):
612 global blob_marks
614 parser.next()
615 mark = parser.get_mark()
616 parser.next()
617 data = parser.get_data()
618 blob_marks[mark] = data
619 parser.next()
621 def get_merge_files(repo, p1, p2, files):
622 for e in repo[p1].files():
623 if e not in files:
624 if e not in repo[p1].manifest():
625 continue
626 f = { 'ctx' : repo[p1][e] }
627 files[e] = f
629 def parse_commit(parser):
630 global marks, blob_marks, parsed_refs
631 global mode
633 from_mark = merge_mark = None
635 ref = parser[1]
636 parser.next()
638 commit_mark = parser.get_mark()
639 parser.next()
640 author = parser.get_author()
641 parser.next()
642 committer = parser.get_author()
643 parser.next()
644 data = parser.get_data()
645 parser.next()
646 if parser.check('from'):
647 from_mark = parser.get_mark()
648 parser.next()
649 if parser.check('merge'):
650 merge_mark = parser.get_mark()
651 parser.next()
652 if parser.check('merge'):
653 die('octopus merges are not supported yet')
655 files = {}
657 for line in parser:
658 if parser.check('M'):
659 t, m, mark_ref, path = line.split(' ', 3)
660 mark = int(mark_ref[1:])
661 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
662 elif parser.check('D'):
663 t, path = line.split(' ', 1)
664 f = { 'deleted' : True }
665 else:
666 die('Unknown file command: %s' % line)
667 files[path] = f
669 def getfilectx(repo, memctx, f):
670 of = files[f]
671 if 'deleted' in of:
672 raise IOError
673 if 'ctx' in of:
674 return of['ctx']
675 is_exec = of['mode'] == 'x'
676 is_link = of['mode'] == 'l'
677 rename = of.get('rename', None)
678 return context.memfilectx(f, of['data'],
679 is_link, is_exec, rename)
681 repo = parser.repo
683 user, date, tz = author
684 extra = {}
686 if committer != author:
687 extra['committer'] = "%s %u %u" % committer
689 if from_mark:
690 p1 = repo.changelog.node(mark_to_rev(from_mark))
691 else:
692 p1 = '\0' * 20
694 if merge_mark:
695 p2 = repo.changelog.node(mark_to_rev(merge_mark))
696 else:
697 p2 = '\0' * 20
700 # If files changed from any of the parents, hg wants to know, but in git if
701 # nothing changed from the first parent, nothing changed.
703 if merge_mark:
704 get_merge_files(repo, p1, p2, files)
706 # Check if the ref is supposed to be a named branch
707 if ref.startswith('refs/heads/branches/'):
708 branch = ref[len('refs/heads/branches/'):]
709 extra['branch'] = hgref(branch)
711 if mode == 'hg':
712 i = data.find('\n--HG--\n')
713 if i >= 0:
714 tmp = data[i + len('\n--HG--\n'):].strip()
715 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
716 if k == 'rename':
717 old, new = v.split(' => ', 1)
718 files[new]['rename'] = old
719 elif k == 'branch':
720 extra[k] = v
721 elif k == 'extra':
722 ek, ev = v.split(' : ', 1)
723 extra[ek] = urllib.unquote(ev)
724 data = data[:i]
726 ctx = context.memctx(repo, (p1, p2), data,
727 files.keys(), getfilectx,
728 user, (date, tz), extra)
730 tmp = encoding.encoding
731 encoding.encoding = 'utf-8'
733 node = repo.commitctx(ctx)
735 encoding.encoding = tmp
737 rev = repo[node].rev()
739 parsed_refs[ref] = node
740 marks.new_mark(rev, commit_mark)
742 def parse_reset(parser):
743 global parsed_refs
745 ref = parser[1]
746 parser.next()
747 # ugh
748 if parser.check('commit'):
749 parse_commit(parser)
750 return
751 if not parser.check('from'):
752 return
753 from_mark = parser.get_mark()
754 parser.next()
756 node = parser.repo.changelog.node(mark_to_rev(from_mark))
757 parsed_refs[ref] = node
759 def parse_tag(parser):
760 name = parser[1]
761 parser.next()
762 from_mark = parser.get_mark()
763 parser.next()
764 tagger = parser.get_author()
765 parser.next()
766 data = parser.get_data()
767 parser.next()
769 parsed_tags[name] = (tagger, data)
771 def write_tag(repo, tag, node, msg, author):
772 branch = repo[node].branch()
773 tip = branch_tip(repo, branch)
774 tip = repo[tip]
776 def getfilectx(repo, memctx, f):
777 try:
778 fctx = tip.filectx(f)
779 data = fctx.data()
780 except error.ManifestLookupError:
781 data = ""
782 content = data + "%s %s\n" % (hghex(node), tag)
783 return context.memfilectx(f, content, False, False, None)
785 p1 = tip.hex()
786 p2 = '\0' * 20
787 if not author:
788 author = (None, 0, 0)
789 user, date, tz = author
791 ctx = context.memctx(repo, (p1, p2), msg,
792 ['.hgtags'], getfilectx,
793 user, (date, tz), {'branch' : branch})
795 tmp = encoding.encoding
796 encoding.encoding = 'utf-8'
798 tagnode = repo.commitctx(ctx)
800 encoding.encoding = tmp
802 return tagnode
804 def do_export(parser):
805 global parsed_refs, bmarks, peer
807 p_bmarks = []
809 parser.next()
811 for line in parser.each_block('done'):
812 if parser.check('blob'):
813 parse_blob(parser)
814 elif parser.check('commit'):
815 parse_commit(parser)
816 elif parser.check('reset'):
817 parse_reset(parser)
818 elif parser.check('tag'):
819 parse_tag(parser)
820 elif parser.check('feature'):
821 pass
822 else:
823 die('unhandled export command: %s' % line)
825 for ref, node in parsed_refs.iteritems():
826 if ref.startswith('refs/heads/branches'):
827 branch = ref[len('refs/heads/branches/'):]
828 if branch in branches and node in branches[branch]:
829 # up to date
830 continue
831 print "ok %s" % ref
832 elif ref.startswith('refs/heads/'):
833 bmark = ref[len('refs/heads/'):]
834 p_bmarks.append((bmark, node))
835 continue
836 elif ref.startswith('refs/tags/'):
837 tag = ref[len('refs/tags/'):]
838 tag = hgref(tag)
839 author, msg = parsed_tags.get(tag, (None, None))
840 if mode == 'git':
841 if not msg:
842 msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
843 write_tag(parser.repo, tag, node, msg, author)
844 else:
845 fp = parser.repo.opener('localtags', 'a')
846 fp.write('%s %s\n' % (hghex(node), tag))
847 fp.close()
848 print "ok %s" % ref
849 else:
850 # transport-helper/fast-export bugs
851 continue
853 if peer:
854 parser.repo.push(peer, force=force_push)
856 # handle bookmarks
857 for bmark, node in p_bmarks:
858 ref = 'refs/heads/' + bmark
859 new = hghex(node)
861 if bmark in bmarks:
862 old = bmarks[bmark].hex()
863 else:
864 old = ''
866 if old == new:
867 continue
869 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
870 # fake bookmark
871 pass
872 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
873 # updated locally
874 pass
875 else:
876 print "error %s" % ref
877 continue
879 if peer:
880 rb = peer.listkeys('bookmarks')
881 old = rb.get(bmark, '')
882 if not peer.pushkey('bookmarks', bmark, old, new):
883 print "error %s" % ref
884 continue
886 print "ok %s" % ref
888 print
890 def fix_path(alias, repo, orig_url):
891 url = urlparse.urlparse(orig_url, 'file')
892 if url.scheme != 'file' or os.path.isabs(url.path):
893 return
894 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
895 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
896 subprocess.call(cmd)
898 def main(args):
899 global prefix, dirname, branches, bmarks
900 global marks, blob_marks, parsed_refs
901 global peer, mode, bad_mail, bad_name
902 global track_branches, force_push, is_tmp
903 global parsed_tags
904 global filenodes
906 alias = args[1]
907 url = args[2]
908 peer = None
910 hg_git_compat = False
911 track_branches = True
912 force_push = True
914 try:
915 if get_config('remote-hg.hg-git-compat') == 'true\n':
916 hg_git_compat = True
917 track_branches = False
918 if get_config('remote-hg.track-branches') == 'false\n':
919 track_branches = False
920 if get_config('remote-hg.force-push') == 'false\n':
921 force_push = False
922 except subprocess.CalledProcessError:
923 pass
925 if hg_git_compat:
926 mode = 'hg'
927 bad_mail = 'none@none'
928 bad_name = ''
929 else:
930 mode = 'git'
931 bad_mail = 'unknown'
932 bad_name = 'Unknown'
934 if alias[4:] == url:
935 is_tmp = True
936 alias = util.sha1(alias).hexdigest()
937 else:
938 is_tmp = False
940 gitdir = os.environ['GIT_DIR']
941 dirname = os.path.join(gitdir, 'hg', alias)
942 branches = {}
943 bmarks = {}
944 blob_marks = {}
945 parsed_refs = {}
946 marks = None
947 parsed_tags = {}
948 filenodes = {}
950 repo = get_repo(url, alias)
951 prefix = 'refs/hg/%s' % alias
953 if not is_tmp:
954 fix_path(alias, peer or repo, url)
956 if not os.path.exists(dirname):
957 os.makedirs(dirname)
959 marks_path = os.path.join(dirname, 'marks-hg')
960 marks = Marks(marks_path)
962 parser = Parser(repo)
963 for line in parser:
964 if parser.check('capabilities'):
965 do_capabilities(parser)
966 elif parser.check('list'):
967 do_list(parser)
968 elif parser.check('import'):
969 do_import(parser)
970 elif parser.check('export'):
971 do_export(parser)
972 else:
973 die('unhandled command: %s' % line)
974 sys.stdout.flush()
976 def bye():
977 if not marks:
978 return
979 if not is_tmp:
980 marks.store()
981 else:
982 shutil.rmtree(dirname)
984 atexit.register(bye)
985 sys.exit(main(sys.argv))