remote-hg: improve node traversing
[git/gitweb.git] / contrib / remote-helpers / git-remote-hg
blob07ea104e436929395bbc9ab8ff0d0c9eccc6bcb1
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, 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, hashlib
28 # If you are not in hg-git-compat mode and want to disable the tracking of
29 # named branches:
30 # git config --global remote-hg.track-branches false
32 # If you don't want to force pushes (and thus risk creating new remote heads):
33 # git config --global remote-hg.force-push false
35 # If you want the equivalent of hg's clone/pull--insecure option:
36 # git config --global remote-hg.insecure true
38 # If you want to switch to hg-git compatibility mode:
39 # git config --global remote-hg.hg-git-compat 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 hgbin(node):
79 return hg.node.bin(node)
81 def hgref(ref):
82 return ref.replace('___', ' ')
84 def gitref(ref):
85 return ref.replace(' ', '___')
87 def get_config(config):
88 cmd = ['git', 'config', '--get', config]
89 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
90 output, _ = process.communicate()
91 return output
93 def get_config_bool(config, default=False):
94 value = get_config(config).rstrip('\n')
95 if value == "true":
96 return True
97 elif value == "false":
98 return False
99 else:
100 return default
102 class Marks:
104 def __init__(self, path):
105 self.path = path
106 self.tips = {}
107 self.marks = {}
108 self.rev_marks = {}
109 self.last_mark = 0
111 self.load()
113 def load(self):
114 if not os.path.exists(self.path):
115 return
117 tmp = json.load(open(self.path))
119 self.tips = tmp['tips']
120 self.marks = tmp['marks']
121 self.last_mark = tmp['last-mark']
123 for rev, mark in self.marks.iteritems():
124 self.rev_marks[mark] = int(rev)
126 def dict(self):
127 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
129 def store(self):
130 json.dump(self.dict(), open(self.path, 'w'))
132 def __str__(self):
133 return str(self.dict())
135 def from_rev(self, rev):
136 return self.marks[str(rev)]
138 def to_rev(self, mark):
139 return self.rev_marks[mark]
141 def next_mark(self):
142 self.last_mark += 1
143 return self.last_mark
145 def get_mark(self, rev):
146 self.last_mark += 1
147 self.marks[str(rev)] = self.last_mark
148 return self.last_mark
150 def new_mark(self, rev, mark):
151 self.marks[str(rev)] = mark
152 self.rev_marks[mark] = rev
153 self.last_mark = mark
155 def is_marked(self, rev):
156 return str(rev) in self.marks
158 def get_tip(self, branch):
159 return self.tips.get(branch, 0)
161 def set_tip(self, branch, tip):
162 self.tips[branch] = tip
164 class Parser:
166 def __init__(self, repo):
167 self.repo = repo
168 self.line = self.get_line()
170 def get_line(self):
171 return sys.stdin.readline().strip()
173 def __getitem__(self, i):
174 return self.line.split()[i]
176 def check(self, word):
177 return self.line.startswith(word)
179 def each_block(self, separator):
180 while self.line != separator:
181 yield self.line
182 self.line = self.get_line()
184 def __iter__(self):
185 return self.each_block('')
187 def next(self):
188 self.line = self.get_line()
189 if self.line == 'done':
190 self.line = None
192 def get_mark(self):
193 i = self.line.index(':') + 1
194 return int(self.line[i:])
196 def get_data(self):
197 if not self.check('data'):
198 return None
199 i = self.line.index(' ') + 1
200 size = int(self.line[i:])
201 return sys.stdin.read(size)
203 def get_author(self):
204 global bad_mail
206 ex = None
207 m = RAW_AUTHOR_RE.match(self.line)
208 if not m:
209 return None
210 _, name, email, date, tz = m.groups()
211 if name and 'ext:' in name:
212 m = re.match('^(.+?) ext:\((.+)\)$', name)
213 if m:
214 name = m.group(1)
215 ex = urllib.unquote(m.group(2))
217 if email != bad_mail:
218 if name:
219 user = '%s <%s>' % (name, email)
220 else:
221 user = '<%s>' % (email)
222 else:
223 user = name
225 if ex:
226 user += ex
228 tz = int(tz)
229 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
230 return (user, int(date), -tz)
232 def fix_file_path(path):
233 if not os.path.isabs(path):
234 return path
235 return os.path.relpath(path, '/')
237 def export_files(files):
238 global marks, filenodes
240 final = []
241 for f in files:
242 fid = node.hex(f.filenode())
244 if fid in filenodes:
245 mark = filenodes[fid]
246 else:
247 mark = marks.next_mark()
248 filenodes[fid] = mark
249 d = f.data()
251 print "blob"
252 print "mark :%u" % mark
253 print "data %d" % len(d)
254 print d
256 path = fix_file_path(f.path())
257 final.append((gitmode(f.flags()), mark, path))
259 return final
261 def get_filechanges(repo, ctx, parent):
262 modified = set()
263 added = set()
264 removed = set()
266 # load earliest manifest first for caching reasons
267 prev = parent.manifest().copy()
268 cur = ctx.manifest()
270 for fn in cur:
271 if fn in prev:
272 if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
273 modified.add(fn)
274 del prev[fn]
275 else:
276 added.add(fn)
277 removed |= set(prev.keys())
279 return added | modified, removed
281 def fixup_user_git(user):
282 name = mail = None
283 user = user.replace('"', '')
284 m = AUTHOR_RE.match(user)
285 if m:
286 name = m.group(1)
287 mail = m.group(2).strip()
288 else:
289 m = EMAIL_RE.match(user)
290 if m:
291 name = m.group(1)
292 mail = m.group(2)
293 else:
294 m = NAME_RE.match(user)
295 if m:
296 name = m.group(1).strip()
297 return (name, mail)
299 def fixup_user_hg(user):
300 def sanitize(name):
301 # stole this from hg-git
302 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
304 m = AUTHOR_HG_RE.match(user)
305 if m:
306 name = sanitize(m.group(1))
307 mail = sanitize(m.group(2))
308 ex = m.group(3)
309 if ex:
310 name += ' ext:(' + urllib.quote(ex) + ')'
311 else:
312 name = sanitize(user)
313 if '@' in user:
314 mail = name
315 else:
316 mail = None
318 return (name, mail)
320 def fixup_user(user):
321 global mode, bad_mail
323 if mode == 'git':
324 name, mail = fixup_user_git(user)
325 else:
326 name, mail = fixup_user_hg(user)
328 if not name:
329 name = bad_name
330 if not mail:
331 mail = bad_mail
333 return '%s <%s>' % (name, mail)
335 def get_repo(url, alias):
336 global dirname, peer
338 myui = ui.ui()
339 myui.setconfig('ui', 'interactive', 'off')
340 myui.fout = sys.stderr
342 if get_config_bool('remote-hg.insecure'):
343 myui.setconfig('web', 'cacerts', '')
345 extensions.loadall(myui)
347 if hg.islocal(url):
348 repo = hg.repository(myui, url)
349 if not os.path.exists(dirname):
350 os.makedirs(dirname)
351 else:
352 shared_path = os.path.join(gitdir, 'hg')
353 if not os.path.exists(shared_path):
354 try:
355 hg.clone(myui, {}, url, shared_path, update=False, pull=True)
356 except:
357 die('Repository error')
359 if not os.path.exists(dirname):
360 os.makedirs(dirname)
362 local_path = os.path.join(dirname, 'clone')
363 if not os.path.exists(local_path):
364 hg.share(myui, shared_path, local_path, update=False)
366 repo = hg.repository(myui, local_path)
367 try:
368 peer = hg.peer(myui, {}, url)
369 except:
370 die('Repository error')
371 repo.pull(peer, heads=None, force=True)
373 return repo
375 def rev_to_mark(rev):
376 global marks
377 return marks.from_rev(rev)
379 def mark_to_rev(mark):
380 global marks
381 return marks.to_rev(mark)
383 def export_ref(repo, name, kind, head):
384 global prefix, marks, mode
386 ename = '%s/%s' % (kind, name)
387 tip = marks.get_tip(ename)
389 revs = xrange(tip, head.rev() + 1)
390 count = 0
392 for rev in revs:
394 c = repo[rev]
395 node = c.node()
397 if marks.is_marked(c.hex()):
398 count += 1
399 continue
401 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(node)
402 rev_branch = extra['branch']
404 author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
405 if 'committer' in extra:
406 user, time, tz = extra['committer'].rsplit(' ', 2)
407 committer = "%s %s %s" % (user, time, gittz(int(tz)))
408 else:
409 committer = author
411 parents = [repo[p] for p in repo.changelog.parentrevs(rev) if p >= 0]
413 if len(parents) == 0:
414 modified = c.manifest().keys()
415 removed = []
416 else:
417 modified, removed = get_filechanges(repo, c, parents[0])
419 desc += '\n'
421 if mode == 'hg':
422 extra_msg = ''
424 if rev_branch != 'default':
425 extra_msg += 'branch : %s\n' % rev_branch
427 renames = []
428 for f in c.files():
429 if f not in c.manifest():
430 continue
431 rename = c.filectx(f).renamed()
432 if rename:
433 renames.append((rename[0], f))
435 for e in renames:
436 extra_msg += "rename : %s => %s\n" % e
438 for key, value in extra.iteritems():
439 if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
440 continue
441 else:
442 extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
444 if extra_msg:
445 desc += '\n--HG--\n' + extra_msg
447 if len(parents) == 0 and rev:
448 print 'reset %s/%s' % (prefix, ename)
450 modified_final = export_files(c.filectx(f) for f in modified)
452 print "commit %s/%s" % (prefix, ename)
453 print "mark :%d" % (marks.get_mark(rev))
454 print "author %s" % (author)
455 print "committer %s" % (committer)
456 print "data %d" % (len(desc))
457 print desc
459 if len(parents) > 0:
460 print "from :%s" % (rev_to_mark(parents[0].rev()))
461 if len(parents) > 1:
462 print "merge :%s" % (rev_to_mark(parents[1].rev()))
464 for f in modified_final:
465 print "M %s :%u %s" % f
466 for f in removed:
467 print "D %s" % (fix_file_path(f))
468 print
470 count += 1
471 if (count % 100 == 0):
472 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
474 # make sure the ref is updated
475 print "reset %s/%s" % (prefix, ename)
476 print "from :%u" % rev_to_mark(head.rev())
477 print
479 marks.set_tip(ename, head.rev())
481 def export_tag(repo, tag):
482 export_ref(repo, tag, 'tags', repo[hgref(tag)])
484 def export_bookmark(repo, bmark):
485 head = bmarks[hgref(bmark)]
486 export_ref(repo, bmark, 'bookmarks', head)
488 def export_branch(repo, branch):
489 tip = get_branch_tip(repo, branch)
490 head = repo[tip]
491 export_ref(repo, branch, 'branches', head)
493 def export_head(repo):
494 global g_head
495 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
497 def do_capabilities(parser):
498 global prefix, dirname
500 print "import"
501 print "export"
502 print "refspec refs/heads/branches/*:%s/branches/*" % prefix
503 print "refspec refs/heads/*:%s/bookmarks/*" % prefix
504 print "refspec refs/tags/*:%s/tags/*" % prefix
506 path = os.path.join(dirname, 'marks-git')
508 if os.path.exists(path):
509 print "*import-marks %s" % path
510 print "*export-marks %s" % path
512 print
514 def branch_tip(repo, branch):
515 # older versions of mercurial don't have this
516 if hasattr(repo, 'branchtip'):
517 return repo.branchtip(branch)
518 else:
519 return repo.branchtags()[branch]
521 def get_branch_tip(repo, branch):
522 global branches
524 heads = branches.get(hgref(branch), None)
525 if not heads:
526 return None
528 # verify there's only one head
529 if (len(heads) > 1):
530 warn("Branch '%s' has more than one head, consider merging" % branch)
531 return branch_tip(repo, hgref(branch))
533 return heads[0]
535 def list_head(repo, cur):
536 global g_head, bmarks
538 head = bookmarks.readcurrent(repo)
539 if head:
540 node = repo[head]
541 else:
542 # fake bookmark from current branch
543 head = cur
544 node = repo['.']
545 if not node:
546 node = repo['tip']
547 if not node:
548 return
549 if head == 'default':
550 head = 'master'
551 bmarks[head] = node
553 head = gitref(head)
554 print "@refs/heads/%s HEAD" % head
555 g_head = (head, node)
557 def do_list(parser):
558 global branches, bmarks, track_branches
560 repo = parser.repo
561 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
562 bmarks[bmark] = repo[node]
564 cur = repo.dirstate.branch()
566 list_head(repo, cur)
568 if track_branches:
569 for branch in repo.branchmap():
570 heads = repo.branchheads(branch)
571 if len(heads):
572 branches[branch] = heads
574 for branch in branches:
575 print "? refs/heads/branches/%s" % gitref(branch)
577 for bmark in bmarks:
578 print "? refs/heads/%s" % gitref(bmark)
580 for tag, node in repo.tagslist():
581 if tag == 'tip':
582 continue
583 print "? refs/tags/%s" % gitref(tag)
585 print
587 def do_import(parser):
588 repo = parser.repo
590 path = os.path.join(dirname, 'marks-git')
592 print "feature done"
593 if os.path.exists(path):
594 print "feature import-marks=%s" % path
595 print "feature export-marks=%s" % path
596 sys.stdout.flush()
598 tmp = encoding.encoding
599 encoding.encoding = 'utf-8'
601 # lets get all the import lines
602 while parser.check('import'):
603 ref = parser[1]
605 if (ref == 'HEAD'):
606 export_head(repo)
607 elif ref.startswith('refs/heads/branches/'):
608 branch = ref[len('refs/heads/branches/'):]
609 export_branch(repo, branch)
610 elif ref.startswith('refs/heads/'):
611 bmark = ref[len('refs/heads/'):]
612 export_bookmark(repo, bmark)
613 elif ref.startswith('refs/tags/'):
614 tag = ref[len('refs/tags/'):]
615 export_tag(repo, tag)
617 parser.next()
619 encoding.encoding = tmp
621 print 'done'
623 def parse_blob(parser):
624 global blob_marks
626 parser.next()
627 mark = parser.get_mark()
628 parser.next()
629 data = parser.get_data()
630 blob_marks[mark] = data
631 parser.next()
633 def get_merge_files(repo, p1, p2, files):
634 for e in repo[p1].files():
635 if e not in files:
636 if e not in repo[p1].manifest():
637 continue
638 f = { 'ctx' : repo[p1][e] }
639 files[e] = f
641 def parse_commit(parser):
642 global marks, blob_marks, parsed_refs
643 global mode
645 from_mark = merge_mark = None
647 ref = parser[1]
648 parser.next()
650 commit_mark = parser.get_mark()
651 parser.next()
652 author = parser.get_author()
653 parser.next()
654 committer = parser.get_author()
655 parser.next()
656 data = parser.get_data()
657 parser.next()
658 if parser.check('from'):
659 from_mark = parser.get_mark()
660 parser.next()
661 if parser.check('merge'):
662 merge_mark = parser.get_mark()
663 parser.next()
664 if parser.check('merge'):
665 die('octopus merges are not supported yet')
667 # fast-export adds an extra newline
668 if data[-1] == '\n':
669 data = data[:-1]
671 files = {}
673 for line in parser:
674 if parser.check('M'):
675 t, m, mark_ref, path = line.split(' ', 3)
676 mark = int(mark_ref[1:])
677 f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
678 elif parser.check('D'):
679 t, path = line.split(' ', 1)
680 f = { 'deleted' : True }
681 else:
682 die('Unknown file command: %s' % line)
683 files[path] = f
685 def getfilectx(repo, memctx, f):
686 of = files[f]
687 if 'deleted' in of:
688 raise IOError
689 if 'ctx' in of:
690 return of['ctx']
691 is_exec = of['mode'] == 'x'
692 is_link = of['mode'] == 'l'
693 rename = of.get('rename', None)
694 return context.memfilectx(f, of['data'],
695 is_link, is_exec, rename)
697 repo = parser.repo
699 user, date, tz = author
700 extra = {}
702 if committer != author:
703 extra['committer'] = "%s %u %u" % committer
705 if from_mark:
706 p1 = repo.changelog.node(mark_to_rev(from_mark))
707 else:
708 p1 = '\0' * 20
710 if merge_mark:
711 p2 = repo.changelog.node(mark_to_rev(merge_mark))
712 else:
713 p2 = '\0' * 20
716 # If files changed from any of the parents, hg wants to know, but in git if
717 # nothing changed from the first parent, nothing changed.
719 if merge_mark:
720 get_merge_files(repo, p1, p2, files)
722 # Check if the ref is supposed to be a named branch
723 if ref.startswith('refs/heads/branches/'):
724 branch = ref[len('refs/heads/branches/'):]
725 extra['branch'] = hgref(branch)
727 if mode == 'hg':
728 i = data.find('\n--HG--\n')
729 if i >= 0:
730 tmp = data[i + len('\n--HG--\n'):].strip()
731 for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
732 if k == 'rename':
733 old, new = v.split(' => ', 1)
734 files[new]['rename'] = old
735 elif k == 'branch':
736 extra[k] = v
737 elif k == 'extra':
738 ek, ev = v.split(' : ', 1)
739 extra[ek] = urllib.unquote(ev)
740 data = data[:i]
742 ctx = context.memctx(repo, (p1, p2), data,
743 files.keys(), getfilectx,
744 user, (date, tz), extra)
746 tmp = encoding.encoding
747 encoding.encoding = 'utf-8'
749 node = hghex(repo.commitctx(ctx))
751 encoding.encoding = tmp
753 rev = repo[node].rev()
755 parsed_refs[ref] = node
756 marks.new_mark(rev, commit_mark)
758 def parse_reset(parser):
759 global parsed_refs
761 ref = parser[1]
762 parser.next()
763 # ugh
764 if parser.check('commit'):
765 parse_commit(parser)
766 return
767 if not parser.check('from'):
768 return
769 from_mark = parser.get_mark()
770 parser.next()
772 node = parser.repo.changelog.node(mark_to_rev(from_mark))
773 parsed_refs[ref] = hghex(node)
775 def parse_tag(parser):
776 name = parser[1]
777 parser.next()
778 from_mark = parser.get_mark()
779 parser.next()
780 tagger = parser.get_author()
781 parser.next()
782 data = parser.get_data()
783 parser.next()
785 parsed_tags[name] = (tagger, data)
787 def write_tag(repo, tag, node, msg, author):
788 branch = repo[node].branch()
789 tip = branch_tip(repo, branch)
790 tip = repo[tip]
792 def getfilectx(repo, memctx, f):
793 try:
794 fctx = tip.filectx(f)
795 data = fctx.data()
796 except error.ManifestLookupError:
797 data = ""
798 content = data + "%s %s\n" % (node, tag)
799 return context.memfilectx(f, content, False, False, None)
801 p1 = tip.hex()
802 p2 = '\0' * 20
803 if not author:
804 author = (None, 0, 0)
805 user, date, tz = author
807 ctx = context.memctx(repo, (p1, p2), msg,
808 ['.hgtags'], getfilectx,
809 user, (date, tz), {'branch' : branch})
811 tmp = encoding.encoding
812 encoding.encoding = 'utf-8'
814 tagnode = repo.commitctx(ctx)
816 encoding.encoding = tmp
818 return tagnode
820 def do_export(parser):
821 global parsed_refs, bmarks, peer
823 p_bmarks = []
825 parser.next()
827 for line in parser.each_block('done'):
828 if parser.check('blob'):
829 parse_blob(parser)
830 elif parser.check('commit'):
831 parse_commit(parser)
832 elif parser.check('reset'):
833 parse_reset(parser)
834 elif parser.check('tag'):
835 parse_tag(parser)
836 elif parser.check('feature'):
837 pass
838 else:
839 die('unhandled export command: %s' % line)
841 for ref, node in parsed_refs.iteritems():
842 bnode = hgbin(node)
843 if ref.startswith('refs/heads/branches'):
844 branch = ref[len('refs/heads/branches/'):]
845 if branch in branches and bnode in branches[branch]:
846 # up to date
847 continue
848 print "ok %s" % ref
849 elif ref.startswith('refs/heads/'):
850 bmark = ref[len('refs/heads/'):]
851 p_bmarks.append((bmark, node))
852 continue
853 elif ref.startswith('refs/tags/'):
854 tag = ref[len('refs/tags/'):]
855 tag = hgref(tag)
856 author, msg = parsed_tags.get(tag, (None, None))
857 if mode == 'git':
858 if not msg:
859 msg = 'Added tag %s for changeset %s' % (tag, node[:12]);
860 write_tag(parser.repo, tag, node, msg, author)
861 else:
862 fp = parser.repo.opener('localtags', 'a')
863 fp.write('%s %s\n' % (node, tag))
864 fp.close()
865 print "ok %s" % ref
866 else:
867 # transport-helper/fast-export bugs
868 continue
870 if peer:
871 parser.repo.push(peer, force=force_push, newbranch=True)
872 remote_bmarks = peer.listkeys('bookmarks')
874 # handle bookmarks
875 for bmark, node in p_bmarks:
876 ref = 'refs/heads/' + bmark
877 new = node
879 if bmark in bmarks:
880 old = bmarks[bmark].hex()
881 else:
882 old = ''
884 if old == new:
885 continue
887 if bmark == 'master' and 'master' not in parser.repo._bookmarks:
888 # fake bookmark
889 print "ok %s" % ref
890 continue
891 elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
892 # updated locally
893 pass
894 else:
895 print "error %s" % ref
896 continue
898 if peer:
899 old = remote_bmarks.get(bmark, '')
900 if not peer.pushkey('bookmarks', bmark, old, new):
901 print "error %s" % ref
902 continue
904 print "ok %s" % ref
906 print
908 def fix_path(alias, repo, orig_url):
909 url = urlparse.urlparse(orig_url, 'file')
910 if url.scheme != 'file' or os.path.isabs(url.path):
911 return
912 abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
913 cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
914 subprocess.call(cmd)
916 def main(args):
917 global prefix, gitdir, dirname, branches, bmarks
918 global marks, blob_marks, parsed_refs
919 global peer, mode, bad_mail, bad_name
920 global track_branches, force_push, is_tmp
921 global parsed_tags
922 global filenodes
924 alias = args[1]
925 url = args[2]
926 peer = None
928 hg_git_compat = get_config_bool('remote-hg.hg-git-compat')
929 track_branches = get_config_bool('remote-hg.track-branches', True)
930 force_push = get_config_bool('remote-hg.force-push')
932 if hg_git_compat:
933 mode = 'hg'
934 bad_mail = 'none@none'
935 bad_name = ''
936 else:
937 mode = 'git'
938 bad_mail = 'unknown'
939 bad_name = 'Unknown'
941 if alias[4:] == url:
942 is_tmp = True
943 alias = hashlib.sha1(alias).hexdigest()
944 else:
945 is_tmp = False
947 gitdir = os.environ['GIT_DIR']
948 dirname = os.path.join(gitdir, 'hg', alias)
949 branches = {}
950 bmarks = {}
951 blob_marks = {}
952 parsed_refs = {}
953 marks = None
954 parsed_tags = {}
955 filenodes = {}
957 repo = get_repo(url, alias)
958 prefix = 'refs/hg/%s' % alias
960 if not is_tmp:
961 fix_path(alias, peer or repo, url)
963 marks_path = os.path.join(dirname, 'marks-hg')
964 marks = Marks(marks_path)
966 if sys.platform == 'win32':
967 import msvcrt
968 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
970 parser = Parser(repo)
971 for line in parser:
972 if parser.check('capabilities'):
973 do_capabilities(parser)
974 elif parser.check('list'):
975 do_list(parser)
976 elif parser.check('import'):
977 do_import(parser)
978 elif parser.check('export'):
979 do_export(parser)
980 else:
981 die('unhandled command: %s' % line)
982 sys.stdout.flush()
984 def bye():
985 if not marks:
986 return
987 if not is_tmp:
988 marks.store()
989 else:
990 shutil.rmtree(dirname)
992 atexit.register(bye)
993 sys.exit(main(sys.argv))