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 ...
This commit is contained in:
commit
37d32de72a
@ -8,8 +8,11 @@
|
||||
# Just copy to your ~/bin, or anywhere in your $PATH.
|
||||
# Then you can clone with:
|
||||
# git clone hg::/path/to/mercurial/repo/
|
||||
#
|
||||
# For remote repositories a local clone is stored in
|
||||
# "$GIT_DIR/hg/origin/clone/.hg/".
|
||||
|
||||
from mercurial import hg, ui, bookmarks, context, util, encoding
|
||||
from mercurial import hg, ui, bookmarks, context, util, encoding, node, error
|
||||
|
||||
import re
|
||||
import sys
|
||||
@ -18,11 +21,22 @@ import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import urllib
|
||||
import atexit
|
||||
|
||||
#
|
||||
# If you want to switch to hg-git compatibility mode:
|
||||
# git config --global remote-hg.hg-git-compat true
|
||||
#
|
||||
# If you are not in hg-git-compat mode and want to disable the tracking of
|
||||
# named branches:
|
||||
# git config --global remote-hg.track-branches false
|
||||
#
|
||||
# If you don't want to force pushes (and thus risk creating new remote heads):
|
||||
# git config --global remote-hg.force-push false
|
||||
#
|
||||
# If you want the equivalent of hg's clone/pull--insecure option:
|
||||
# git config remote-hg.insecure true
|
||||
#
|
||||
# git:
|
||||
# Sensible defaults for git.
|
||||
# hg bookmarks are exported as git branches, hg branches are prefixed
|
||||
@ -56,6 +70,9 @@ def hgmode(mode):
|
||||
m = { '100755': 'x', '120000': 'l' }
|
||||
return m.get(mode, '')
|
||||
|
||||
def hghex(node):
|
||||
return hg.node.hex(node)
|
||||
|
||||
def get_config(config):
|
||||
cmd = ['git', 'config', '--get', config]
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
@ -188,9 +205,15 @@ class Parser:
|
||||
tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
|
||||
return (user, int(date), -tz)
|
||||
|
||||
def fix_file_path(path):
|
||||
if not os.path.isabs(path):
|
||||
return path
|
||||
return os.path.relpath(path, '/')
|
||||
|
||||
def export_file(fc):
|
||||
d = fc.data()
|
||||
print "M %s inline %s" % (gitmode(fc.flags()), fc.path())
|
||||
path = fix_file_path(fc.path())
|
||||
print "M %s inline %s" % (gitmode(fc.flags()), path)
|
||||
print "data %d" % len(d)
|
||||
print d
|
||||
|
||||
@ -267,17 +290,30 @@ def get_repo(url, alias):
|
||||
|
||||
myui = ui.ui()
|
||||
myui.setconfig('ui', 'interactive', 'off')
|
||||
myui.fout = sys.stderr
|
||||
|
||||
try:
|
||||
if get_config('remote-hg.insecure') == 'true\n':
|
||||
myui.setconfig('web', 'cacerts', '')
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
if hg.islocal(url):
|
||||
repo = hg.repository(myui, url)
|
||||
else:
|
||||
local_path = os.path.join(dirname, 'clone')
|
||||
if not os.path.exists(local_path):
|
||||
peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)
|
||||
try:
|
||||
peer, dstpeer = hg.clone(myui, {}, url, local_path, update=True, pull=True)
|
||||
except:
|
||||
die('Repository error')
|
||||
repo = dstpeer.local()
|
||||
else:
|
||||
repo = hg.repository(myui, local_path)
|
||||
peer = hg.peer(myui, {}, url)
|
||||
try:
|
||||
peer = hg.peer(myui, {}, url)
|
||||
except:
|
||||
die('Repository error')
|
||||
repo.pull(peer, heads=None, force=True)
|
||||
|
||||
return repo
|
||||
@ -372,7 +408,7 @@ def export_ref(repo, name, kind, head):
|
||||
for f in modified:
|
||||
export_file(c.filectx(f))
|
||||
for f in removed:
|
||||
print "D %s" % (f)
|
||||
print "D %s" % (fix_file_path(f))
|
||||
print
|
||||
|
||||
count += 1
|
||||
@ -532,7 +568,6 @@ def parse_blob(parser):
|
||||
data = parser.get_data()
|
||||
blob_marks[mark] = data
|
||||
parser.next()
|
||||
return
|
||||
|
||||
def get_merge_files(repo, p1, p2, files):
|
||||
for e in repo[p1].files():
|
||||
@ -543,7 +578,7 @@ def get_merge_files(repo, p1, p2, files):
|
||||
files[e] = f
|
||||
|
||||
def parse_commit(parser):
|
||||
global marks, blob_marks, bmarks, parsed_refs
|
||||
global marks, blob_marks, parsed_refs
|
||||
global mode
|
||||
|
||||
from_mark = merge_mark = None
|
||||
@ -576,7 +611,7 @@ def parse_commit(parser):
|
||||
mark = int(mark_ref[1:])
|
||||
f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
|
||||
elif parser.check('D'):
|
||||
t, path = line.split(' ')
|
||||
t, path = line.split(' ', 1)
|
||||
f = { 'deleted' : True }
|
||||
else:
|
||||
die('Unknown file command: %s' % line)
|
||||
@ -619,11 +654,15 @@ def parse_commit(parser):
|
||||
if merge_mark:
|
||||
get_merge_files(repo, p1, p2, files)
|
||||
|
||||
# Check if the ref is supposed to be a named branch
|
||||
if ref.startswith('refs/heads/branches/'):
|
||||
extra['branch'] = ref[len('refs/heads/branches/'):]
|
||||
|
||||
if mode == 'hg':
|
||||
i = data.find('\n--HG--\n')
|
||||
if i >= 0:
|
||||
tmp = data[i + len('\n--HG--\n'):].strip()
|
||||
for k, v in [e.split(' : ') for e in tmp.split('\n')]:
|
||||
for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
|
||||
if k == 'rename':
|
||||
old, new = v.split(' => ', 1)
|
||||
files[new]['rename'] = old
|
||||
@ -648,10 +687,11 @@ def parse_commit(parser):
|
||||
rev = repo[node].rev()
|
||||
|
||||
parsed_refs[ref] = node
|
||||
|
||||
marks.new_mark(rev, commit_mark)
|
||||
|
||||
def parse_reset(parser):
|
||||
global parsed_refs
|
||||
|
||||
ref = parser[1]
|
||||
parser.next()
|
||||
# ugh
|
||||
@ -681,6 +721,8 @@ def parse_tag(parser):
|
||||
def do_export(parser):
|
||||
global parsed_refs, bmarks, peer
|
||||
|
||||
p_bmarks = []
|
||||
|
||||
parser.next()
|
||||
|
||||
for line in parser.each_block('done'):
|
||||
@ -699,28 +741,55 @@ def do_export(parser):
|
||||
|
||||
for ref, node in parsed_refs.iteritems():
|
||||
if ref.startswith('refs/heads/branches'):
|
||||
pass
|
||||
print "ok %s" % ref
|
||||
elif ref.startswith('refs/heads/'):
|
||||
bmark = ref[len('refs/heads/'):]
|
||||
if bmark in bmarks:
|
||||
old = bmarks[bmark].hex()
|
||||
else:
|
||||
old = ''
|
||||
if not bookmarks.pushbookmark(parser.repo, bmark, old, node):
|
||||
continue
|
||||
p_bmarks.append((bmark, node))
|
||||
continue
|
||||
elif ref.startswith('refs/tags/'):
|
||||
tag = ref[len('refs/tags/'):]
|
||||
parser.repo.tag([tag], node, None, True, None, {})
|
||||
if mode == 'git':
|
||||
msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
|
||||
parser.repo.tag([tag], node, msg, False, None, {})
|
||||
else:
|
||||
parser.repo.tag([tag], node, None, True, None, {})
|
||||
print "ok %s" % ref
|
||||
else:
|
||||
# transport-helper/fast-export bugs
|
||||
continue
|
||||
|
||||
if peer:
|
||||
parser.repo.push(peer, force=force_push)
|
||||
|
||||
# handle bookmarks
|
||||
for bmark, node in p_bmarks:
|
||||
ref = 'refs/heads/' + bmark
|
||||
new = hghex(node)
|
||||
|
||||
if bmark in bmarks:
|
||||
old = bmarks[bmark].hex()
|
||||
else:
|
||||
old = ''
|
||||
|
||||
if bmark == 'master' and 'master' not in parser.repo._bookmarks:
|
||||
# fake bookmark
|
||||
pass
|
||||
elif bookmarks.pushbookmark(parser.repo, bmark, old, new):
|
||||
# updated locally
|
||||
pass
|
||||
else:
|
||||
print "error %s" % ref
|
||||
continue
|
||||
|
||||
if peer:
|
||||
if not peer.pushkey('bookmarks', bmark, old, new):
|
||||
print "error %s" % ref
|
||||
continue
|
||||
|
||||
print "ok %s" % ref
|
||||
|
||||
print
|
||||
|
||||
if peer:
|
||||
parser.repo.push(peer, force=False)
|
||||
|
||||
def fix_path(alias, repo, orig_url):
|
||||
repo_url = util.url(repo.url())
|
||||
url = util.url(orig_url)
|
||||
@ -733,7 +802,7 @@ def main(args):
|
||||
global prefix, dirname, branches, bmarks
|
||||
global marks, blob_marks, parsed_refs
|
||||
global peer, mode, bad_mail, bad_name
|
||||
global track_branches
|
||||
global track_branches, force_push, is_tmp
|
||||
|
||||
alias = args[1]
|
||||
url = args[2]
|
||||
@ -741,12 +810,16 @@ def main(args):
|
||||
|
||||
hg_git_compat = False
|
||||
track_branches = True
|
||||
force_push = True
|
||||
|
||||
try:
|
||||
if get_config('remote-hg.hg-git-compat') == 'true\n':
|
||||
hg_git_compat = True
|
||||
track_branches = False
|
||||
if get_config('remote-hg.track-branches') == 'false\n':
|
||||
track_branches = False
|
||||
if get_config('remote-hg.force-push') == 'false\n':
|
||||
force_push = False
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
@ -771,6 +844,7 @@ def main(args):
|
||||
bmarks = {}
|
||||
blob_marks = {}
|
||||
parsed_refs = {}
|
||||
marks = None
|
||||
|
||||
repo = get_repo(url, alias)
|
||||
prefix = 'refs/hg/%s' % alias
|
||||
@ -798,9 +872,13 @@ def main(args):
|
||||
die('unhandled command: %s' % line)
|
||||
sys.stdout.flush()
|
||||
|
||||
def bye():
|
||||
if not marks:
|
||||
return
|
||||
if not is_tmp:
|
||||
marks.store()
|
||||
else:
|
||||
shutil.rmtree(dirname)
|
||||
|
||||
atexit.register(bye)
|
||||
sys.exit(main(sys.argv))
|
||||
|
@ -22,7 +22,6 @@ fi
|
||||
|
||||
# clone to a git repo
|
||||
git_clone () {
|
||||
hg -R $1 bookmark -f -r tip master &&
|
||||
git clone -q "hg::$PWD/$1" $2
|
||||
}
|
||||
|
||||
@ -30,6 +29,7 @@ git_clone () {
|
||||
hg_clone () {
|
||||
(
|
||||
hg init $2 &&
|
||||
hg -R $2 bookmark -i master &&
|
||||
cd $1 &&
|
||||
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
|
||||
) &&
|
||||
@ -50,7 +50,8 @@ hg_push () {
|
||||
}
|
||||
|
||||
hg_log () {
|
||||
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
|
||||
hg -R $1 log --graph --debug >log &&
|
||||
grep -v 'tag: *default/' log
|
||||
}
|
||||
|
||||
setup () {
|
||||
@ -62,6 +63,8 @@ setup () {
|
||||
echo "commit = -d \"0 0\""
|
||||
echo "debugrawcommit = -d \"0 0\""
|
||||
echo "tag = -d \"0 0\""
|
||||
echo "[extensions]"
|
||||
echo "graphlog ="
|
||||
) >> "$HOME"/.hgrc &&
|
||||
git config --global remote-hg.hg-git-compat true
|
||||
|
||||
@ -200,8 +203,8 @@ test_expect_success 'hg branch' '
|
||||
hg_push hgrepo gitrepo &&
|
||||
hg_clone gitrepo hgrepo2 &&
|
||||
|
||||
: TODO, avoid "master" bookmark &&
|
||||
(cd hgrepo2 && hg checkout gamma) &&
|
||||
: Back to the common revision &&
|
||||
(cd hgrepo && hg checkout default) &&
|
||||
|
||||
hg_log hgrepo > expected &&
|
||||
hg_log hgrepo2 > actual &&
|
||||
|
@ -27,7 +27,6 @@ fi
|
||||
|
||||
# clone to a git repo with git
|
||||
git_clone_git () {
|
||||
hg -R $1 bookmark -f -r tip master &&
|
||||
git clone -q "hg::$PWD/$1" $2
|
||||
}
|
||||
|
||||
@ -35,6 +34,7 @@ git_clone_git () {
|
||||
hg_clone_git () {
|
||||
(
|
||||
hg init $2 &&
|
||||
hg -R $2 bookmark -i master &&
|
||||
cd $1 &&
|
||||
git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
|
||||
) &&
|
||||
@ -47,7 +47,7 @@ git_clone_hg () {
|
||||
(
|
||||
git init -q $2 &&
|
||||
cd $1 &&
|
||||
hg bookmark -f -r tip master &&
|
||||
hg bookmark -i -f -r tip master &&
|
||||
hg -q push -r master ../$2 || true
|
||||
)
|
||||
}
|
||||
@ -78,7 +78,8 @@ hg_push_hg () {
|
||||
}
|
||||
|
||||
hg_log () {
|
||||
hg -R $1 log --graph --debug | grep -v 'tag: *default/'
|
||||
hg -R $1 log --graph --debug >log &&
|
||||
grep -v 'tag: *default/' log
|
||||
}
|
||||
|
||||
git_log () {
|
||||
@ -97,6 +98,7 @@ setup () {
|
||||
echo "[extensions]"
|
||||
echo "hgext.bookmarks ="
|
||||
echo "hggit ="
|
||||
echo "graphlog ="
|
||||
) >> "$HOME"/.hgrc &&
|
||||
git config --global receive.denycurrentbranch warn
|
||||
git config --global remote-hg.hg-git-compat true
|
||||
|
@ -118,4 +118,40 @@ test_expect_success 'update bookmark' '
|
||||
hg -R hgrepo bookmarks | egrep "devel[ ]+3:"
|
||||
'
|
||||
|
||||
author_test () {
|
||||
echo $1 >> content &&
|
||||
hg commit -u "$2" -m "add $1" &&
|
||||
echo "$3" >> ../expected
|
||||
}
|
||||
|
||||
test_expect_success 'authors' '
|
||||
mkdir -p tmp && cd tmp &&
|
||||
test_when_finished "cd .. && rm -rf tmp" &&
|
||||
|
||||
(
|
||||
hg init hgrepo &&
|
||||
cd hgrepo &&
|
||||
|
||||
touch content &&
|
||||
hg add content &&
|
||||
|
||||
author_test alpha "" "H G Wells <wells@example.com>" &&
|
||||
author_test beta "test" "test <unknown>" &&
|
||||
author_test beta "test <test@example.com> (comment)" "test <unknown>" &&
|
||||
author_test gamma "<test@example.com>" "Unknown <test@example.com>" &&
|
||||
author_test delta "name<test@example.com>" "name <test@example.com>" &&
|
||||
author_test epsilon "name <test@example.com" "name <unknown>" &&
|
||||
author_test zeta " test " "test <unknown>" &&
|
||||
author_test eta "test < test@example.com >" "test <test@example.com>" &&
|
||||
author_test theta "test >test@example.com>" "test <unknown>" &&
|
||||
author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
|
||||
author_test kappa "test@example.com" "test@example.com <unknown>"
|
||||
) &&
|
||||
|
||||
git clone "hg::$PWD/hgrepo" gitrepo &&
|
||||
git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual &&
|
||||
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user