1#!/usr/bin/env python
2#
3# Copyright (c) 2012 Felipe Contreras
4#
5
6# Inspired by Rocco Rutte's hg-fast-export
7
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/
11#
12# For remote repositories a local clone is stored in
13# "$GIT_DIR/hg/origin/clone/.hg/".
14
15from mercurial import hg, ui, bookmarks, context, util, encoding, node, error, extensions
16
17import re
18import sys
19import os
20import json
21import shutil
22import subprocess
23import urllib
24import atexit
25import urlparse
26
27#
28# If you want to switch to hg-git compatibility mode:
29# git config --global remote-hg.hg-git-compat true
30#
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
34#
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
37#
38# If you want the equivalent of hg's clone/pull--insecure option:
39# git config remote-hg.insecure true
40#
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.
45#
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.
50#
51
52NAME_RE = re.compile('^([^<>]+)')
53AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
54EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
55AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
56RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
57
58def die(msg, *args):
59 sys.stderr.write('ERROR: %s\n' % (msg % args))
60 sys.exit(1)
61
62def warn(msg, *args):
63 sys.stderr.write('WARNING: %s\n' % (msg % args))
64
65def gitmode(flags):
66 return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'
67
68def gittz(tz):
69 return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
70
71def hgmode(mode):
72 m = { '100755': 'x', '120000': 'l' }
73 return m.get(mode, '')
74
75def hghex(node):
76 return hg.node.hex(node)
77
78def get_config(config):
79 cmd = ['git', 'config', '--get', config]
80 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
81 output, _ = process.communicate()
82 return output
83
84class Marks:
85
86 def __init__(self, path):
87 self.path = path
88 self.tips = {}
89 self.marks = {}
90 self.rev_marks = {}
91 self.last_mark = 0
92
93 self.load()
94
95 def load(self):
96 if not os.path.exists(self.path):
97 return
98
99 tmp = json.load(open(self.path))
100
101 self.tips = tmp['tips']
102 self.marks = tmp['marks']
103 self.last_mark = tmp['last-mark']
104
105 for rev, mark in self.marks.iteritems():
106 self.rev_marks[mark] = int(rev)
107
108 def dict(self):
109 return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
110
111 def store(self):
112 json.dump(self.dict(), open(self.path, 'w'))
113
114 def __str__(self):
115 return str(self.dict())
116
117 def from_rev(self, rev):
118 return self.marks[str(rev)]
119
120 def to_rev(self, mark):
121 return self.rev_marks[mark]
122
123 def get_mark(self, rev):
124 self.last_mark += 1
125 self.marks[str(rev)] = self.last_mark
126 return self.last_mark
127
128 def new_mark(self, rev, mark):
129 self.marks[str(rev)] = mark
130 self.rev_marks[mark] = rev
131 self.last_mark = mark
132
133 def is_marked(self, rev):
134 return str(rev) in self.marks
135
136 def get_tip(self, branch):
137 return self.tips.get(branch, 0)
138
139 def set_tip(self, branch, tip):
140 self.tips[branch] = tip
141
142class Parser:
143
144 def __init__(self, repo):
145 self.repo = repo
146 self.line = self.get_line()
147
148 def get_line(self):
149 return sys.stdin.readline().strip()
150
151 def __getitem__(self, i):
152 return self.line.split()[i]
153
154 def check(self, word):
155 return self.line.startswith(word)
156
157 def each_block(self, separator):
158 while self.line != separator:
159 yield self.line
160 self.line = self.get_line()
161
162 def __iter__(self):
163 return self.each_block('')
164
165 def next(self):
166 self.line = self.get_line()
167 if self.line == 'done':
168 self.line = None
169
170 def get_mark(self):
171 i = self.line.index(':') + 1
172 return int(self.line[i:])
173
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)
180
181 def get_author(self):
182 global bad_mail
183
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))
194
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
202
203 if ex:
204 user += ex
205
206 tz = int(tz)
207 tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
208 return (user, int(date), -tz)
209
210def fix_file_path(path):
211 if not os.path.isabs(path):
212 return path
213 return os.path.relpath(path, '/')
214
215def 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
221
222def get_filechanges(repo, ctx, parent):
223 modified = set()
224 added = set()
225 removed = set()
226
227 cur = ctx.manifest()
228 prev = repo[parent].manifest().copy()
229
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())
238
239 return added | modified, removed
240
241def 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)
258
259def fixup_user_hg(user):
260 def sanitize(name):
261 # stole this from hg-git
262 return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
263
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
277
278 return (name, mail)
279
280def fixup_user(user):
281 global mode, bad_mail
282
283 if mode == 'git':
284 name, mail = fixup_user_git(user)
285 else:
286 name, mail = fixup_user_hg(user)
287
288 if not name:
289 name = bad_name
290 if not mail:
291 mail = bad_mail
292
293 return '%s <%s>' % (name, mail)
294
295def get_repo(url, alias):
296 global dirname, peer
297
298 myui = ui.ui()
299 myui.setconfig('ui', 'interactive', 'off')
300 myui.fout = sys.stderr
301
302 try:
303 if get_config('remote-hg.insecure') == 'true\n':
304 myui.setconfig('web', 'cacerts', '')
305 except subprocess.CalledProcessError:
306 pass
307
308 try:
309 mod = extensions.load(myui, 'hgext.schemes', None)
310 mod.extsetup(myui)
311 except ImportError:
312 pass
313
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)
331
332 return repo
333
334def rev_to_mark(rev):
335 global marks
336 return marks.from_rev(rev)
337
338def mark_to_rev(mark):
339 global marks
340 return marks.to_rev(mark)
341
342def export_ref(repo, name, kind, head):
343 global prefix, marks, mode
344
345 ename = '%s/%s' % (kind, name)
346 tip = marks.get_tip(ename)
347
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
354
355 revs = [rev for rev in revs if not marks.is_marked(rev)]
356
357 for rev in revs:
358
359 c = repo[rev]
360 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
361 rev_branch = extra['branch']
362
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
369
370 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
371
372 if len(parents) == 0:
373 modified = c.manifest().keys()
374 removed = []
375 else:
376 modified, removed = get_filechanges(repo, c, parents[0])
377
378 desc += '\n'
379
380 if mode == 'hg':
381 extra_msg = ''
382
383 if rev_branch != 'default':
384 extra_msg += 'branch : %s\n' % rev_branch
385
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))
393
394 for e in renames:
395 extra_msg += "rename : %s => %s\n" % e
396
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))
402
403 if extra_msg:
404 desc += '\n--HG--\n' + extra_msg
405
406 if len(parents) == 0 and rev:
407 print 'reset %s/%s' % (prefix, ename)
408
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
415
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]))
420
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
426
427 count += 1
428 if (count % 100 == 0):
429 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
430 print "#############################################################"
431
432 # make sure the ref is updated
433 print "reset %s/%s" % (prefix, ename)
434 print "from :%u" % rev_to_mark(rev)
435 print
436
437 marks.set_tip(ename, rev)
438
439def export_tag(repo, tag):
440 export_ref(repo, tag, 'tags', repo[tag])
441
442def export_bookmark(repo, bmark):
443 head = bmarks[bmark]
444 export_ref(repo, bmark, 'bookmarks', head)
445
446def export_branch(repo, branch):
447 tip = get_branch_tip(repo, branch)
448 head = repo[tip]
449 export_ref(repo, branch, 'branches', head)
450
451def export_head(repo):
452 global g_head
453 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
454
455def do_capabilities(parser):
456 global prefix, dirname
457
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
463
464 path = os.path.join(dirname, 'marks-git')
465
466 if os.path.exists(path):
467 print "*import-marks %s" % path
468 print "*export-marks %s" % path
469
470 print
471
472def 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]
478
479def get_branch_tip(repo, branch):
480 global branches
481
482 heads = branches.get(branch, None)
483 if not heads:
484 return None
485
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)
490
491 return heads[0]
492
493def list_head(repo, cur):
494 global g_head, bmarks
495
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
510
511 print "@refs/heads/%s HEAD" % head
512 g_head = (head, node)
513
514def do_list(parser):
515 global branches, bmarks, mode, track_branches
516
517 repo = parser.repo
518 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
519 bmarks[bmark] = repo[node]
520
521 cur = repo.dirstate.branch()
522
523 list_head(repo, cur)
524
525 if track_branches:
526 for branch in repo.branchmap():
527 heads = repo.branchheads(branch)
528 if len(heads):
529 branches[branch] = heads
530
531 for branch in branches:
532 print "? refs/heads/branches/%s" % branch
533
534 for bmark in bmarks:
535 print "? refs/heads/%s" % bmark
536
537 for tag, node in repo.tagslist():
538 if tag == 'tip':
539 continue
540 print "? refs/tags/%s" % tag
541
542 print
543
544def do_import(parser):
545 repo = parser.repo
546
547 path = os.path.join(dirname, 'marks-git')
548
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()
554
555 tmp = encoding.encoding
556 encoding.encoding = 'utf-8'
557
558 # lets get all the import lines
559 while parser.check('import'):
560 ref = parser[1]
561
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)
573
574 parser.next()
575
576 encoding.encoding = tmp
577
578 print 'done'
579
580def parse_blob(parser):
581 global blob_marks
582
583 parser.next()
584 mark = parser.get_mark()
585 parser.next()
586 data = parser.get_data()
587 blob_marks[mark] = data
588 parser.next()
589
590def 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
597
598def parse_commit(parser):
599 global marks, blob_marks, parsed_refs
600 global mode
601
602 from_mark = merge_mark = None
603
604 ref = parser[1]
605 parser.next()
606
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')
623
624 files = {}
625
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
637
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)
649
650 repo = parser.repo
651
652 user, date, tz = author
653 extra = {}
654
655 if committer != author:
656 extra['committer'] = "%s %u %u" % committer
657
658 if from_mark:
659 p1 = repo.changelog.node(mark_to_rev(from_mark))
660 else:
661 p1 = '\0' * 20
662
663 if merge_mark:
664 p2 = repo.changelog.node(mark_to_rev(merge_mark))
665 else:
666 p2 = '\0' * 20
667
668 #
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.
671 #
672 if merge_mark:
673 get_merge_files(repo, p1, p2, files)
674
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/'):]
678
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]
693
694 ctx = context.memctx(repo, (p1, p2), data,
695 files.keys(), getfilectx,
696 user, (date, tz), extra)
697
698 tmp = encoding.encoding
699 encoding.encoding = 'utf-8'
700
701 node = repo.commitctx(ctx)
702
703 encoding.encoding = tmp
704
705 rev = repo[node].rev()
706
707 parsed_refs[ref] = node
708 marks.new_mark(rev, commit_mark)
709
710def parse_reset(parser):
711 global parsed_refs
712
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()
723
724 node = parser.repo.changelog.node(mark_to_rev(from_mark))
725 parsed_refs[ref] = node
726
727def 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()
736
737 parsed_tags[name] = (tagger, data)
738
739def write_tag(repo, tag, node, msg, author):
740 branch = repo[node].branch()
741 tip = branch_tip(repo, branch)
742 tip = repo[tip]
743
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)
752
753 p1 = tip.hex()
754 p2 = '\0' * 20
755 if not author:
756 author = (None, 0, 0)
757 user, date, tz = author
758
759 ctx = context.memctx(repo, (p1, p2), msg,
760 ['.hgtags'], getfilectx,
761 user, (date, tz), {'branch' : branch})
762
763 tmp = encoding.encoding
764 encoding.encoding = 'utf-8'
765
766 tagnode = repo.commitctx(ctx)
767
768 encoding.encoding = tmp
769
770 return tagnode
771
772def do_export(parser):
773 global parsed_refs, bmarks, peer
774
775 p_bmarks = []
776
777 parser.next()
778
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)
792
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
819
820 if peer:
821 parser.repo.push(peer, force=force_push)
822
823 # handle bookmarks
824 for bmark, node in p_bmarks:
825 ref = 'refs/heads/' + bmark
826 new = hghex(node)
827
828 if bmark in bmarks:
829 old = bmarks[bmark].hex()
830 else:
831 old = ''
832
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
842
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
849
850 print "ok %s" % ref
851
852 print
853
854def 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)
861
862def 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
868
869 alias = args[1]
870 url = args[2]
871 peer = None
872
873 hg_git_compat = False
874 track_branches = True
875 force_push = True
876
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
887
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'
896
897 if alias[4:] == url:
898 is_tmp = True
899 alias = util.sha1(alias).hexdigest()
900 else:
901 is_tmp = False
902
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 = {}
911
912 repo = get_repo(url, alias)
913 prefix = 'refs/hg/%s' % alias
914
915 if not is_tmp:
916 fix_path(alias, peer or repo, url)
917
918 if not os.path.exists(dirname):
919 os.makedirs(dirname)
920
921 marks_path = os.path.join(dirname, 'marks-hg')
922 marks = Marks(marks_path)
923
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()
937
938def bye():
939 if not marks:
940 return
941 if not is_tmp:
942 marks.store()
943 else:
944 shutil.rmtree(dirname)
945
946atexit.register(bye)
947sys.exit(main(sys.argv))