Skip to content

Commit c8c82b1

Browse files
committed
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
2 parents df85972 + 1606959 commit c8c82b1

3 files changed

Lines changed: 141 additions & 36 deletions

File tree

contrib/remote-helpers/git-remote-bzr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Marks:
9494
return self.last_mark
9595

9696
def is_marked(self, rev):
97-
return self.marks.has_key(rev)
97+
return str(rev) in self.marks
9898

9999
def new_mark(self, rev, mark):
100100
self.marks[rev] = mark

contrib/remote-helpers/git-remote-hg

Lines changed: 136 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# For remote repositories a local clone is stored in
1313
# "$GIT_DIR/hg/origin/clone/.hg/".
1414

15-
from mercurial import hg, ui, bookmarks, context, util, encoding, node, error
15+
from mercurial import hg, ui, bookmarks, context, util, encoding, node, error, extensions
1616

1717
import re
1818
import sys
@@ -22,6 +22,7 @@ import shutil
2222
import subprocess
2323
import urllib
2424
import atexit
25+
import urlparse
2526

2627
#
2728
# If you want to switch to hg-git compatibility mode:
@@ -50,6 +51,7 @@ import atexit
5051

5152
NAME_RE = re.compile('^([^<>]+)')
5253
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
54+
EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
5355
AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
5456
RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
5557

@@ -73,6 +75,12 @@ def hgmode(mode):
7375
def hghex(node):
7476
return hg.node.hex(node)
7577

78+
def hgref(ref):
79+
return ref.replace('___', ' ')
80+
81+
def gitref(ref):
82+
return ref.replace(' ', '___')
83+
7684
def get_config(config):
7785
cmd = ['git', 'config', '--get', config]
7886
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
@@ -118,6 +126,10 @@ class Marks:
118126
def to_rev(self, mark):
119127
return self.rev_marks[mark]
120128

129+
def next_mark(self):
130+
self.last_mark += 1
131+
return self.last_mark
132+
121133
def get_mark(self, rev):
122134
self.last_mark += 1
123135
self.marks[str(rev)] = self.last_mark
@@ -129,7 +141,7 @@ class Marks:
129141
self.last_mark = mark
130142

131143
def is_marked(self, rev):
132-
return self.marks.has_key(str(rev))
144+
return str(rev) in self.marks
133145

134146
def get_tip(self, branch):
135147
return self.tips.get(branch, 0)
@@ -210,20 +222,38 @@ def fix_file_path(path):
210222
return path
211223
return os.path.relpath(path, '/')
212224

213-
def export_file(fc):
214-
d = fc.data()
215-
path = fix_file_path(fc.path())
216-
print "M %s inline %s" % (gitmode(fc.flags()), path)
217-
print "data %d" % len(d)
218-
print d
225+
def export_files(files):
226+
global marks, filenodes
227+
228+
final = []
229+
for f in files:
230+
fid = node.hex(f.filenode())
231+
232+
if fid in filenodes:
233+
mark = filenodes[fid]
234+
else:
235+
mark = marks.next_mark()
236+
filenodes[fid] = mark
237+
d = f.data()
238+
239+
print "blob"
240+
print "mark :%u" % mark
241+
print "data %d" % len(d)
242+
print d
243+
244+
path = fix_file_path(f.path())
245+
final.append((gitmode(f.flags()), mark, path))
246+
247+
return final
219248

220249
def get_filechanges(repo, ctx, parent):
221250
modified = set()
222251
added = set()
223252
removed = set()
224253

225-
cur = ctx.manifest()
254+
# load earliest manifest first for caching reasons
226255
prev = repo[parent].manifest().copy()
256+
cur = ctx.manifest()
227257

228258
for fn in cur:
229259
if fn in prev:
@@ -244,9 +274,14 @@ def fixup_user_git(user):
244274
name = m.group(1)
245275
mail = m.group(2).strip()
246276
else:
247-
m = NAME_RE.match(user)
277+
m = EMAIL_RE.match(user)
248278
if m:
249-
name = m.group(1).strip()
279+
name = m.group(1)
280+
mail = m.group(2)
281+
else:
282+
m = NAME_RE.match(user)
283+
if m:
284+
name = m.group(1).strip()
250285
return (name, mail)
251286

252287
def fixup_user_hg(user):
@@ -298,6 +333,12 @@ def get_repo(url, alias):
298333
except subprocess.CalledProcessError:
299334
pass
300335

336+
try:
337+
mod = extensions.load(myui, 'hgext.schemes', None)
338+
mod.extsetup(myui)
339+
except ImportError:
340+
pass
341+
301342
if hg.islocal(url):
302343
repo = hg.repository(myui, url)
303344
else:
@@ -393,6 +434,8 @@ def export_ref(repo, name, kind, head):
393434
if len(parents) == 0 and rev:
394435
print 'reset %s/%s' % (prefix, ename)
395436

