Skip to content

Commit 37d32de

Browse files
committed
Merge branch 'fc/remote-hg'
Updates remote-hg helper (in contrib/). * fc/remote-hg: (21 commits) remote-hg: activate graphlog extension for hg_log() remote-hg: fix bad file paths remote-hg: document location of stored hg repository remote-hg: fix bad state issue remote-hg: add 'insecure' option remote-hg: add simple mail test remote-hg: add basic author tests remote-hg: show more proper errors remote-hg: force remote push remote-hg: push to the appropriate branch remote-hg: update tags globally remote-hg: update remote bookmarks remote-hg: refactor export remote-hg: split bookmark handling remote-hg: redirect buggy mercurial output remote-hg: trivial test cleanups remote-hg: make sure fake bookmarks are updated remote-hg: fix for files with spaces remote-hg: properly report errors on bookmark pushes remote-hg: add missing config variable in doc ...
2 parents 4b35b00 + 9a57988 commit 37d32de

File tree

4 files changed

+148
-29
lines changed

4 files changed

+148
-29
lines changed

contrib/remote-helpers/git-remote-hg

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
# Just copy to your ~/bin, or anywhere in your $PATH.
99
# Then you can clone with:
1010
# 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/".
1114

12-
from mercurial import hg, ui, bookmarks, context, util, encoding
15+
from mercurial import hg, ui, bookmarks, context, util, encoding, node, error
1316

1417
import re
1518
import sys
@@ -18,11 +21,22 @@ import json
1821
import shutil
1922
import subprocess
2023
import urllib
24+
import atexit
2125

2226
#
2327
# If you want to switch to hg-git compatibility mode:
2428
# git config --global remote-hg.hg-git-compat true
2529
#
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+
#
2640
# git:
2741
# Sensible defaults for git.
2842
# hg bookmarks are exported as git branches, hg branches are prefixed
@@ -56,6 +70,9 @@ def hgmode(mode):
5670
m = { '100755': 'x', '120000': 'l' }
5771
return m.get(mode, '')
5872

73+
def hghex(node):
74+
return hg.node.hex(node)
75+
5976
def get_config(config):
6077
cmd = ['git', 'config', '--get', config]
6178
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
@@ -188,9 +205,15 @@ class Parser:
188205
tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
189206
return (user, int(date), -tz)
190207

208+
def fix_file_path(path):
209+
if not os.path.isabs(path):
210+
return path
211+
return os.path.relpath(path, '/')
212+
191213
def export_file(fc):
192214
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)
194217
print "data %d" % len(d)
195218
print d
196219

@@ -267,17 +290,30 @@ def get_repo(url, alias):
267290

268291
myui = ui.ui()
269292
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
270300

271301
if hg.islocal(url):
272302
repo = hg.repository(myui, url)
273303
else:
274304
local_path = os.path.join(dirname, 'clone')
275305
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')
277310
repo = dstpeer.local()
278311
else:
279312
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')
281317
repo.pull(peer, heads=None, force=True)
282318

283319
return repo
@@ -372,7 +408,7 @@ def export_ref(repo, name, kind, head):
372408
for f in modified:
373409
export_file(c.filectx(f))
374410
for f in removed:
375-
print "D %s" % (f)
411+
print "D %s" % (fix_file_path(f))
376412
print
377413

378414
count += 1
@@ -532,7 +568,6 @@ def parse_blob(parser):
532568
data = parser.get_data()
533569
blob_marks[mark] = data
534570
parser.next()
535-
return
536571

537572
def get_merge_files(repo, p1, p2, files):
538573
for e in repo[p1].files():
@@ -543,7 +578,7 @@ def get_merge_files(repo, p1, p2, files):
543578
files[e] = f
544579

545580
def parse_commit(parser):
546-
global marks, blob_marks, bmarks, parsed_refs
581+
global marks, blob_marks, parsed_refs
547582
global mode
548583

549584
from_mark = merge_mark = None
@@ -576,7 +611,7 @@ def parse_commit(parser):
576611
mark = int(mark_ref[1:])
577612
f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
578613
elif parser.check('D'):
579-
t, path = line.split(' ')
614+
t, path = line.split(' ', 1)
580615
f = { 'deleted' : True }
581616
else:
582617
die('Unknown file command: %s' % line)
@@ -619,11 +654,15 @@ def parse_commit(parser):
619654
if merge_mark:
620655
get_merge_files(repo, p1, p2, files)
621656

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+
622661
if mode == 'hg':
623662
i = data.find('\n--HG--\n')
624663
if i >= 0:
625664
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')]:
627666
if k == 'rename':
628667
old, new = v.split(' => ', 1)
629668
files[new]['rename'] = old
@@ -648,10 +687,11 @@ def parse_commit(parser):
648687
rev = repo[node].rev()
649688

650689
parsed_refs[ref] = node
651-
652690
marks.new_mark(rev, commit_mark)
653691

654692
def parse_reset(parser):
693+
global parsed_refs
694+
655695
ref = parser[1]
656696
parser.next()
657697
# ugh
@@ -681,6 +721,8 @@ def parse_tag(parser):
681721
def do_export(parser):
682722
global parsed_refs, bmarks, peer
683723

724+
p_bmarks = []
725+
684726
parser.next()
685727

