8
8
# Just copy to your ~/bin, or anywhere in your $PATH.
9
9
# Then you can clone with:
10
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/".
11
14
12
- from mercurial import hg , ui , bookmarks , context , util , encoding
15
+ from mercurial import hg , ui , bookmarks , context , util , encoding , node , error
13
16
14
17
import re
15
18
import sys
@@ -18,11 +21,22 @@ import json
18
21
import shutil
19
22
import subprocess
20
23
import urllib
24
+ import atexit
21
25
22
26
#
23
27
# If you want to switch to hg-git compatibility mode:
24
28
# git config --global remote-hg.hg-git-compat true
25
29
#
30
+ # If you are not in hg-git-compat mode and want to disable the tracking of
31
+ # named branches:
32
+ # git config --global remote-hg.track-branches false
33
+ #
34
+ # If you don't want to force pushes (and thus risk creating new remote heads):
35
+ # git config --global remote-hg.force-push false
36
+ #
37
+ # If you want the equivalent of hg's clone/pull--insecure option:
38
+ # git config remote-hg.insecure true
39
+ #
26
40
# git:
27
41
# Sensible defaults for git.
28
42
# hg bookmarks are exported as git branches, hg branches are prefixed
@@ -56,6 +70,9 @@ def hgmode(mode):
56
70
m = { '100755' : 'x' , '120000' : 'l' }
57
71
return m .get (mode , '' )
58
72
73
+ def hghex (node ):
74
+ return hg .node .hex (node )
75
+
59
76
def get_config (config ):
60
77
cmd = ['git' , 'config' , '--get' , config ]
61
78
process = subprocess .Popen (cmd , stdout = subprocess .PIPE )
@@ -188,9 +205,15 @@ class Parser:
188
205
tz = ((tz / 100 ) * 3600 ) + ((tz % 100 ) * 60 )
189
206
return (user , int (date ), - tz )
190
207
208
+ def fix_file_path (path ):
209
+ if not os .path .isabs (path ):
210
+ return path
211
+ return os .path .relpath (path , '/' )
212
+
191
213
def export_file (fc ):
192
214
d = fc .data ()
193
- print "M %s inline %s" % (gitmode (fc .flags ()), fc .path ())
215
+ path = fix_file_path (fc .path ())
216
+ print "M %s inline %s" % (gitmode (fc .flags ()), path )
194
217
print "data %d" % len (d )
195
218
print d
196
219
@@ -267,17 +290,30 @@ def get_repo(url, alias):
267
290
268
291
myui = ui .ui ()
269
292
myui .setconfig ('ui' , 'interactive' , 'off' )
293
+ myui .fout = sys .stderr
294
+
295
+ try :
296
+ if get_config ('remote-hg.insecure' ) == 'true\n ' :
297
+ myui .setconfig ('web' , 'cacerts' , '' )
298
+ except subprocess .CalledProcessError :
299
+ pass
270
300
271
301
if hg .islocal (url ):
272
302
repo = hg .repository (myui , url )
273
303
else :
274
304
local_path = os .path .join (dirname , 'clone' )
275
305
if not os .path .exists (local_path ):
276
- peer , dstpeer = hg .clone (myui , {}, url , local_path , update = False , pull = True )
306
+ try :
307
+ peer , dstpeer = hg .clone (myui , {}, url , local_path , update = True , pull = True )
308
+ except :
309
+ die ('Repository error' )
277
310
repo = dstpeer .local ()
278
311
else :
279
312
repo = hg .repository (myui , local_path )
280
- peer = hg .peer (myui , {}, url )
313
+ try :
314
+ peer = hg .peer (myui , {}, url )
315
+ except :
316
+ die ('Repository error' )
281
317
repo .pull (peer , heads = None , force = True )
282
318
283
319
return repo
@@ -372,7 +408,7 @@ def export_ref(repo, name, kind, head):
372
408
for f in modified :
373
409
export_file (c .filectx (f ))
374
410
for f in removed :
375
- print "D %s" % (f )
411
+ print "D %s" % (fix_file_path ( f ) )
376
412
print
377
413
378
414
count += 1
@@ -532,7 +568,6 @@ def parse_blob(parser):
532
568
data = parser .get_data ()
533
569
blob_marks [mark ] = data
534
570
parser .next ()
535
- return
536
571
537
572
def get_merge_files (repo , p1 , p2 , files ):
538
573
for e in repo [p1 ].files ():
@@ -543,7 +578,7 @@ def get_merge_files(repo, p1, p2, files):
543
578
files [e ] = f
544
579
545
580
def parse_commit (parser ):
546
- global marks , blob_marks , bmarks , parsed_refs
581
+ global marks , blob_marks , parsed_refs
547
582
global mode
548
583
549
584
from_mark = merge_mark = None
@@ -576,7 +611,7 @@ def parse_commit(parser):
576
611
mark = int (mark_ref [1 :])
577
612
f = { 'mode' : hgmode (m ), 'data' : blob_marks [mark ] }
578
613
elif parser .check ('D' ):
579
- t , path = line .split (' ' )
614
+ t , path = line .split (' ' , 1 )
580
615
f = { 'deleted' : True }
581
616
else :
582
617
die ('Unknown file command: %s' % line )
@@ -619,11 +654,15 @@ def parse_commit(parser):
619
654
if merge_mark :
620
655
get_merge_files (repo , p1 , p2 , files )
621
656
657
+ # Check if the ref is supposed to be a named branch
658
+ if ref .startswith ('refs/heads/branches/' ):
659
+ extra ['branch' ] = ref [len ('refs/heads/branches/' ):]
660
+
622
661
if mode == 'hg' :
623
662
i = data .find ('\n --HG--\n ' )
624
663
if i >= 0 :
625
664
tmp = data [i + len ('\n --HG--\n ' ):].strip ()
626
- for k , v in [e .split (' : ' ) for e in tmp .split ('\n ' )]:
665
+ for k , v in [e .split (' : ' , 1 ) for e in tmp .split ('\n ' )]:
627
666
if k == 'rename' :
628
667
old , new = v .split (' => ' , 1 )
629
668
files [new ]['rename' ] = old
@@ -648,10 +687,11 @@ def parse_commit(parser):
648
687
rev = repo [node ].rev ()
649
688
650
689
parsed_refs [ref ] = node
651
-
652
690
marks .new_mark (rev , commit_mark )
653
691
654
692
def parse_reset (parser ):
693
+ global parsed_refs
694
+
655
695
ref = parser [1 ]
656
696
parser .next ()
657
697
# ugh
@@ -681,6 +721,8 @@ def parse_tag(parser):
681
721
def do_export (parser ):
682
722
global parsed_refs , bmarks , peer
683
723
724
+ p_bmarks = []
725
+
684
726
parser .next ()
685
727
686
728
for line in parser .each_block ('done' ):
@@ -699,28 +741,55 @@ def do_export(parser):
699
741
700
742
for ref , node in parsed_refs .iteritems ():
701
743
if ref .startswith ('refs/heads/branches' ):
702
- pass
744
+ print "ok %s" % ref
703
745
elif ref .startswith ('refs/heads/' ):
704
746
bmark = ref [len ('refs/heads/' ):]
705
- if bmark in bmarks :
706
- old = bmarks [bmark ].hex ()
707
- else :
708
- old = ''
709
- if not bookmarks .pushbookmark (parser .repo , bmark , old , node ):
710
- continue
747
+ p_bmarks .append ((bmark , node ))
748
+ continue
711
749
elif ref .startswith ('refs/tags/' ):
712
750
tag = ref [len ('refs/tags/' ):]
713
- parser .repo .tag ([tag ], node , None , True , None , {})
751
+ if mode == 'git' :
752
+ msg = 'Added tag %s for changeset %s' % (tag , hghex (node [:6 ]));
753
+ parser .repo .tag ([tag ], node , msg , False , None , {})
754
+ else :
755
+ parser .repo .tag ([tag ], node , None , True , None , {})
756
+ print "ok %s" % ref
714
757
else :
715
758
# transport-helper/fast-export bugs
716
759
continue
760
+
761
+ if peer :
762
+ parser .repo .push (peer , force = force_push )
763
+
764
+ # handle bookmarks
765
+ for bmark , node in p_bmarks :
766
+ ref = 'refs/heads/' + bmark
767
+ new = hghex (node )
768
+
769
+ if bmark in bmarks :
770
+ old = bmarks [bmark ].hex ()
771
+ else :
772
+ old = ''
773
+
774
+ if bmark == 'master' and 'master' not in parser .repo ._bookmarks :
775
+ # fake bookmark
776
+ pass
777
+ elif bookmarks .pushbookmark (parser .repo , bmark , old , new ):
778
+ # updated locally
779
+ pass
780
+ else :
781
+ print "error %s" % ref
782
+ continue
783
+
784
+ if peer :
785
+ if not peer .pushkey ('bookmarks' , bmark , old , new ):
786
+ print "error %s" % ref
787
+ continue
788
+
717
789
print "ok %s" % ref
718
790
719
791
print
720
792
721
- if peer :
722
- parser .repo .push (peer , force = False )
723
-
724
793
def fix_path (alias , repo , orig_url ):
725
794
repo_url = util .url (repo .url ())
726
795
url = util .url (orig_url )
@@ -733,20 +802,24 @@ def main(args):
733
802
global prefix , dirname , branches , bmarks
734
803
global marks , blob_marks , parsed_refs
735
804
global peer , mode , bad_mail , bad_name
736
- global track_branches
805
+ global track_branches , force_push , is_tmp
737
806
738
807
alias = args [1 ]
739
808
url = args [2 ]
740
809
peer = None
741
810
742
811
hg_git_compat = False
743
812
track_branches = True
813
+ force_push = True
814
+
744
815
try :
745
816
if get_config ('remote-hg.hg-git-compat' ) == 'true\n ' :
746
817
hg_git_compat = True
747
818
track_branches = False
748
819
if get_config ('remote-hg.track-branches' ) == 'false\n ' :
749
820
track_branches = False
821
+ if get_config ('remote-hg.force-push' ) == 'false\n ' :
822
+ force_push = False
750
823
except subprocess .CalledProcessError :
751
824
pass
752
825
@@ -771,6 +844,7 @@ def main(args):
771
844
bmarks = {}
772
845
blob_marks = {}
773
846
parsed_refs = {}
847
+ marks = None
774
848
775
849
repo = get_repo (url , alias )
776
850
prefix = 'refs/hg/%s' % alias
@@ -798,9 +872,13 @@ def main(args):
798
872
die ('unhandled command: %s' % line )
799
873
sys .stdout .flush ()
800
874
875
+ def bye ():
876
+ if not marks :
877
+ return
801
878
if not is_tmp :
802
879
marks .store ()
803
880
else :
804
881
shutil .rmtree (dirname )
805
882
883
+ atexit .register (bye )
806
884
sys .exit (main (sys .argv ))
0 commit comments