From: Junio C Hamano Date: Mon, 22 Apr 2013 01:39:58 +0000 (-0700) Subject: Merge branch 'fc/remote-hg' X-Git-Tag: v1.8.3-rc0~38 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/37d32de72a495b8f8ea985fc97ad7ee8d1d82b06?ds=inline;hp=-c 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 ... --- 37d32de72a495b8f8ea985fc97ad7ee8d1d82b06 diff --combined contrib/remote-helpers/git-remote-hg index 45f6c80d45,a5f0013c62..548133121d --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@@ -8,8 -8,11 +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 +21,22 @@@ import jso 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 +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 +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 +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 @@@ -326,8 -362,6 +362,8 @@@ def export_ref(repo, name, kind, head) else: modified, removed = get_filechanges(repo, c, parents[0]) + desc += '\n' + if mode == 'hg': extra_msg = '' @@@ -351,6 -385,7 +387,6 @@@ else: extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value)) - desc += '\n' if extra_msg: desc += '\n--HG--\n' + extra_msg @@@ -372,7 -407,7 +408,7 @@@ 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 -567,6 +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 -577,7 +578,7 @@@ 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 -610,7 +611,7 @@@ 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 -653,15 +654,15 @@@ 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 -686,11 +687,11 @@@ 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 -720,8 +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 -740,55 +741,55 @@@ 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 -801,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 -809,16 +810,16 @@@ 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 -843,7 +844,7 @@@ bmarks = {} blob_marks = {} parsed_refs = {} + marks = None repo = get_repo(url, alias) prefix = 'refs/hg/%s' % alias @@@ -798,9 -871,13 +872,13 @@@ 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)) diff --combined contrib/remote-helpers/test-hg-hg-git.sh index 3f253b7de7,5daad6b961..253e65aaa8 --- a/contrib/remote-helpers/test-hg-hg-git.sh +++ b/contrib/remote-helpers/test-hg-hg-git.sh @@@ -27,7 -27,6 +27,6 @@@ f # 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 +34,7 @@@ 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 +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 +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 +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 @@@ -140,6 -142,7 +142,6 @@@ test_expect_success 'executable bit' git_clone_$x hgrepo-$x gitrepo2-$x && git_log gitrepo2-$x > log-$x done && - cp -r log-* output-* /tmp/foo/ && test_cmp output-hg output-git && test_cmp log-hg log-git diff --combined contrib/remote-helpers/test-hg.sh index 7bb81f2f8e,6a1e4b1ad6..d5b873051f --- a/contrib/remote-helpers/test-hg.sh +++ b/contrib/remote-helpers/test-hg.sh @@@ -115,7 -115,43 +115,43 @@@ test_expect_success 'update bookmark' git push ) && - hg -R hgrepo bookmarks | grep "devel\s\+3:" + 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 " && + author_test beta "test" "test " && + author_test beta "test (comment)" "test " && + author_test gamma "" "Unknown " && + author_test delta "name" "name " && + author_test epsilon "name " && + author_test zeta " test " "test " && + author_test eta "test < test@example.com >" "test " && + author_test theta "test >test@example.com>" "test " && + author_test iota "test < test example com>" "test " && + author_test kappa "test@example.com" "test@example.com " + ) && + + git clone "hg::$PWD/hgrepo" gitrepo && + git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual && + + test_cmp expected actual + ' + test_done