686728
for line in parser.each_block('done'):
@@ -699,28 +741,55 @@ def do_export(parser):
699741

700742
for ref, node in parsed_refs.iteritems():
701743
if ref.startswith('refs/heads/branches'):
702-
pass
744+
print "ok %s" % ref
703745
elif ref.startswith('refs/heads/'):
704746
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
711749
elif ref.startswith('refs/tags/'):
712750
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
714757
else:
715758
# transport-helper/fast-export bugs
716759
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+
717789
print "ok %s" % ref
718790

719791
print
720792

721-
if peer:
722-
parser.repo.push(peer, force=False)
723-
724793
def fix_path(alias, repo, orig_url):
725794
repo_url = util.url(repo.url())
726795
url = util.url(orig_url)
@@ -733,20 +802,24 @@ def main(args):
733802
global prefix, dirname, branches, bmarks
734803
global marks, blob_marks, parsed_refs
735804
global peer, mode, bad_mail, bad_name
736-
global track_branches
805+
global track_branches, force_push, is_tmp
737806

738807
alias = args[1]
739808
url = args[2]
740809
peer = None
741810

742811
hg_git_compat = False
743812
track_branches = True
813+
force_push = True
814+
744815
try:
745816
if get_config('remote-hg.hg-git-compat') == 'true\n':
746817
hg_git_compat = True
747818
track_branches = False
748819
if get_config('remote-hg.track-branches') == 'false\n':
749820
track_branches = False
821+
if get_config('remote-hg.force-push') == 'false\n':
822+
force_push = False
750823
except subprocess.CalledProcessError:
751824
pass
752825

@@ -771,6 +844,7 @@ def main(args):
771844
bmarks = {}
772845
blob_marks = {}
773846
parsed_refs = {}
847+
marks = None
774848

775849
repo = get_repo(url, alias)
776850
prefix = 'refs/hg/%s' % alias
@@ -798,9 +872,13 @@ def main(args):
798872
die('unhandled command: %s' % line)
799873
sys.stdout.flush()
800874

875+
def bye():
876+
if not marks:
877+
return
801878
if not is_tmp:
802879
marks.store()
803880
else:
804881
shutil.rmtree(dirname)
805882

883+
atexit.register(bye)
806884
sys.exit(main(sys.argv))

contrib/remote-helpers/test-hg-bidi.sh

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ fi
2222

2323
# clone to a git repo
2424
git_clone () {
25-
hg -R $1 bookmark -f -r tip master &&
2625
git clone -q "hg::$PWD/$1" $2
2726
}
2827

2928
# clone to an hg repo
3029
hg_clone () {
3130
(
3231
hg init $2 &&
32+
hg -R $2 bookmark -i master &&
3333
cd $1 &&
3434
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
3535
) &&
@@ -50,7 +50,8 @@ hg_push () {
5050
}
5151

5252
hg_log () {
53-
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
53+
hg -R $1 log --graph --debug >log &&
54+
grep -v 'tag: *default/' log
5455
}
5556

5657
setup () {
@@ -62,6 +63,8 @@ setup () {
6263
echo "commit = -d \"0 0\""
6364
echo "debugrawcommit = -d \"0 0\""
6465
echo "tag = -d \"0 0\""
66+
echo "[extensions]"
67+
echo "graphlog ="
6568
) >> "$HOME"/.hgrc &&
6669
git config --global remote-hg.hg-git-compat true
6770

@@ -200,8 +203,8 @@ test_expect_success 'hg branch' '
200203
hg_push hgrepo gitrepo &&
201204
hg_clone gitrepo hgrepo2 &&
202205
203-
: TODO, avoid "master" bookmark &&
204-
(cd hgrepo2 && hg checkout gamma) &&
206+
: Back to the common revision &&
207+
(cd hgrepo && hg checkout default) &&
205208
206209
hg_log hgrepo > expected &&
207210
hg_log hgrepo2 > actual &&

contrib/remote-helpers/test-hg-hg-git.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ fi
2727

2828
# clone to a git repo with git
2929
git_clone_git () {
30-
hg -R $1 bookmark -f -r tip master &&
3130
git clone -q "hg::$PWD/$1" $2
3231
}
3332

3433
# clone to an hg repo with git
3534
hg_clone_git () {
3635
(
3736
hg init $2 &&
37+
hg -R $2 bookmark -i master &&
3838
cd $1 &&
3939
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
4040
) &&
@@ -47,7 +47,7 @@ git_clone_hg () {
4747
(
4848
git init -q $2 &&
4949
cd $1 &&
50-
hg bookmark -f -r tip master &&
50+
hg bookmark -i -f -r tip master &&
5151
hg -q push -r master ../$2 || true
5252
)
5353
}
@@ -78,7 +78,8 @@ hg_push_hg () {
7878
}
7979

8080
hg_log () {
81-
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
81+
hg -R $1 log --graph --debug >log &&
82+
grep -v 'tag: *default/' log
8283
}
8384

8485
git_log () {
@@ -97,6 +98,7 @@ setup () {
9798
echo "[extensions]"
9899
echo "hgext.bookmarks ="
99100
echo "hggit ="
101+
echo "graphlog ="
100102
) >> "$HOME"/.hgrc &&
101103
git config --global receive.denycurrentbranch warn
102104
git config --global remote-hg.hg-git-compat true

0 commit comments

Comments
 (0)