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
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 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)
325
326 return repo
327
328def rev_to_mark(rev):
329 global marks
330 return marks.from_rev(rev)
331
332def mark_to_rev(mark):
333 global marks
334 return marks.to_rev(mark)
335
336def export_ref(repo, name, kind, head):
337 global prefix, marks, mode
338
339 ename = '%s/%s' % (kind, name)
340 tip = marks.get_tip(ename)
341
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
348
349 revs = [rev for rev in revs if not marks.is_marked(rev)]
350
351 for rev in revs:
352
353 c = repo[rev]
354 (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
355 rev_branch = extra['branch']
356
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
363
364 parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
365
366 if len(parents) == 0:
367 modified = c.manifest().keys()
368 removed = []
369 else:
370 modified, removed = get_filechanges(repo, c, parents[0])
371
372 desc += '\n'
373
374 if mode == 'hg':
375 extra_msg = ''
376
377 if rev_branch != 'default':
378 extra_msg += 'branch : %s\n' % rev_branch
379
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))
387
388 for e in renames:
389 extra_msg += "rename : %s => %s\n" % e
390
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))
396
397 if extra_msg:
398 desc += '\n--HG--\n' + extra_msg
399
400 if len(parents) == 0 and rev:
401 print 'reset %s/%s' % (prefix, ename)
402
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
409
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]))
414
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
420
421 count += 1
422 if (count % 100 == 0):
423 print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
424 print "#############################################################"
425
426 # make sure the ref is updated
427 print "reset %s/%s" % (prefix, ename)
428 print "from :%u" % rev_to_mark(rev)
429 print
430
431 marks.set_tip(ename, rev)
432
433def export_tag(repo, tag):
434 export_ref(repo, tag, 'tags', repo[tag])
435
436def export_bookmark(repo, bmark):
437 head = bmarks[bmark]
438 export_ref(repo, bmark, 'bookmarks', head)
439
440def export_branch(repo, branch):
441 tip = get_branch_tip(repo, branch)
442 head = repo[tip]
443 export_ref(repo, branch, 'branches', head)
444
445def export_head(repo):
446 global g_head
447 export_ref(repo, g_head[0], 'bookmarks', g_head[1])
448
449def do_capabilities(parser):
450 global prefix, dirname
451
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
457
458 path = os.path.join(dirname, 'marks-git')
459
460 if os.path.exists(path):
461 print "*import-marks %s" % path
462 print "*export-marks %s" % path
463
464 print
465
466def 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]
472
473def get_branch_tip(repo, branch):
474 global branches
475
476 heads = branches.get(branch, None)
477 if not heads:
478 return None
479
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)
484
485 return heads[0]
486
487def list_head(repo, cur):
488 global g_head, bmarks
489
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
504
505 print "@refs/heads/%s HEAD" % head
506 g_head = (head, node)
507
508def do_list(parser):
509 global branches, bmarks, mode, track_branches
510
511 repo = parser.repo
512 for bmark, node in bookmarks.listbookmarks(repo).iteritems():
513 bmarks[bmark] = repo[node]
514
515 cur = repo.dirstate.branch()
516
517 list_head(repo, cur)
518
519 if track_branches:
520 for branch in repo.branchmap():
521 heads = repo.branchheads(branch)
522 if len(heads):
523 branches[branch] = heads
524
525 for branch in branches:
526 print "? refs/heads/branches/%s" % branch
527
528 for bmark in bmarks:
529 print "? refs/heads/%s" % bmark
530
531 for tag, node in repo.tagslist():
532 if tag == 'tip':
533 continue
534 print "? refs/tags/%s" % tag
535
536 print
537
538def do_import(parser):
539 repo = parser.repo
540
541 path = os.path.join(dirname, 'marks-git')
542
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()
548
549 tmp = encoding.encoding
550 encoding.encoding = 'utf-8'
551
552 # lets get all the import lines
553 while parser.check('import'):
554 ref = parser[1]
555
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)
567
568 parser.next()
569
570 encoding.encoding = tmp
571
572 print 'done'
573
574def parse_blob(parser):
575 global blob_marks
576
577 parser.next()
578 mark = parser.get_mark()
579 parser.next()
580 data = parser.get_data()
581 blob_marks[mark] = data
582 parser.next()
583
584def 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
591
592def parse_commit(parser):
593 global marks, blob_marks, parsed_refs
594 global mode
595
596 from_mark = merge_mark = None
597
598 ref = parser[1]
599 parser.next()
600
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')
617
618 files = {}
619
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
631
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)
643
644 repo = parser.repo
645
646 user, date, tz = author
647 extra = {}
648
649 if committer != author:
650 extra['committer'] = "%s %u %u" % committer
651
652 if from_mark:
653 p1 = repo.changelog.node(mark_to_rev(from_mark))
654 else:
655 p1 = '\0' * 20
656
657 if merge_mark:
658 p2 = repo.changelog.node(mark_to_rev(merge_mark))
659 else:
660 p2 = '\0' * 20
661
662 #
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.
665 #
666 if merge_mark:
667 get_merge_files(repo, p1, p2, files)
668
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/'):]
672
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]
687
688 ctx = context.memctx(repo, (p1, p2), data,
689 files.keys(), getfilectx,
690 user, (date, tz), extra)
691
692 tmp = encoding.encoding
693 encoding.encoding = 'utf-8'
694
695 node = repo.commitctx(ctx)
696
697 encoding.encoding = tmp
698
699 rev = repo[node].rev()
700
701 parsed_refs[ref] = node
702 marks.new_mark(rev, commit_mark)
703
704def parse_reset(parser):
705 global parsed_refs
706
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()
717
718 node = parser.repo.changelog.node(mark_to_rev(from_mark))
719 parsed_refs[ref] = node
720
721def 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()
730
731 parsed_tags[name] = (tagger, data)
732
733def write_tag(repo, tag, node, msg, author):
734 branch = repo[node].branch()
735 tip = branch_tip(repo, branch)
736 tip = repo[tip]
737
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)
746
747 p1 = tip.hex()
748 p2 = '\0' * 20
749 if not author:
750 author = (None, 0, 0)
751 user, date, tz = author
752
753 ctx = context.memctx(repo, (p1, p2), msg,
754 ['.hgtags'], getfilectx,
755 user, (date, tz), {'branch' : branch})
756
757 tmp = encoding.encoding
758 encoding.encoding = 'utf-8'
759
760 tagnode = repo.commitctx(ctx)
761
762 encoding.encoding = tmp
763
764 return tagnode
765
766def do_export(parser):
767 global parsed_refs, bmarks, peer
768
769 p_bmarks = []
770
771 parser.next()
772
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)
786
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
813
814 if peer:
815 parser.repo.push(peer, force=force_push)
816
817 # handle bookmarks
818 for bmark, node in p_bmarks:
819 ref = 'refs/heads/' + bmark
820 new = hghex(node)
821
822 if bmark in bmarks:
823 old = bmarks[bmark].hex()
824 else:
825 old = ''
826
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
836
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
843
844 print "ok %s" % ref
845
846 print
847
848def 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)
855
856def 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
862
863 alias = args[1]
864 url = args[2]
865 peer = None
866
867 hg_git_compat = False
868 track_branches = True
869 force_push = True
870
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
881
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'
890
891 if alias[4:] == url:
892 is_tmp = True
893 alias = util.sha1(alias).hexdigest()
894 else:
895 is_tmp = False
896
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 = {}
905
906 repo = get_repo(url, alias)
907 prefix = 'refs/hg/%s' % alias
908
909 if not is_tmp:
910 fix_path(alias, peer or repo, url)
911
912 if not os.path.exists(dirname):
913 os.makedirs(dirname)
914
915 marks_path = os.path.join(dirname, 'marks-hg')
916 marks = Marks(marks_path)
917
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()
931
932def bye():
933 if not marks:
934 return
935 if not is_tmp:
936 marks.store()
937 else:
938 shutil.rmtree(dirname)
939
940atexit.register(bye)
941sys.exit(main(sys.argv))