# HG changeset patch # User alexrayne # Date 2021-05-02 21:10:45 # Node ID 2fd1162cde20d9e06c53b299bdd35be977565aa0 # Parent 9cc9b4a25248a5fe8265e7b9659077a9e0f8098a sparse: make sure excluded subrepos don't break things fix subprepo.subrepo - now provide DummySubrepo for subs out of sparse. This dummy sub is empty node, always clean and ready to get/remove fix subrepoutil.submerge - now ignores changes for items out of sparce diff --git a/hgext/sparse.py b/hgext/sparse.py --- a/hgext/sparse.py +++ b/hgext/sparse.py @@ -86,6 +86,8 @@ from mercurial import ( pycompat, registrar, sparse, + subrepo, + subrepoutil, util, ) @@ -106,6 +108,7 @@ def extsetup(ui): _setuplog(ui) _setupadd(ui) _setupdirstate(ui) + _setupsubrepo(ui) def replacefilecache(cls, propname, replacement): @@ -286,6 +289,54 @@ def _setupdirstate(ui): extensions.wrapfunction(dirstate.dirstate, func, _wrapper) +class DummySubrepo(subrepo.abstractsubrepo): + """Dumy subrepo is replacement of subrepo, that should be filterout from sparce. + this subrepo acts as always clean and always get/remove well. + """ + + def dirty(self, ignoreupdate=False, missing=False): + return False + + def get(self, state, overwrite=False): + return + + def remove(self): + return + + +def _setupsubrepo(ui): + """Modify the dirstate to prevent stat'ing excluded files, + and to prevent modifications to files outside the checkout. + """ + + def _state(orig, ctx, ui): + sparsematch = sparse.matcher(ctx.repo(), revs=[ctx.rev()]) + if not sparsematch.always(): + # filter allstate, leave only sparce pathes + allstate = orig(ctx, ui) + sparcestate = dict() + for (idx, item) in allstate.items(): + if sparsematch(idx): + sparcestate[idx] = item + return sparcestate + else: + return orig(ctx, ui) + + # extensions.wrapfunction(subrepoutil, b'state', _state) + + """ provide DummySubrepo for pathes out of sparse + """ + + def _subrepo(orig, ctx, path, allowwdir=False, allowcreate=True): + sparsematch = sparse.matcher(ctx.repo(), revs=[ctx.rev()]) + if not sparsematch.always(): + if not sparsematch(path): + return DummySubrepo(ctx, path) + return orig(ctx, path, allowwdir, allowcreate) + + extensions.wrapfunction(subrepo, b'subrepo', _subrepo) + + @command( b'debugsparse', [ diff --git a/mercurial/subrepoutil.py b/mercurial/subrepoutil.py --- a/mercurial/subrepoutil.py +++ b/mercurial/subrepoutil.py @@ -21,6 +21,7 @@ from . import ( pathutil, phases, pycompat, + sparse, util, ) from .utils import ( @@ -183,6 +184,9 @@ def submerge(repo, wctx, mctx, actx, ove sa = actx.substate sm = {} + s1match = sparse.matcher(repo, revs=[wctx.rev()]) + s2match = sparse.matcher(repo, revs=[mctx.rev()]) + repo.ui.debug(b"subrepo merge %s %s %s\n" % (wctx, mctx, actx)) def debug(s, msg, r=b""): @@ -192,6 +196,9 @@ def submerge(repo, wctx, mctx, actx, ove promptssrc = filemerge.partextras(labels) for s, l in sorted(pycompat.iteritems(s1)): + if not s1match(s): + sm[s] = l # ignore changes out of sparse + continue a = sa.get(s, nullstate) ld = l # local state with possible dirty flag for compares if wctx.sub(s).dirty(): @@ -202,6 +209,9 @@ def submerge(repo, wctx, mctx, actx, ove prompts = promptssrc.copy() prompts[b's'] = s if s in s2: + if not s2match(s): + sm[s] = l # ignore changes out of sparse + continue r = s2[s] if ld == r or r == a: # no change or local is newer sm[s] = l @@ -288,6 +298,14 @@ def submerge(repo, wctx, mctx, actx, ove mctx.sub(s).get(r) sm[s] = r elif r != sa[s]: + if not s2match(s): + # ignore changes out of sparse, + continue + elif not s1match(s): + # recreate changes out of sparse, + # sm[s] = r + continue + prompts = promptssrc.copy() prompts[b's'] = s if (