437+
modified_final = export_files(c.filectx(f) for f in modified)
438+
396439
print "commit %s/%s" % (prefix, ename)
397440
print "mark :%d" % (marks.get_mark(rev))
398441
print "author %s" % (author)
@@ -405,8 +448,8 @@ def export_ref(repo, name, kind, head):
405448
if len(parents) > 1:
406449
print "merge :%s" % (rev_to_mark(parents[1]))
407450

408-
for f in modified:
409-
export_file(c.filectx(f))
451+
for f in modified_final:
452+
print "M %s :%u %s" % f
410453
for f in removed:
411454
print "D %s" % (fix_file_path(f))
412455
print
@@ -424,10 +467,10 @@ def export_ref(repo, name, kind, head):
424467
marks.set_tip(ename, rev)
425468

426469
def export_tag(repo, tag):
427-
export_ref(repo, tag, 'tags', repo[tag])
470+
export_ref(repo, tag, 'tags', repo[hgref(tag)])
428471

429472
def export_bookmark(repo, bmark):
430-
head = bmarks[bmark]
473+
head = bmarks[hgref(bmark)]
431474
export_ref(repo, bmark, 'bookmarks', head)
432475

433476
def export_branch(repo, branch):
@@ -456,19 +499,24 @@ def do_capabilities(parser):
456499

457500
print
458501

502+
def branch_tip(repo, branch):
503+
# older versions of mercurial don't have this
504+
if hasattr(repo, 'branchtip'):
505+
return repo.branchtip(branch)
506+
else:
507+
return repo.branchtags()[branch]
508+
459509
def get_branch_tip(repo, branch):
460510
global branches
461511

462-
heads = branches.get(branch, None)
512+
heads = branches.get(hgref(branch), None)
463513
if not heads:
464514
return None
465515

466516
# verify there's only one head
467517
if (len(heads) > 1):
468518
warn("Branch '%s' has more than one head, consider merging" % branch)
469-
# older versions of mercurial don't have this
470-
if hasattr(repo, "branchtip"):
471-
return repo.branchtip(branch)
519+
return branch_tip(repo, hgref(branch))
472520

473521
return heads[0]
474522

@@ -490,6 +538,7 @@ def list_head(repo, cur):
490538
head = 'master'
491539
bmarks[head] = node
492540

541+
head = gitref(head)
493542
print "@refs/heads/%s HEAD" % head
494543
g_head = (head, node)
495544

@@ -511,15 +560,15 @@ def do_list(parser):
511560
branches[branch] = heads
512561

513562
for branch in branches:
514-
print "? refs/heads/branches/%s" % branch
563+
print "? refs/heads/branches/%s" % gitref(branch)
515564

516565
for bmark in bmarks:
517-
print "? refs/heads/%s" % bmark
566+
print "? refs/heads/%s" % gitref(bmark)
518567

519568
for tag, node in repo.tagslist():
520569
if tag == 'tip':
521570
continue
522-
print "? refs/tags/%s" % tag
571+
print "? refs/tags/%s" % gitref(tag)
523572

524573
print
525574

@@ -603,6 +652,10 @@ def parse_commit(parser):
603652
if parser.check('merge'):
604653
die('octopus merges are not supported yet')
605654

655+
# fast-export adds an extra newline
656+
if data[-1] == '\n':
657+
data = data[:-1]
658+
606659
files = {}
607660

608661
for line in parser:
@@ -656,7 +709,8 @@ def parse_commit(parser):
656709

657710
# Check if the ref is supposed to be a named branch
658711
if ref.startswith('refs/heads/branches/'):
659-
extra['branch'] = ref[len('refs/heads/branches/'):]
712+
branch = ref[len('refs/heads/branches/'):]
713+
extra['branch'] = hgref(branch)
660714

661715
if mode == 'hg':
662716
i = data.find('\n--HG--\n')
@@ -716,7 +770,40 @@ def parse_tag(parser):
716770
data = parser.get_data()
717771
parser.next()
718772

719-
# nothing to do
773+
parsed_tags[name] = (tagger, data)
774+
775+
def write_tag(repo, tag, node, msg, author):
776+
branch = repo[node].branch()
777+
tip = branch_tip(repo, branch)
778+
tip = repo[tip]
779+
780+
def getfilectx(repo, memctx, f):
781+
try:
782+
fctx = tip.filectx(f)
783+
data = fctx.data()
784+
except error.ManifestLookupError:
785+
data = ""
786+
content = data + "%s %s\n" % (hghex(node), tag)
787+
return context.memfilectx(f, content, False, False, None)
788+
789+
p1 = tip.hex()
790+
p2 = '\0' * 20
791+
if not author:
792+
author = (None, 0, 0)
793+
user, date, tz = author
794+
795+
ctx = context.memctx(repo, (p1, p2), msg,
796+
['.hgtags'], getfilectx,
797+
user, (date, tz), {'branch' : branch})
798+
799+
tmp = encoding.encoding
800+
encoding.encoding = 'utf-8'
801+
802+
tagnode = repo.commitctx(ctx)
803+
804+
encoding.encoding = tmp
805+
806+
return tagnode
720807

