mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 08:18:15 +03:00
svnexternals: preliminary support for subrepos based externals
At this point, only pulling externals definitions into .hgsub and .hgsubstate is supported. One difference between subrepos and svn:externals is the former separate the source definition and target revision in two files, while svn:externals definitions contain both. To handle this, the svn:externals revision references is replaced with a {REV} placeholder and stored in .hgsub, prefixed with the external base directory separated with a ':', while the revision is extracted in .hgsubstate. For instance, the following external: -r3 ^/externals/proj2@2 deps/proj2 Becomes: (.hgsub) deps/proj2 = [hgsubversion] :-r{REV} ^/externals/proj2@2 deps/proj2 (.hgsubstate) 3 deps/proj2
This commit is contained in:
parent
a0bae52693
commit
fe5402cb96
@ -47,10 +47,19 @@ try:
|
||||
except ImportError:
|
||||
revset = None
|
||||
|
||||
try:
|
||||
from mercurial import subrepo
|
||||
# require svnsubrepo and hg >= 1.7.1
|
||||
subrepo.svnsubrepo
|
||||
hgutil.checklink
|
||||
except ImportError:
|
||||
subrepo = None
|
||||
|
||||
import svncommands
|
||||
import util
|
||||
import svnrepo
|
||||
import wrappers
|
||||
import svnexternals
|
||||
|
||||
svnopts = [
|
||||
('', 'stupid', None,
|
||||
@ -155,6 +164,9 @@ def extsetup():
|
||||
if revset:
|
||||
revset.symbols.update(util.revsets)
|
||||
|
||||
if subrepo:
|
||||
subrepo.types['hgsubversion'] = svnexternals.svnsubrepo
|
||||
|
||||
def reposetup(ui, repo):
|
||||
if repo.local():
|
||||
svnrepo.generate_repo_class(ui, repo)
|
||||
|
@ -97,8 +97,8 @@ def commit(ui, repo, rev_ctx, meta, base_revision, svn):
|
||||
elif parent_branch and parent_branch != 'default':
|
||||
branch_path = 'branches/%s' % parent_branch
|
||||
|
||||
extchanges = svnexternals.diff(svnexternals.parse(parent),
|
||||
svnexternals.parse(rev_ctx))
|
||||
extchanges = svnexternals.diff(svnexternals.parse(ui, parent),
|
||||
svnexternals.parse(ui, rev_ctx))
|
||||
addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
|
||||
rev_ctx.files(), extchanges)
|
||||
deleteddirs = set(deleteddirs)
|
||||
|
@ -37,7 +37,7 @@ def updateexternals(ui, meta, current):
|
||||
if bp not in branches:
|
||||
parent = meta.get_parent_revision(revnum, b)
|
||||
pctx = meta.repo[parent]
|
||||
branches[bp] = (svnexternals.parse(pctx), pctx)
|
||||
branches[bp] = (svnexternals.parse(ui, pctx), pctx)
|
||||
branches[bp][0][p] = entry
|
||||
|
||||
# register externals file changes
|
||||
|
@ -343,13 +343,13 @@ def getcopies(svn, meta, branch, branchpath, r, files, parentctx):
|
||||
hgcopies.update({k: v})
|
||||
return hgcopies
|
||||
|
||||
def fetch_externals(svn, branchpath, r, parentctx):
|
||||
def fetch_externals(ui, svn, branchpath, r, parentctx):
|
||||
"""Extract svn:externals for the current revision and branch
|
||||
|
||||
Return an externalsfile instance or None if there are no externals
|
||||
to convert and never were.
|
||||
"""
|
||||
externals = svnexternals.parse(parentctx)
|
||||
externals = svnexternals.parse(ui, parentctx)
|
||||
# Detect property additions only, changes are handled by checking
|
||||
# existing entries individually. Projects are unlikely to store
|
||||
# externals on many different root directories, so we trade code
|
||||
@ -600,7 +600,7 @@ def convert_rev(ui, meta, svn, r, tbdelta):
|
||||
|
||||
externals = {}
|
||||
if meta.layout != 'single':
|
||||
externals = fetch_externals(svn, branches[b], r, parentctx)
|
||||
externals = fetch_externals(ui, svn, branches[b], r, parentctx)
|
||||
externals = svnexternals.getchanges(ui, meta.repo, parentctx,
|
||||
externals)
|
||||
files_touched.extend(externals)
|
||||
|
@ -3,6 +3,15 @@ import cStringIO
|
||||
import os, re, shutil, stat, subprocess
|
||||
from mercurial import util as hgutil
|
||||
from mercurial.i18n import _
|
||||
|
||||
try:
|
||||
from mercurial import subrepo
|
||||
# require svnsubrepo and hg >= 1.7.1
|
||||
subrepo.svnsubrepo
|
||||
hgutil.checklink
|
||||
except ImportError:
|
||||
subrepo = None
|
||||
|
||||
import util
|
||||
|
||||
class externalsfile(dict):
|
||||
@ -301,11 +310,32 @@ def getchanges(ui, repo, parentctx, exts):
|
||||
hgsubversion needs for externals bookkeeping, to their new content
|
||||
as raw bytes or None if the file has to be removed.
|
||||
"""
|
||||
files = {
|
||||
'.hgsvnexternals': None,
|
||||
}
|
||||
if exts:
|
||||
files['.hgsvnexternals'] = exts.write()
|
||||
mode = ui.config('hgsubversion', 'externals', 'svnexternals')
|
||||
if mode == 'svnexternals':
|
||||
files = {
|
||||
'.hgsvnexternals': None,
|
||||
}
|
||||
if exts:
|
||||
files['.hgsvnexternals'] = exts.write()
|
||||
elif mode == 'subrepos':
|
||||
# XXX: clobering the subrepos files is good enough for now
|
||||
files = {
|
||||
'.hgsub': None,
|
||||
'.hgsubstate': None,
|
||||
}
|
||||
if exts:
|
||||
defs = parsedefinitions(ui, repo, '', exts)
|
||||
hgsub, hgsubstate = [], []
|
||||
for path, rev, source, pegrev, norevline, base in sorted(defs):
|
||||
hgsub.append('%s = [hgsubversion] %s:%s\n'
|
||||
% (path, base, norevline))
|
||||
if rev is None:
|
||||
rev = 'HEAD'
|
||||
hgsubstate.append('%s %s\n' % (rev, path))
|
||||
files['.hgsub'] = ''.join(hgsub)
|
||||
files['.hgsubstate'] = ''.join(hgsubstate)
|
||||
else:
|
||||
raise hgutil.Abort(_('unknown externals modes: %s') % mode)
|
||||
|
||||
# Should the really be updated?
|
||||
updates = {}
|
||||
@ -318,11 +348,29 @@ def getchanges(ui, repo, parentctx, exts):
|
||||
updates[fn] = None
|
||||
return updates
|
||||
|
||||
def parse(ctx):
|
||||
def parse(ui, ctx):
|
||||
"""Return the externals definitions stored in ctx as a (possibly empty)
|
||||
externalsfile().
|
||||
"""
|
||||
external = externalsfile()
|
||||
if '.hgsvnexternals' in ctx:
|
||||
external.read(ctx['.hgsvnexternals'].data())
|
||||
mode = ui.config('hgsubversion', 'externals', 'svnexternals')
|
||||
if mode == 'svnexternals':
|
||||
if '.hgsvnexternals' in ctx:
|
||||
external.read(ctx['.hgsvnexternals'].data())
|
||||
elif mode == 'subrepos':
|
||||
for path in ctx.substate:
|
||||
src, rev = ctx.substate[path][:2]
|
||||
base, norevline = src.split(':', 1)
|
||||
base = base.strip()
|
||||
if rev is None:
|
||||
rev = 'HEAD'
|
||||
line = norevline.replace('{REV}', rev)
|
||||
external.setdefault(base, []).append(line)
|
||||
else:
|
||||
raise hgutil.Abort(_('unknown externals modes: %s') % mode)
|
||||
return external
|
||||
|
||||
if subrepo:
|
||||
class svnsubrepo(subrepo.svnsubrepo):
|
||||
def __init__(self, ctx, path, state):
|
||||
super(svnsubrepo, self).__init__(ctx, path, state)
|
||||
|
@ -139,6 +139,72 @@ class TestFetchExternals(test_util.TestBase):
|
||||
['subdir2/deps/project1'], repo, 3)
|
||||
checkdeps(['subdir/deps/project1'], ['deps/project2'], repo, 4)
|
||||
|
||||
def test_hgsub(self, stupid=False):
|
||||
repo = self._load_fixture_and_fetch('externals.svndump',
|
||||
externals='subrepos',
|
||||
stupid=stupid)
|
||||
self.assertEqual("""\
|
||||
deps/project1 = [hgsubversion] :^/externals/project1 deps/project1
|
||||
""", repo[0]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
HEAD deps/project1
|
||||
""", repo[0]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
deps/project1 = [hgsubversion] :^/externals/project1 deps/project1
|
||||
deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2
|
||||
""", repo[1]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
HEAD deps/project1
|
||||
2 deps/project2
|
||||
""", repo[1]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2
|
||||
subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1
|
||||
subdir2/deps/project1 = [hgsubversion] subdir2:^/externals/project1 deps/project1
|
||||
""", repo[2]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
2 deps/project2
|
||||
HEAD subdir/deps/project1
|
||||
HEAD subdir2/deps/project1
|
||||
""", repo[2]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2
|
||||
subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1
|
||||
""", repo[3]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
2 deps/project2
|
||||
HEAD subdir/deps/project1
|
||||
""", repo[3]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
subdir/deps/project1 = [hgsubversion] subdir:^/externals/project1 deps/project1
|
||||
""", repo[4]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
HEAD subdir/deps/project1
|
||||
""", repo[4]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2
|
||||
subdir2/deps/project1 = [hgsubversion] subdir2:^/externals/project1 deps/project1
|
||||
""", repo[5]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
2 deps/project2
|
||||
HEAD subdir2/deps/project1
|
||||
""", repo[5]['.hgsubstate'].data())
|
||||
|
||||
self.assertEqual("""\
|
||||
deps/project2 = [hgsubversion] :-r{REV} ^/externals/project2@2 deps/project2
|
||||
""", repo[6]['.hgsub'].data())
|
||||
self.assertEqual("""\
|
||||
2 deps/project2
|
||||
""", repo[6]['.hgsubstate'].data())
|
||||
|
||||
def test_hgsub_stupid(self):
|
||||
self.test_hgsub(True)
|
||||
|
||||
class TestPushExternals(test_util.TestBase):
|
||||
def setUp(self):
|
||||
test_util.TestBase.setUp(self)
|
||||
|
@ -172,7 +172,7 @@ def load_svndump_fixture(path, fixture_name):
|
||||
|
||||
def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False,
|
||||
subdir='', noupdate=True, layout='auto',
|
||||
startrev=0):
|
||||
startrev=0, externals=None):
|
||||
load_svndump_fixture(repo_path, fixture_name)
|
||||
if subdir:
|
||||
repo_path += '/' + subdir
|
||||
@ -188,6 +188,8 @@ def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False,
|
||||
cmd.append('--stupid')
|
||||
if noupdate:
|
||||
cmd.append('--noupdate')
|
||||
if externals:
|
||||
cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals]
|
||||
|
||||
dispatch.dispatch(cmd)
|
||||
|
||||
@ -275,7 +277,7 @@ class TestBase(unittest.TestCase):
|
||||
return testui(stupid, layout)
|
||||
|
||||
def _load_fixture_and_fetch(self, fixture_name, subdir=None, stupid=False,
|
||||
layout='auto', startrev=0):
|
||||
layout='auto', startrev=0, externals=None):
|
||||
if layout == 'single':
|
||||
if subdir is None:
|
||||
subdir = 'trunk'
|
||||
@ -284,7 +286,7 @@ class TestBase(unittest.TestCase):
|
||||
return load_fixture_and_fetch(fixture_name, self.repo_path,
|
||||
self.wc_path, subdir=subdir,
|
||||
stupid=stupid, layout=layout,
|
||||
startrev=startrev)
|
||||
startrev=startrev, externals=externals)
|
||||
|
||||
def _add_svn_rev(self, changes):
|
||||
'''changes is a dict of filename -> contents'''
|
||||
|
Loading…
Reference in New Issue
Block a user