Browse Source

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
maint
Junio C Hamano 12 years ago
parent
commit
c8c82b1ba3
  1. 2
      contrib/remote-helpers/git-remote-bzr
  2. 167
      contrib/remote-helpers/git-remote-hg
  3. 8
      contrib/remote-helpers/test-hg.sh

2
contrib/remote-helpers/git-remote-bzr

@ -94,7 +94,7 @@ class Marks: @@ -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

167
contrib/remote-helpers/git-remote-hg

@ -12,7 +12,7 @@ @@ -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 @@ -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 @@ -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): @@ -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: @@ -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: @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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): @@ -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

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

@ -137,15 +137,15 @@ test_expect_success 'authors' ' @@ -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 &&

Loading…
Cancel
Save