721808
def do_export(parser):
722809
global parsed_refs, bmarks, peer
@@ -741,18 +828,27 @@ def do_export(parser):
741828

742829
for ref, node in parsed_refs.iteritems():
743830
if ref.startswith('refs/heads/branches'):
831+
branch = ref[len('refs/heads/branches/'):]
832+
if branch in branches and node in branches[branch]:
833+
# up to date
834+
continue
744835
print "ok %s" % ref
745836
elif ref.startswith('refs/heads/'):
746837
bmark = ref[len('refs/heads/'):]
747838
p_bmarks.append((bmark, node))
748839
continue
749840
elif ref.startswith('refs/tags/'):
750841
tag = ref[len('refs/tags/'):]
842+
tag = hgref(tag)
843+
author, msg = parsed_tags.get(tag, (None, None))
751844
if mode == 'git':
752-
msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
753-
parser.repo.tag([tag], node, msg, False, None, {})
845+
if not msg:
846+
msg = 'Added tag %s for changeset %s' % (tag, hghex(node[:6]));
847+
write_tag(parser.repo, tag, node, msg, author)
754848
else:
755-
parser.repo.tag([tag], node, None, True, None, {})
849+
fp = parser.repo.opener('localtags', 'a')
850+
fp.write('%s %s\n' % (hghex(node), tag))
851+
fp.close()
756852
print "ok %s" % ref
757853
else:
758854
# transport-helper/fast-export bugs
@@ -771,6 +867,9 @@ def do_export(parser):
771867
else:
772868
old = ''
773869

870+
if old == new:
871+
continue
872+
774873
if bmark == 'master' and 'master' not in parser.repo._bookmarks:
775874
# fake bookmark
776875
pass
@@ -782,6 +881,8 @@ def do_export(parser):
782881
continue
783882

784883
if peer:
884+
rb = peer.listkeys('bookmarks')
885+
old = rb.get(bmark, '')
785886
if not peer.pushkey('bookmarks', bmark, old, new):
786887
print "error %s" % ref
787888
continue
@@ -791,18 +892,20 @@ def do_export(parser):
791892
print
792893

793894
def fix_path(alias, repo, orig_url):
794-
repo_url = util.url(repo.url())
795-
url = util.url(orig_url)
796-
if str(url) == str(repo_url):
895+
url = urlparse.urlparse(orig_url, 'file')
896+
if url.scheme != 'file' or os.path.isabs(url.path):
797897
return
798-
cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % repo_url]
898+
abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
899+
cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
799900
subprocess.call(cmd)
800901

801902
def main(args):
802903
global prefix, dirname, branches, bmarks
803904
global marks, blob_marks, parsed_refs
804905
global peer, mode, bad_mail, bad_name
805906
global track_branches, force_push, is_tmp
907+
global parsed_tags
908+
global filenodes
806909

807910
alias = args[1]
808911
url = args[2]
@@ -845,6 +948,8 @@ def main(args):
845948
blob_marks = {}
846949
parsed_refs = {}
847950
marks = None
951+
parsed_tags = {}
952+
filenodes = {}
848953

849954
repo = get_repo(url, alias)
850955
prefix = 'refs/hg/%s' % alias

contrib/remote-helpers/test-hg.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ test_expect_success 'authors' '
137137
138138
author_test alpha "" "H G Wells <wells@example.com>" &&
139139
author_test beta "test" "test <unknown>" &&
140-
author_test beta "test <test@example.com> (comment)" "test <unknown>" &&
140+
author_test beta "test <test@example.com> (comment)" "test <test@example.com>" &&
141141
author_test gamma "<test@example.com>" "Unknown <test@example.com>" &&
142142
author_test delta "name<test@example.com>" "name <test@example.com>" &&
143-
author_test epsilon "name <test@example.com" "name <unknown>" &&
143+
author_test epsilon "name <test@example.com" "name <test@example.com>" &&
144144
author_test zeta " test " "test <unknown>" &&
145145
author_test eta "test < test@example.com >" "test <test@example.com>" &&
146-
author_test theta "test >test@example.com>" "test <unknown>" &&
146+
author_test theta "test >test@example.com>" "test <test@example.com>" &&
147147
author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
148-
author_test kappa "test@example.com" "test@example.com <unknown>"
148+
author_test kappa "test@example.com" "Unknown <test@example.com>"
149149
) &&
150150
151151
git clone "hg::$PWD/hgrepo" gitrepo &&

0 commit comments

Comments
 (0)