mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 08:18:15 +03:00
Support for single-directory clones.
This commit is contained in:
parent
2bd7b60221
commit
b0da25dd93
@ -66,6 +66,8 @@ wrapcmds = { # cmd: generic, target, fixdoc, ppopts, opts
|
||||
'file mapping Subversion usernames to Mercurial authors'),
|
||||
('', 'filemap', '',
|
||||
'file containing rules for remapping Subversion repository paths'),
|
||||
('', 'layout', 'auto', ('import standard layout or single '
|
||||
'directory? Can be standard, single, or auto.')),
|
||||
]),
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ class RevisionData(object):
|
||||
self.ui.flush()
|
||||
if p[-1] == '/':
|
||||
dir = p[len(root):]
|
||||
new = [dir + f for f, k in svn.list_files(dir, r) if k == 'f']
|
||||
new = [p + f for f, k in svn.list_files(dir, r) if k == 'f']
|
||||
files.update(new)
|
||||
else:
|
||||
files.add(p[len(root):])
|
||||
@ -107,7 +107,7 @@ class RevisionData(object):
|
||||
if i % 50 == 0:
|
||||
svn.init_ra_and_client()
|
||||
i += 1
|
||||
data, mode = svn.get_file(p, r)
|
||||
data, mode = svn.get_file(p[len(root):], r)
|
||||
self.set(p, data, 'x' in mode, 'l' in mode)
|
||||
|
||||
self.missing = set()
|
||||
@ -304,7 +304,7 @@ class HgEditor(delta.Editor):
|
||||
def open_directory(self, path, parent_baton, base_revision, dir_pool=None):
|
||||
self.current.batons[path] = path
|
||||
p_, branch = self.meta.split_branch_path(path)[:2]
|
||||
if p_ == '':
|
||||
if p_ == '' or (self.meta.layout == 'single' and p_):
|
||||
self.current.emptybranches[branch] = False
|
||||
return path
|
||||
|
||||
|
@ -29,14 +29,13 @@ def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
current.findmissing(svn)
|
||||
|
||||
# update externals
|
||||
|
||||
if current.externals:
|
||||
# TODO fix and re-enable externals for single-directory clones
|
||||
if current.externals and not meta.layout == 'single':
|
||||
|
||||
# accumulate externals records for all branches
|
||||
revnum = current.rev.revnum
|
||||
branches = {}
|
||||
for path, entry in current.externals.iteritems():
|
||||
|
||||
if not meta.is_path_valid(path):
|
||||
ui.warn('WARNING: Invalid path %s in externals\n' % path)
|
||||
continue
|
||||
@ -56,7 +55,9 @@ def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
|
||||
# register externals file changes
|
||||
for bp, external in branches.iteritems():
|
||||
path = bp + '/.hgsvnexternals'
|
||||
if bp and bp[-1] != '/':
|
||||
bp += '/'
|
||||
path = (bp and bp + '.hgsvnexternals') or '.hgsvnexternals'
|
||||
if external:
|
||||
current.set(path, external.write(), False, False)
|
||||
else:
|
||||
|
@ -103,6 +103,8 @@ def diff_branchrev(ui, svn, meta, branch, r, parentctx):
|
||||
error.
|
||||
"""
|
||||
def make_diff_path(branch):
|
||||
if meta.layout == 'single':
|
||||
return ''
|
||||
if branch == 'trunk' or branch is None:
|
||||
return 'trunk'
|
||||
elif branch.startswith('../'):
|
||||
@ -203,7 +205,10 @@ def diff_branchrev(ui, svn, meta, branch, r, parentctx):
|
||||
|
||||
for p in r.paths:
|
||||
if p.startswith(diff_path) and r.paths[p].action == 'D':
|
||||
p2 = p[len(diff_path)+1:].strip('/')
|
||||
if diff_path:
|
||||
p2 = p[len(diff_path)+1:].strip('/')
|
||||
else:
|
||||
p2 = p
|
||||
if p2 in parentctx:
|
||||
files_data[p2] = None
|
||||
continue
|
||||
@ -221,7 +226,10 @@ def diff_branchrev(ui, svn, meta, branch, r, parentctx):
|
||||
raise IOError()
|
||||
|
||||
if path in binary_files:
|
||||
data, mode = svn.get_file(diff_path + '/' + path, r.revnum)
|
||||
pa = path
|
||||
if diff_path:
|
||||
pa = diff_path + '/' + path
|
||||
data, mode = svn.get_file(pa, r.revnum)
|
||||
isexe = 'x' in mode
|
||||
islink = 'l' in mode
|
||||
else:
|
||||
@ -236,6 +244,10 @@ def diff_branchrev(ui, svn, meta, branch, r, parentctx):
|
||||
data = parentctx[path].data()
|
||||
|
||||
copied = copies.get(path)
|
||||
# TODO this branch feels like it should not be required,
|
||||
# and this may actually imply a bug in getcopies
|
||||
if copied not in parentctx.manifest():
|
||||
copied = None
|
||||
return context.memfilectx(path=path, data=data, islink=islink,
|
||||
isexec=isexe, copied=copied)
|
||||
|
||||
@ -347,7 +359,7 @@ def fetch_externals(svn, branchpath, r, parentctx):
|
||||
dirs.update([p for p,k in svn.list_files(branchpath, r.revnum) if k == 'd'])
|
||||
dirs.add('')
|
||||
else:
|
||||
branchprefix = branchpath + '/'
|
||||
branchprefix = (branchpath and branchpath + '/') or branchpath
|
||||
for path, e in r.paths.iteritems():
|
||||
if e.action == 'D':
|
||||
continue
|
||||
@ -369,7 +381,8 @@ def fetch_externals(svn, branchpath, r, parentctx):
|
||||
# Retrieve new or updated values
|
||||
for dir in dirs:
|
||||
try:
|
||||
values = svn.list_props(branchpath + '/' + dir, r.revnum)
|
||||
dpath = (branchpath and branchpath + '/' + dir) or dir
|
||||
values = svn.list_props(dpath, r.revnum)
|
||||
externals[dir] = values.get('svn:externals', '')
|
||||
except IOError:
|
||||
externals[dir] = ''
|
||||
@ -394,7 +407,7 @@ def fetch_branchrev(svn, meta, branch, branchpath, r, parentctx):
|
||||
if kind == 'f':
|
||||
files.append(path)
|
||||
else:
|
||||
branchprefix = branchpath + '/'
|
||||
branchprefix = (branchpath and branchpath + '/') or ''
|
||||
for path, e in r.paths.iteritems():
|
||||
if not path.startswith(branchprefix):
|
||||
continue
|
||||
@ -423,7 +436,10 @@ def fetch_branchrev(svn, meta, branch, branchpath, r, parentctx):
|
||||
copies = getcopies(svn, meta, branch, branchpath, r, files, parentctx)
|
||||
|
||||
def filectxfn(repo, memctx, path):
|
||||
data, mode = svn.get_file(branchpath + '/' + path, r.revnum)
|
||||
svnpath = path
|
||||
if branchpath:
|
||||
svnpath = branchpath + '/' + path
|
||||
data, mode = svn.get_file(svnpath, r.revnum)
|
||||
isexec = 'x' in mode
|
||||
islink = 'l' in mode
|
||||
copied = copies.get(path)
|
||||
@ -509,7 +525,6 @@ def branches_in_paths(meta, tbdelta, paths, revnum, checkpath, listdir):
|
||||
if branchname and branchname.startswith('../'):
|
||||
continue
|
||||
branches[branchname] = branchpath
|
||||
|
||||
return branches
|
||||
|
||||
def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
@ -549,7 +564,6 @@ def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
date = meta.fixdate(r.date)
|
||||
check_deleted_branches = set(tbdelta['branches'][1])
|
||||
for b in branches:
|
||||
|
||||
parentctx = meta.repo[meta.get_parent_revision(r.revnum, b)]
|
||||
if parentctx.branch() != (b or 'default'):
|
||||
check_deleted_branches.add(b)
|
||||
@ -570,9 +584,10 @@ def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
files_touched, filectxfn2 = fetch_branchrev(
|
||||
svn, meta, b, branches[b], r, parentctx)
|
||||
|
||||
externals = fetch_externals(svn, branches[b], r, parentctx)
|
||||
if externals is not None:
|
||||
files_touched.append('.hgsvnexternals')
|
||||
if meta.layout != 'single':
|
||||
externals = fetch_externals(svn, branches[b], r, parentctx)
|
||||
if externals is not None:
|
||||
files_touched.append('.hgsvnexternals')
|
||||
|
||||
def filectxfn(repo, memctx, path):
|
||||
if path == '.hgsvnexternals':
|
||||
|
@ -32,17 +32,23 @@ def verify(ui, repo, *args, **opts):
|
||||
if args:
|
||||
url = args[0]
|
||||
svn = svnrepo.svnremoterepo(ui, url).svn
|
||||
meta = repo.svnmeta(svn.uuid, svn.subdir)
|
||||
|
||||
btypes = {'default': 'trunk'}
|
||||
branchpath = btypes.get(ctx.branch(), 'branches/%s' % ctx.branch())
|
||||
branchpath = posixpath.normpath(branchpath)
|
||||
if meta.layout == 'standard':
|
||||
branchpath = btypes.get(ctx.branch(), 'branches/%s' % ctx.branch())
|
||||
else:
|
||||
branchpath = ''
|
||||
svnfiles = set()
|
||||
result = 0
|
||||
for fn, type in svn.list_files(branchpath, srev):
|
||||
for fn, type in svn.list_files(posixpath.normpath(branchpath), srev):
|
||||
if type != 'f':
|
||||
continue
|
||||
svnfiles.add(fn)
|
||||
data, mode = svn.get_file(branchpath + '/' + fn, srev)
|
||||
fp = fn
|
||||
if branchpath:
|
||||
fp = branchpath + '/' + fn
|
||||
data, mode = svn.get_file(posixpath.normpath(fp), srev)
|
||||
fctx = ctx[fn]
|
||||
dmatch = fctx.data() == data
|
||||
mmatch = fctx.flags() == mode
|
||||
@ -87,6 +93,8 @@ def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
|
||||
os.unlink(maps.TagMap.filepath(repo))
|
||||
tags = maps.TagMap(repo)
|
||||
|
||||
layout = None
|
||||
|
||||
skipped = set()
|
||||
|
||||
for rev in repo:
|
||||
@ -126,6 +134,18 @@ def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
|
||||
assert revpath.startswith(subdir), ('That does not look like the '
|
||||
'right location in the repo.')
|
||||
|
||||
if layout is None:
|
||||
if (subdir or '/') == revpath:
|
||||
layout = 'single'
|
||||
else:
|
||||
layout = 'standard'
|
||||
f = open(os.path.join(svnmetadir, 'layout'), 'w')
|
||||
f.write(layout)
|
||||
f.close()
|
||||
elif layout == 'single':
|
||||
assert (subdir or '/') == revpath, ('Possible layout detection'
|
||||
' defect in replay')
|
||||
|
||||
# write repository uuid if required
|
||||
if uuid is None:
|
||||
uuid = convinfo[4:40]
|
||||
@ -142,17 +162,17 @@ def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
|
||||
|
||||
# find commitpath, write to revmap
|
||||
commitpath = revpath[len(subdir)+1:]
|
||||
bp = posixpath.normpath('/'.join([subdir, 'branches', ctx.branch()]))
|
||||
if revpath == bp:
|
||||
commitpath = ctx.branch()
|
||||
elif commitpath == 'trunk':
|
||||
commitpath = ''
|
||||
elif commitpath.startswith('tags'):
|
||||
if ctx.extra().get('close'):
|
||||
continue
|
||||
commitpath = '../' + commitpath
|
||||
if layout == 'standard':
|
||||
if commitpath.startswith('branches/'):
|
||||
commitpath = commitpath[len('branches/'):]
|
||||
elif commitpath == 'trunk':
|
||||
commitpath = ''
|
||||
else:
|
||||
if commitpath.startswith('tags/') and ctx.extra().get('close'):
|
||||
continue
|
||||
commitpath = '../' + commitpath
|
||||
else:
|
||||
assert False, 'unhandled rev %s: %s' % (rev, convinfo)
|
||||
commitpath = ''
|
||||
revmap.write('%s %s %s\n' % (revision, ctx.hex(), commitpath))
|
||||
|
||||
revision = int(revision)
|
||||
|
@ -69,6 +69,13 @@ class SVNMeta(object):
|
||||
f.close()
|
||||
else:
|
||||
self.tag_locations = tag_locations
|
||||
if os.path.exists(self.layoutfile):
|
||||
f = open(self.layoutfile)
|
||||
self._layout = f.read().strip()
|
||||
f.close()
|
||||
self.repo.ui.setconfig('hgsubversion', 'layout', self._layout)
|
||||
else:
|
||||
self._layout = None
|
||||
pickle_atomic(self.tag_locations, self.tag_locations_file,
|
||||
self.meta_data_dir)
|
||||
# ensure nested paths are handled properly
|
||||
@ -82,6 +89,21 @@ class SVNMeta(object):
|
||||
self.lastdate = '1970-01-01 00:00:00 -0000'
|
||||
self.filemap = maps.FileMap(repo)
|
||||
|
||||
@property
|
||||
def layout(self):
|
||||
# this method can't determine the layout, but it needs to be
|
||||
# resolved into something other than auto before this ever
|
||||
# gets called
|
||||
if not self._layout or self._layout == 'auto':
|
||||
lo = self.repo.ui.config('hgsubversion', 'layout', default='auto')
|
||||
if lo == 'auto':
|
||||
raise hgutil.Abort('layout not yet determined')
|
||||
self._layout = lo
|
||||
f = open(self.layoutfile, 'w')
|
||||
f.write(self._layout)
|
||||
f.close()
|
||||
return self._layout
|
||||
|
||||
@property
|
||||
def editor(self):
|
||||
if not hasattr(self, '_editor'):
|
||||
@ -127,6 +149,10 @@ class SVNMeta(object):
|
||||
def authors_file(self):
|
||||
return os.path.join(self.meta_data_dir, 'authors')
|
||||
|
||||
@property
|
||||
def layoutfile(self):
|
||||
return os.path.join(self.meta_data_dir, 'layout')
|
||||
|
||||
def fixdate(self, date):
|
||||
if date is not None:
|
||||
date = date.replace('T', ' ').replace('Z', '').split('.')[0]
|
||||
@ -145,6 +171,8 @@ class SVNMeta(object):
|
||||
def localname(self, path):
|
||||
"""Compute the local name for a branch located at path.
|
||||
"""
|
||||
if self.layout == 'single':
|
||||
return 'default'
|
||||
if path == 'trunk':
|
||||
return None
|
||||
elif path.startswith('branches/'):
|
||||
@ -152,6 +180,8 @@ class SVNMeta(object):
|
||||
return '../%s' % path
|
||||
|
||||
def remotename(self, branch):
|
||||
if self.layout == 'single':
|
||||
return ''
|
||||
if branch == 'default' or branch is None:
|
||||
return 'trunk'
|
||||
elif branch.startswith('../'):
|
||||
@ -160,23 +190,27 @@ class SVNMeta(object):
|
||||
|
||||
def genextra(self, revnum, branch):
|
||||
extra = {}
|
||||
branchpath = 'trunk'
|
||||
if branch:
|
||||
extra['branch'] = branch
|
||||
if branch.startswith('../'):
|
||||
branchpath = branch[3:]
|
||||
else:
|
||||
branchpath = 'branches/%s' % branch
|
||||
|
||||
subdir = self.subdir
|
||||
if subdir and subdir[-1] == '/':
|
||||
subdir = subdir[:-1]
|
||||
if subdir and subdir[0] != '/':
|
||||
subdir = '/' + subdir
|
||||
|
||||
if self.layout == 'single':
|
||||
path = subdir or '/'
|
||||
else:
|
||||
branchpath = 'trunk'
|
||||
if branch:
|
||||
extra['branch'] = branch
|
||||
if branch.startswith('../'):
|
||||
branchpath = branch[3:]
|
||||
else:
|
||||
branchpath = 'branches/%s' % branch
|
||||
path = '%s/%s' % (subdir , branchpath)
|
||||
|
||||
extra['convert_revision'] = 'svn:%(uuid)s%(path)s@%(rev)s' % {
|
||||
'uuid': self.uuid,
|
||||
'path': '%s/%s' % (subdir , branchpath),
|
||||
'path': path,
|
||||
'rev': revnum,
|
||||
}
|
||||
return extra
|
||||
@ -185,6 +219,8 @@ class SVNMeta(object):
|
||||
'''Normalize a path to strip of leading slashes and our subdir if we
|
||||
have one.
|
||||
'''
|
||||
if self.subdir and path == self.subdir[:-1]:
|
||||
return ''
|
||||
if path and path[0] == '/':
|
||||
path = path[1:]
|
||||
if path and path.startswith(self.subdir):
|
||||
@ -200,6 +236,8 @@ class SVNMeta(object):
|
||||
Note that it's only a tag if it was copied from the path '' in a branch
|
||||
(or tag) we have, for our purposes.
|
||||
"""
|
||||
if self.layout == 'single':
|
||||
return False
|
||||
path = self.normalize(path)
|
||||
for tagspath in self.tag_locations:
|
||||
onpath = path.startswith(tagspath)
|
||||
@ -217,9 +255,11 @@ class SVNMeta(object):
|
||||
|
||||
If existing=True, will return None, None, None if the file isn't on some known
|
||||
branch. If existing=False, then it will guess what the branch would be if it were
|
||||
known.
|
||||
known. Server-side branch path should be relative to our subdirectory.
|
||||
"""
|
||||
path = self.normalize(path)
|
||||
if self.layout == 'single':
|
||||
return (path, None, '')
|
||||
if self.is_path_tag(path):
|
||||
tag = self.is_path_tag(path)
|
||||
matched = [t for t in self.tags.iterkeys() if tag.startswith(t+'/')]
|
||||
@ -325,6 +365,19 @@ class SVNMeta(object):
|
||||
return int(revnum), self.localname(self.normalize(branch))
|
||||
|
||||
def update_branch_tag_map_for_rev(self, revision):
|
||||
"""Given a revision object, determine changes to branches and tags.
|
||||
|
||||
Returns: a dict of {
|
||||
'tags': (added_tags, rmtags),
|
||||
'branches': (added_branches, self.closebranches),
|
||||
} where adds are dicts where the keys are branch/tag names and
|
||||
values are the place the branch/tag came from. The deletions are
|
||||
sets of the deleted branches.
|
||||
"""
|
||||
if self.layout == 'single':
|
||||
return {'tags': ({}, set()),
|
||||
'branches': ({None: (None, 0, -1), }, set()),
|
||||
}
|
||||
paths = revision.paths
|
||||
added_branches = {}
|
||||
added_tags = {}
|
||||
|
@ -345,7 +345,8 @@ class SubversionRepo(object):
|
||||
dir: the directory to list, no leading slash
|
||||
rev: the revision at which to list the directory, defaults to HEAD
|
||||
"""
|
||||
if dir[-1] == '/':
|
||||
# TODO this should just not accept leading slashes like the docstring says
|
||||
if dir and dir[-1] == '/':
|
||||
dir = dir[:-1]
|
||||
if revision is None:
|
||||
revision = self.HEAD
|
||||
@ -555,6 +556,7 @@ class SubversionRepo(object):
|
||||
otherwise. If the file does not exist at this revision, raise
|
||||
IOError.
|
||||
"""
|
||||
assert not path.startswith('/')
|
||||
mode = ''
|
||||
try:
|
||||
out = cStringIO.StringIO()
|
||||
@ -633,7 +635,9 @@ class SubversionRepo(object):
|
||||
def path2url(self, path):
|
||||
"""Build svn URL for path, URL-escaping path.
|
||||
"""
|
||||
assert path[0] != '/'
|
||||
if not path or path == '.':
|
||||
return self.svn_url
|
||||
assert path[0] != '/', path
|
||||
return '/'.join((self.svn_url,
|
||||
urllib.quote(path).rstrip('/'),
|
||||
))
|
||||
|
@ -228,6 +228,16 @@ def pull(repo, source, heads=[], force=False):
|
||||
svn = svnrepo.svnremoterepo(repo.ui, svn_url).svn
|
||||
meta = repo.svnmeta(svn.uuid, svn.subdir)
|
||||
|
||||
layout = repo.ui.config('hgsubversion', 'layout', 'auto')
|
||||
if layout == 'auto':
|
||||
rootlist = svn.list_dir('', revision=(stopat_rev or None))
|
||||
if sum(map(lambda x: x in rootlist, ('branches', 'tags', 'trunk'))):
|
||||
layout = 'standard'
|
||||
else:
|
||||
layout = 'single'
|
||||
repo.ui.setconfig('hgsubversion', 'layout', layout)
|
||||
repo.ui.note('using %s layout\n' % layout)
|
||||
|
||||
start = max(meta.revmap.seen, skipto_rev)
|
||||
initializing_repo = meta.revmap.seen <= 0
|
||||
ui = repo.ui
|
||||
@ -351,9 +361,10 @@ optionmap = {
|
||||
'defaulthost': ('hgsubversion', 'defaulthost'),
|
||||
'defaultauthors': ('hgsubversion', 'defaultauthors'),
|
||||
'usebranchnames': ('hgsubversion', 'usebranchnames'),
|
||||
'layout': ('hgsubversion', 'layout'),
|
||||
}
|
||||
|
||||
dontretain = { 'hgsubversion': set(['authormap', 'filemap']) }
|
||||
dontretain = { 'hgsubversion': set(['authormap', 'filemap', 'layout', ]) }
|
||||
|
||||
def clone(orig, ui, source, dest=None, **opts):
|
||||
"""
|
||||
|
@ -9,9 +9,9 @@ from tests import test_util
|
||||
from hgsubversion import wrappers
|
||||
|
||||
|
||||
def _do_case(self, name):
|
||||
def _do_case(self, name, layout):
|
||||
subdir = test_util.subdir.get(name, '')
|
||||
self._load_fixture_and_fetch(name, subdir=subdir, stupid=False)
|
||||
self._load_fixture_and_fetch(name, subdir=subdir, stupid=False, layout=layout)
|
||||
assert len(self.repo) > 0, 'Repo had no changes, maybe you need to add a subdir entry in test_util?'
|
||||
wc2_path = self.wc_path + '_stupid'
|
||||
u = ui.ui()
|
||||
@ -19,22 +19,28 @@ def _do_case(self, name):
|
||||
if subdir:
|
||||
checkout_path += '/' + subdir
|
||||
u.setconfig('hgsubversion', 'stupid', '1')
|
||||
u.setconfig('hgsubversion', 'layout', layout)
|
||||
hg.clone(u, test_util.fileurl(checkout_path), wc2_path, update=False)
|
||||
if layout == 'single':
|
||||
self.assertEqual(len(self.repo.heads()), 1)
|
||||
self.repo2 = hg.repository(ui.ui(), wc2_path)
|
||||
self.assertEqual(self.repo.heads(), self.repo2.heads())
|
||||
|
||||
|
||||
def buildmethod(case, name):
|
||||
m = lambda self: self._do_case(case)
|
||||
def buildmethod(case, name, layout):
|
||||
m = lambda self: self._do_case(case, layout)
|
||||
m.__name__ = name
|
||||
m.__doc__ = 'Test stupid produces same as real on %s.' % case
|
||||
m.__doc__ = 'Test stupid produces same as real on %s. (%s)' % (case, layout)
|
||||
return m
|
||||
|
||||
attrs = {'_do_case': _do_case,
|
||||
}
|
||||
for case in (f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')):
|
||||
name = 'test_' + case[:-len('.svndump')]
|
||||
attrs[name] = buildmethod(case, name)
|
||||
attrs[name] = buildmethod(case, name, 'auto')
|
||||
name += '_single'
|
||||
attrs[name] = buildmethod(case, name, 'single')
|
||||
|
||||
StupidPullTests = type('StupidPullTests', (test_util.TestBase, ), attrs)
|
||||
|
||||
|
||||
|
@ -14,19 +14,19 @@ from mercurial import ui
|
||||
|
||||
from hgsubversion import svncommands
|
||||
|
||||
def _do_case(self, name, stupid):
|
||||
def _do_case(self, name, stupid, layout):
|
||||
subdir = test_util.subdir.get(name, '')
|
||||
repo = self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid)
|
||||
repo = self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout)
|
||||
assert len(self.repo) > 0
|
||||
for i in repo:
|
||||
ctx = repo[i]
|
||||
self.assertEqual(svncommands.verify(repo.ui, repo, rev=ctx.node()), 0)
|
||||
|
||||
def buildmethod(case, name, stupid):
|
||||
m = lambda self: self._do_case(case, stupid)
|
||||
def buildmethod(case, name, stupid, layout):
|
||||
m = lambda self: self._do_case(case, stupid, layout)
|
||||
m.__name__ = name
|
||||
bits = case, stupid and 'stupid' or 'real'
|
||||
m.__doc__ = 'Test verify on %s with %s replay.' % bits
|
||||
bits = case, stupid and 'stupid' or 'real', layout
|
||||
m.__doc__ = 'Test verify on %s with %s replay. (%s)' % bits
|
||||
return m
|
||||
|
||||
attrs = {'_do_case': _do_case}
|
||||
@ -35,10 +35,16 @@ for case in fixtures:
|
||||
# this fixture results in an empty repository, don't use it
|
||||
if case == 'project_root_not_repo_root.svndump':
|
||||
continue
|
||||
name = 'test_' + case[:-len('.svndump')]
|
||||
attrs[name] = buildmethod(case, name, False)
|
||||
name += '_stupid'
|
||||
attrs[name] = buildmethod(case, name, True)
|
||||
bname = 'test_' + case[:-len('.svndump')]
|
||||
attrs[bname] = buildmethod(case, bname, False, 'standard')
|
||||
name = bname + '_stupid'
|
||||
attrs[name] = buildmethod(case, name, True, 'standard')
|
||||
name = bname + '_single'
|
||||
attrs[name] = buildmethod(case, name, False, 'single')
|
||||
# Disabled because the "stupid and real are the same" tests
|
||||
# verify this plus even more.
|
||||
# name = bname + '_single_stupid'
|
||||
# attrs[name] = buildmethod(case, name, True, 'single')
|
||||
|
||||
VerifyTests = type('VerifyTests', (test_util.TestBase,), attrs)
|
||||
|
||||
|
@ -10,9 +10,12 @@ from mercurial import ui
|
||||
from hgsubversion import svncommands
|
||||
from hgsubversion import svnmeta
|
||||
|
||||
def _do_case(self, name, stupid):
|
||||
def _do_case(self, name, stupid, single):
|
||||
subdir = test_util.subdir.get(name, '')
|
||||
self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid)
|
||||
layout = 'auto'
|
||||
if single:
|
||||
layout = 'single'
|
||||
self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout)
|
||||
assert len(self.repo) > 0
|
||||
wc2_path = self.wc_path + '_clone'
|
||||
u = ui.ui()
|
||||
@ -27,7 +30,7 @@ def _do_case(self, name, stupid):
|
||||
self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')),
|
||||
'no .hg/svn directory in the destination!')
|
||||
dest = hg.repository(u, os.path.dirname(dest.path))
|
||||
for tf in ('rev_map', 'uuid', 'tagmap', ):
|
||||
for tf in ('rev_map', 'uuid', 'tagmap', 'layout', ):
|
||||
stf = os.path.join(src.path, 'svn', tf)
|
||||
self.assertTrue(os.path.isfile(stf), '%r is missing!' % stf)
|
||||
dtf = os.path.join(dest.path, 'svn', tf)
|
||||
@ -54,11 +57,15 @@ def _do_case(self, name, stupid):
|
||||
self.assertEqual(srcinfo[2], destinfo[2])
|
||||
|
||||
|
||||
def buildmethod(case, name, stupid):
|
||||
m = lambda self: self._do_case(case, stupid)
|
||||
def buildmethod(case, name, stupid, single):
|
||||
m = lambda self: self._do_case(case, stupid, single)
|
||||
m.__name__ = name
|
||||
m.__doc__ = ('Test rebuildmeta on %s with %s replay.' %
|
||||
(case, (stupid and 'stupid') or 'real'))
|
||||
m.__doc__ = ('Test rebuildmeta on %s with %s replay. (%s)' %
|
||||
(case,
|
||||
(stupid and 'stupid') or 'real',
|
||||
(single and 'single') or 'standard',
|
||||
)
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
@ -68,10 +75,13 @@ for case in [f for f in os.listdir(test_util.FIXTURES) if f.endswith('.svndump')
|
||||
# this fixture results in an empty repository, don't use it
|
||||
if case == 'project_root_not_repo_root.svndump':
|
||||
continue
|
||||
name = 'test_' + case[:-len('.svndump')]
|
||||
attrs[name] = buildmethod(case, name, False)
|
||||
name += '_stupid'
|
||||
attrs[name] = buildmethod(case, name, True)
|
||||
bname = 'test_' + case[:-len('.svndump')]
|
||||
attrs[bname] = buildmethod(case, bname, False, False)
|
||||
name = bname + '_stupid'
|
||||
attrs[name] = buildmethod(case, name, True, False)
|
||||
name = bname + '_single'
|
||||
attrs[name] = buildmethod(case, name, False, True)
|
||||
|
||||
RebuildMetaTests = type('RebuildMetaTests', (test_util.TestBase, ), attrs)
|
||||
|
||||
|
||||
|
72
tests/test_single_dir_clone.py
Normal file
72
tests/test_single_dir_clone.py
Normal file
@ -0,0 +1,72 @@
|
||||
import shutil
|
||||
|
||||
import test_util
|
||||
|
||||
|
||||
class TestSingleDir(test_util.TestBase):
|
||||
def test_clone_single_dir_simple(self):
|
||||
repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
|
||||
stupid=False,
|
||||
layout='single',
|
||||
subdir='')
|
||||
self.assertEqual(repo.branchtags().keys(), ['default'])
|
||||
self.assertEqual(repo['tip'].manifest().keys(),
|
||||
['trunk/beta',
|
||||
'tags/copied_tag/alpha',
|
||||
'trunk/alpha',
|
||||
'tags/copied_tag/beta',
|
||||
'branches/branch_from_tag/alpha',
|
||||
'tags/tag_r3/alpha',
|
||||
'tags/tag_r3/beta',
|
||||
'branches/branch_from_tag/beta'])
|
||||
|
||||
def test_auto_detect_single(self):
|
||||
repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
|
||||
stupid=False,
|
||||
layout='auto')
|
||||
self.assertEqual(repo.branchtags().keys(), ['default',
|
||||
'branch_from_tag'])
|
||||
oldmanifest = test_util.filtermanifest(repo['default'].manifest().keys())
|
||||
# remove standard layout
|
||||
shutil.rmtree(self.wc_path)
|
||||
# try again with subdir to get single dir clone
|
||||
repo = self._load_fixture_and_fetch('branch_from_tag.svndump',
|
||||
stupid=False,
|
||||
layout='auto',
|
||||
subdir='trunk')
|
||||
self.assertEqual(repo.branchtags().keys(), ['default', ])
|
||||
self.assertEqual(repo['default'].manifest().keys(), oldmanifest)
|
||||
|
||||
def test_externals_single(self):
|
||||
repo = self._load_fixture_and_fetch('externals.svndump',
|
||||
stupid=False,
|
||||
layout='single')
|
||||
for rev in repo:
|
||||
assert '.hgsvnexternals' not in repo[rev].manifest()
|
||||
return # TODO enable test when externals in single are fixed
|
||||
expect = """[.]
|
||||
-r2 ^/externals/project2@2 deps/project2
|
||||
[subdir]
|
||||
^/externals/project1 deps/project1
|
||||
[subdir2]
|
||||
^/externals/project1 deps/project1
|
||||
"""
|
||||
test = 2
|
||||
self.assertEqual(self.repo[test]['.hgsvnexternals'].data(), expect)
|
||||
|
||||
def test_externals_single_whole_repo(self):
|
||||
# This is the test which demonstrates the brokenness of externals
|
||||
return # TODO enable test when externals in single are fixed
|
||||
repo = self._load_fixture_and_fetch('externals.svndump',
|
||||
stupid=False,
|
||||
layout='single',
|
||||
subdir='')
|
||||
for rev in repo:
|
||||
rc = repo[rev]
|
||||
if '.hgsvnexternals' in rc:
|
||||
extdata = rc['.hgsvnexternals'].data()
|
||||
assert '[.]' not in extdata
|
||||
print extdata
|
||||
expect = '' # Not honestly sure what this should be...
|
||||
test = 4
|
||||
self.assertEqual(self.repo[test]['.hgsvnexternals'].data(), expect)
|
@ -82,6 +82,10 @@ subdir = {'truncatedhistory.svndump': '/project2',
|
||||
FIXTURES = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||
'fixtures')
|
||||
|
||||
def filtermanifest(manifest):
|
||||
return filter(lambda x: x not in ('.hgtags', '.hgsvnexternals', ),
|
||||
manifest)
|
||||
|
||||
def fileurl(path):
|
||||
path = os.path.abspath(path)
|
||||
drive, path = os.path.splitdrive(path)
|
||||
@ -103,16 +107,21 @@ def load_svndump_fixture(path, fixture_name):
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
proc.communicate()
|
||||
|
||||
def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False, subdir='', noupdate=True):
|
||||
def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False, subdir='',
|
||||
noupdate=True, layout='auto'):
|
||||
load_svndump_fixture(repo_path, fixture_name)
|
||||
if subdir:
|
||||
repo_path += '/' + subdir
|
||||
|
||||
_ui = ui.ui()
|
||||
_ui.setconfig('hgsubversion', 'stupid', str(stupid))
|
||||
confvars = locals()
|
||||
def conf():
|
||||
for var in ('stupid', 'layout'):
|
||||
_ui = ui.ui()
|
||||
_ui.setconfig('hgsubversion', var, confvars[var])
|
||||
return _ui
|
||||
_ui = conf()
|
||||
commands.clone(_ui, fileurl(repo_path), wc_path, noupdate=noupdate)
|
||||
_ui = ui.ui()
|
||||
_ui.setconfig('hgsubversion', 'stupid', str(stupid))
|
||||
_ui = conf()
|
||||
return hg.repository(_ui, wc_path)
|
||||
|
||||
def rmtree(path):
|
||||
@ -170,10 +179,15 @@ class TestBase(unittest.TestCase):
|
||||
os.chdir(self.oldwd)
|
||||
setattr(ui.ui, self.patch[0].func_name, self.patch[0])
|
||||
|
||||
def _load_fixture_and_fetch(self, fixture_name, subdir='', stupid=False):
|
||||
def _load_fixture_and_fetch(self, fixture_name, subdir=None, stupid=False, layout='auto'):
|
||||
if layout == 'single':
|
||||
if subdir is None:
|
||||
subdir = 'trunk'
|
||||
elif subdir is None:
|
||||
subdir = ''
|
||||
return load_fixture_and_fetch(fixture_name, self.repo_path,
|
||||
self.wc_path, subdir=subdir,
|
||||
stupid=stupid)
|
||||
stupid=stupid, layout=layout)
|
||||
|
||||
# define this as a property so that it reloads anytime we need it
|
||||
@property
|
||||
|
Loading…
Reference in New Issue
Block a user