summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2013-04-26 15:19:03 -0700
committerJunio C Hamano <gitster@pobox.com>2013-04-26 15:19:03 -0700
commitc8c82b1ba3fd03b3de4d2c6c760b75726f519cb8 (patch)
treeadd6bbaf9227539ff8843f839a5fb258780f29fa
parentdf8597258e630e6cf5553efd9748b9da4670f5e1 (diff)
parent160695949a074ae252a4d458a8e135fea3245681 (diff)
downloadgit-c8c82b1ba3fd03b3de4d2c6c760b75726f519cb8.tar.gz
Merge branch 'fc/remote-hg'
* fc/remote-hg: remote-hg: strip extra newline remote-hg: use marks instead of inlined files remote-hg: small performance improvement remote-hg: allow refs with spaces remote-hg: don't update bookmarks unnecessarily remote-hg: add support for schemes extension remote-hg: improve email sanitation remote-hg: add custom local tag write code remote-hg: write tags in the appropriate branch remote-hg: custom method to write tags remote-hg: add support for tag objects remote-hg: add branch_tip() helper remote-hg: properly mark branches up-to-date remote-hg: use python urlparse remote-hg: safer bookmark pushing remote-helpers: avoid has_key
-rwxr-xr-xcontrib/remote-helpers/git-remote-bzr2
-rwxr-xr-xcontrib/remote-helpers/git-remote-hg167
-rwxr-xr-xcontrib/remote-helpers/test-hg.sh8
3 files changed, 141 insertions, 36 deletions
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index a622122c86..dcda351d35 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -94,7 +94,7 @@ class Marks:
return self.last_mark
def is_marked(self, rev):
- return self.marks.has_key(rev)
+ return str(rev) in self.marks
def new_mark(self, rev, mark):
self.marks[rev] = mark
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 548133121d..fda4199a98 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -12,7 +12,7 @@
# For remote repositories a local clone is stored in
# "$GIT_DIR/hg/origin/clone/.hg/".
-from mercurial import hg, ui, bookmarks, context, util, encoding, node, error
+from mercurial import hg, ui, bookmarks, context, util, encoding, node, error, extensions
import re
import sys
@@ -22,6 +22,7 @@ import shutil
import subprocess
import urllib
import atexit
+import urlparse
#
# If you want to switch to hg-git compatibility mode:
@@ -50,6 +51,7 @@ import atexit
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
@@ -73,6 +75,12 @@ def hgmode(mode):
def hghex(node):
return hg.node.hex(node)
+def hgref(ref):
+ return ref.replace('___', ' ')
+
+def gitref(ref):
+ return ref.replace(' ', '___')
+
def get_config(config):
cmd = ['git', 'config', '--get', config]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
@@ -118,6 +126,10 @@ class Marks:
def to_rev(self, mark):
return self.rev_marks[mark]
+ def next_mark(self):
+ self.last_mark += 1
+ return self.last_mark
+
def get_mark(self, rev):
self.last_mark += 1
self.marks[str(rev)] = self.last_mark
@@ -129,7 +141,7 @@ class Marks:
self.last_mark = mark
def is_marked(self, rev):
- return self.marks.has_key(str(rev))
+ return str(rev) in self.marks
def get_tip(self, branch):
return self.tips.get(branch, 0)
@@ -210,20 +222,38 @@ def fix_file_path(path):
return path
return os.path.relpath(path, '/')
-def export_file(fc):
- d = fc.data()
- path = fix_file_path(fc.path())
- print "M %s inline %s" % (gitmode(fc.flags()), path)
- print "data %d" % len(d)
- print d
+def export_files(files):
+ global marks, filenodes
+
+ final = []
+ for f in files:
+ fid = node.hex(f.filenode())
+
+ if fid in filenodes:
+ mark = filenodes[fid]
+ else:
+ mark = marks.next_mark()
+ filenodes[fid] = mark
+ d = f.data()
+
+ print "blob"
+ print "mark :%u" % mark
+ print "data %d" % len(d)
+ print d
+
+ path = fix_file_path(f.path())
+ final.append((gitmode(f.flags()), mark, path))
+
+ return final
def get_filechanges(repo, ctx, parent):
modified = set()
added = set()
removed = set()
- cur = ctx.manifest()
+ # load earliest manifest first for caching reasons
prev = repo[parent].manifest().copy()
+ cur = ctx.manifest()
for fn in cur:
if fn in prev:
@@ -244,9 +274,14 @@ def fixup_user_git(user):
name = m.group(1)
mail = m.group(2).strip()
else:
- m = NAME_RE.match(user)
+ m = EMAIL_RE.match(user)
if m:
- name = m.group(1).strip()
+ name = m.group(1)
+ mail = m.group(2)
+ else:
+ m = NAME_RE.match(user)
+ if m:
+ name = m.group(1).strip()
return (name, mail)
def fixup_user_hg(user):
@@ -298,6 +333,12 @@ def get_repo(url, alias):
except subprocess.CalledProcessError:
pass
+ try:
+ mod = extensions.load(myui, 'hgext.schemes', None)
+ mod.extsetup(myui)
+ except ImportError:
+ pass
+
if hg.islocal(url):
repo = hg.repository(myui, url)
else:
@@ -393,6 +434,8 @@ def export_ref(repo, name, kind, head):
if len(parents) == 0 and rev:
print 'reset %s/%s' % (prefix, ename)
+ modified_final = export_files(c.filectx(f) for f in modified)
+
print "commit %s/%s" % (prefix, ename)
print "mark :%d" % (marks.get_mark(rev))
print "author %s" % (author)
@@ -405,8 +448,8 @@ def export_ref(repo, name, kind, head):
if len(parents) > 1:
print "merge :%s" % (rev_to_mark(parents[1]))
- for f in modified:
- export_file(c.filectx(f))
+ for f in modified_final:
+ print "M %s :%u %s" % f
for f in removed:
print "D %s" % (fix_file_path(f))
print
@@ -424,10 +467,10 @@ def export_ref(repo, name, kind, head):
marks.set_tip(ename, rev)
def export_tag(repo, tag):
- export_ref(repo, tag, 'tags', repo[tag])
+ export_ref(repo, tag, 'tags', repo[hgref(tag)])
def export_bookmark(repo, bmark):
- head = bmarks[bmark]
+ head = bmarks[hgref(bmark)]
export_ref(repo, bmark, 'bookmarks', head)
def export_branch(repo, branch):
@@ -456,19 +499,24 @@ def do_capabilities(parser):
print
+def branch_tip(repo, branch):
+ # older versions of mercurial don't have this
+ if hasattr(repo, 'branchtip'):
+ return repo.branchtip(branch)
+ else:
+ return repo.branchtags()[branch]
+
def get_branch_tip(repo, branch):
global branches
- heads = branches.get(branch, None)
+ heads = branches.get(hgref(branch), None)
if not heads:
return None
# verify there's only one head
if (len(heads) > 1):
warn("Branch '%s' has more than one head, consider merging" % branch)
- # older versions of mercurial don't have this
- if hasattr(repo, "branchtip"):
- return repo.branchtip(branch)
+ return branch_tip(repo, hgref(branch))
return heads[0]
@@ -490,6 +538,7 @@ def list_head(repo, cur):
head = 'master'
bmarks[head] = node
+ head = gitref(head)
print "@refs/heads/%s HEAD" % head
g_head = (head, node)
@@ -511,15 +560,15 @@ def do_list(parser):
branches[branch] = heads
for branch in branches:
- print "? refs/heads/branches/%s" % branch
+ print "? refs/heads/branches/%s" % gitref(branch)
for bmark in bmarks:
- print "? refs/heads/%s" % bmark
+ print "? refs/heads/%s" % gitref(bmark)
for tag, node in repo.tagslist():
if tag == 'tip':
continue
- print "? refs/tags/%s" % tag
+ print "? refs/tags/%s" % gitref(tag)
print
@@ -603,6 +652,10 @@ def parse_commit(parser):
if parser.check('merge'):
die('octopus merges are not supported yet')
+ # fast-export adds an extra newline
+ if data[-1] == '\n':
+ data = data[:-1]
+
files = {}
for line in parser:
@@ -656,7 +709,8 @@ def parse_commit(parser):
# Check if the ref is supposed to be a named branch
if ref.startswith('refs/heads/branches/'):
- extra['branch'] = ref[len('refs/heads/branches/'):]
+ branch = ref[len('refs/heads/branches/'):]
+ extra['branch'] = hgref(branch)
if mode == 'hg':
i = data.find('\n--HG--\n')
@@ -716,7 +770,40 @@ def parse_tag(parser):
data = parser.get_data()
parser.next()
- # nothing to do
+ parsed_tags[name] = (tagger, data)
+
+def write_tag(repo, tag, node, msg, author):
+ branch = repo[node].branch()
+ tip = branch_tip(repo, branch)
+ tip = repo[tip]
+
+ def getfilectx(repo, memctx, f):
+ try:
+ fctx = tip.filectx(f)
+ data = fctx.data()
+ except error.ManifestLookupError:
+ data = ""
+ content = data + "%s %s\n" % (hghex(node), tag)
+ return context.memfilectx(f, content, False, False, None)
+
+ p1 = tip.hex()
+ p2 = '\0' * 20
+ if not author:
+ author = (None, 0, 0)
+ user, date, tz = author
+
+ ctx = context.memctx(repo, (p1, p2), msg,
+ ['.hgtags'], getfilectx,
+ user, (date, tz), {'branch' : branch})
+
+ tmp = encoding.encoding
+ encoding.encoding = 'utf-8'
+
+ tagnode = repo.commitctx(ctx)
+
+ encoding.encoding = tmp
+
+ return tagnode
def do_export(parser):
global parsed_refs, bmarks, peer
@@ -741,6 +828,10 @@ def do_export(parser):
for ref, node in parsed_refs.iteritems():
if ref.startswith('refs/heads/branches'):
+ branch = ref[len('refs/heads/branches/'):]
+ if branch in branches and node in branches[branch]:
+ # up to date
+ continue
print "ok %s" % ref
elif ref.startswith('refs/heads/'):
bmark = ref[len('refs/heads/'):]
@@ -748,11 +839,16 @@ def do_export(parser):
continue
elif ref.startswith('refs/tags/'):
tag = ref[len('refs/tags/'):]
+ tag = hgref(tag)
+ author, msg = parsed_tags.get(tag, (None, None))
if mode == 'git':
- msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
- parser.repo.tag([tag], node, msg, False, None, {})
+ if not msg:
+ msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
+ write_tag(parser.repo, tag, node, msg, author)
else:
- parser.repo.tag([tag], node, None, True, None, {})
+ fp = parser.repo.opener('localtags', 'a')
+ fp.write('%s %s\n' % (hghex(node), tag))
+ fp.close()
print "ok %s" % ref
else:
# transport-helper/fast-export bugs
@@ -771,6 +867,9 @@ def do_export(parser):
else:
old = ''
+ if old == new:
+ continue
+
if bmark == 'master' and 'master' not in parser.repo._bookmarks:
# fake bookmark
pass
@@ -782,6 +881,8 @@ def do_export(parser):
continue
if peer:
+ rb = peer.listkeys('bookmarks')
+ old = rb.get(bmark, '')
if not peer.pushkey('bookmarks', bmark, old, new):
print "error %s" % ref
continue
@@ -791,11 +892,11 @@ def do_export(parser):
print
def fix_path(alias, repo, orig_url):
- repo_url = util.url(repo.url())
- url = util.url(orig_url)
- if str(url) == str(repo_url):
+ url = urlparse.urlparse(orig_url, 'file')
+ if url.scheme != 'file' or os.path.isabs(url.path):
return
- cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % repo_url]
+ abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
+ cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
subprocess.call(cmd)
def main(args):
@@ -803,6 +904,8 @@ def main(args):
global marks, blob_marks, parsed_refs
global peer, mode, bad_mail, bad_name
global track_branches, force_push, is_tmp
+ global parsed_tags
+ global filenodes
alias = args[1]
url = args[2]
@@ -845,6 +948,8 @@ def main(args):
blob_marks = {}
parsed_refs = {}
marks = None
+ parsed_tags = {}
+ filenodes = {}
repo = get_repo(url, alias)
prefix = 'refs/hg/%s' % alias
diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh
index d5b873051f..8de2aa7fec 100755
--- a/contrib/remote-helpers/test-hg.sh
+++ b/contrib/remote-helpers/test-hg.sh
@@ -137,15 +137,15 @@ test_expect_success 'authors' '
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 beta "test <test@example.com> (comment)" "test <test@example.com>" &&
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 epsilon "name <test@example.com" "name <test@example.com>" &&
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 theta "test >test@example.com>" "test <test@example.com>" &&
author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
- author_test kappa "test@example.com" "test@example.com <unknown>"
+ author_test kappa "test@example.com" "Unknown <test@example.com>"
) &&
git clone "hg::$PWD/hgrepo" gitrepo &&