narrowcommands.py
511 lines
| 21.2 KiB
| text/x-python
|
PythonLexer
|
r36096 | # narrowcommands.py - command modifications for narrowhg extension | ||
# | ||||
# Copyright 2017 Google, Inc. | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | ||||
import itertools | ||||
|
r39283 | import os | ||
|
r36096 | |||
from mercurial.i18n import _ | ||||
from mercurial import ( | ||||
|
r40106 | bundle2, | ||
|
r36096 | cmdutil, | ||
commands, | ||||
discovery, | ||||
|
r39471 | encoding, | ||
|
r36096 | error, | ||
exchange, | ||||
extensions, | ||||
hg, | ||||
|
r36178 | narrowspec, | ||
|
r36096 | node, | ||
|
r36172 | pycompat, | ||
|
r36096 | registrar, | ||
repair, | ||||
repoview, | ||||
|
r39283 | sparse, | ||
|
r36096 | util, | ||
|
r40110 | wireprototypes, | ||
|
r36096 | ) | ||
|
r43078 | from mercurial.interfaces import ( | ||
repository, | ||||
) | ||||
|
r36096 | |||
table = {} | ||||
command = registrar.command(table) | ||||
def setup(): | ||||
"""Wraps user-facing mercurial commands with narrow-aware versions.""" | ||||
entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd) | ||||
entry[1].append(('', 'narrow', None, | ||||
_("create a narrow clone of select files"))) | ||||
entry[1].append(('', 'depth', '', | ||||
_("limit the history fetched by distance from heads"))) | ||||
|
r39283 | entry[1].append(('', 'narrowspec', '', | ||
_("read narrowspecs from file"))) | ||||
|
r36096 | # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit | ||
if 'sparse' not in extensions.enabled(): | ||||
entry[1].append(('', 'include', [], | ||||
_("specifically fetch this file/directory"))) | ||||
entry[1].append( | ||||
('', 'exclude', [], | ||||
_("do not fetch this file/directory, even if included"))) | ||||
entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd) | ||||
entry[1].append(('', 'depth', '', | ||||
_("limit the history fetched by distance from heads"))) | ||||
extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd) | ||||
def clonenarrowcmd(orig, ui, repo, *args, **opts): | ||||
"""Wraps clone command, so 'hg clone' first wraps localrepo.clone().""" | ||||
|
r36172 | opts = pycompat.byteskwargs(opts) | ||
|
r36096 | wrappedextraprepare = util.nullcontextmanager() | ||
|
r39283 | narrowspecfile = opts['narrowspec'] | ||
if narrowspecfile: | ||||
|
r39843 | filepath = os.path.join(encoding.getcwd(), narrowspecfile) | ||
|
r39283 | ui.status(_("reading narrowspec from '%s'\n") % filepath) | ||
try: | ||||
|
r39501 | fdata = util.readfile(filepath) | ||
except IOError as inst: | ||||
raise error.Abort(_("cannot read narrowspecs from '%s': %s") % | ||||
(filepath, encoding.strtolocal(inst.strerror))) | ||||
|
r39283 | |||
|
r39501 | includes, excludes, profiles = sparse.parseconfig(ui, fdata, 'narrow') | ||
|
r39283 | if profiles: | ||
raise error.Abort(_("cannot specify other files using '%include' in" | ||||
" narrowspec")) | ||||
|
r39577 | narrowspec.validatepatterns(includes) | ||
narrowspec.validatepatterns(excludes) | ||||
|
r39283 | # narrowspec is passed so we should assume that user wants narrow clone | ||
|
r39582 | opts['narrow'] = True | ||
|
r39283 | opts['include'].extend(includes) | ||
opts['exclude'].extend(excludes) | ||||
|
r39582 | if opts['narrow']: | ||
|
r36096 | def pullbundle2extraprepare_widen(orig, pullop, kwargs): | ||
orig(pullop, kwargs) | ||||
if opts.get('depth'): | ||||
kwargs['depth'] = opts['depth'] | ||||
wrappedextraprepare = extensions.wrappedfunction(exchange, | ||||
'_pullbundle2extraprepare', pullbundle2extraprepare_widen) | ||||
|
r39590 | with wrappedextraprepare: | ||
|
r36172 | return orig(ui, repo, *args, **pycompat.strkwargs(opts)) | ||
|
r36096 | |||
def pullnarrowcmd(orig, ui, repo, *args, **opts): | ||||
"""Wraps pull command to allow modifying narrow spec.""" | ||||
wrappedextraprepare = util.nullcontextmanager() | ||||
|
r38871 | if repository.NARROW_REQUIREMENT in repo.requirements: | ||
|
r36096 | |||
def pullbundle2extraprepare_widen(orig, pullop, kwargs): | ||||
orig(pullop, kwargs) | ||||
|
r36474 | if opts.get(r'depth'): | ||
kwargs['depth'] = opts[r'depth'] | ||||
|
r36096 | wrappedextraprepare = extensions.wrappedfunction(exchange, | ||
'_pullbundle2extraprepare', pullbundle2extraprepare_widen) | ||||
with wrappedextraprepare: | ||||
return orig(ui, repo, *args, **opts) | ||||
def archivenarrowcmd(orig, ui, repo, *args, **opts): | ||||
"""Wraps archive command to narrow the default includes.""" | ||||
|
r38871 | if repository.NARROW_REQUIREMENT in repo.requirements: | ||
|
r36096 | repo_includes, repo_excludes = repo.narrowpats | ||
|
r36370 | includes = set(opts.get(r'include', [])) | ||
excludes = set(opts.get(r'exclude', [])) | ||||
|
r36117 | includes, excludes, unused_invalid = narrowspec.restrictpatterns( | ||
|
r36096 | includes, excludes, repo_includes, repo_excludes) | ||
if includes: | ||||
|
r36370 | opts[r'include'] = includes | ||
|
r36096 | if excludes: | ||
|
r36370 | opts[r'exclude'] = excludes | ||
|
r36096 | return orig(ui, repo, *args, **opts) | ||
def pullbundle2extraprepare(orig, pullop, kwargs): | ||||
repo = pullop.repo | ||||
|
r38871 | if repository.NARROW_REQUIREMENT not in repo.requirements: | ||
|
r36096 | return orig(pullop, kwargs) | ||
|
r40110 | if wireprototypes.NARROWCAP not in pullop.remote.capabilities(): | ||
|
r40000 | raise error.Abort(_("server does not support narrow clones")) | ||
|
r36096 | orig(pullop, kwargs) | ||
kwargs['narrow'] = True | ||||
include, exclude = repo.narrowpats | ||||
kwargs['oldincludepats'] = include | ||||
kwargs['oldexcludepats'] = exclude | ||||
|
r40528 | if include: | ||
kwargs['includepats'] = include | ||||
if exclude: | ||||
kwargs['excludepats'] = exclude | ||||
|
r39560 | # calculate known nodes only in ellipses cases because in non-ellipses cases | ||
# we have all the nodes | ||||
|
r42605 | if wireprototypes.ELLIPSESCAP1 in pullop.remote.capabilities(): | ||
|
r39560 | kwargs['known'] = [node.hex(ctx.node()) for ctx in | ||
repo.set('::%ln', pullop.common) | ||||
if ctx.node() != node.nullid] | ||||
if not kwargs['known']: | ||||
# Mercurial serializes an empty list as '' and deserializes it as | ||||
# [''], so delete it instead to avoid handling the empty string on | ||||
# the server. | ||||
del kwargs['known'] | ||||
|
r36096 | |||
extensions.wrapfunction(exchange,'_pullbundle2extraprepare', | ||||
pullbundle2extraprepare) | ||||
def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes, | ||||
newincludes, newexcludes, force): | ||||
oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes) | ||||
newmatch = narrowspec.match(repo.root, newincludes, newexcludes) | ||||
# This is essentially doing "hg outgoing" to find all local-only | ||||
# commits. We will then check that the local-only commits don't | ||||
# have any changes to files that will be untracked. | ||||
unfi = repo.unfiltered() | ||||
outgoing = discovery.findcommonoutgoing(unfi, remote, | ||||
commoninc=commoninc) | ||||
ui.status(_('looking for local changes to affected paths\n')) | ||||
localnodes = [] | ||||
for n in itertools.chain(outgoing.missing, outgoing.excluded): | ||||
if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()): | ||||
localnodes.append(n) | ||||
revstostrip = unfi.revs('descendants(%ln)', localnodes) | ||||
hiddenrevs = repoview.filterrevs(repo, 'visible') | ||||
visibletostrip = list(repo.changelog.node(r) | ||||
for r in (revstostrip - hiddenrevs)) | ||||
if visibletostrip: | ||||
ui.status(_('The following changeset(s) or their ancestors have ' | ||||
'local changes not on the remote:\n')) | ||||
maxnodes = 10 | ||||
if ui.verbose or len(visibletostrip) <= maxnodes: | ||||
for n in visibletostrip: | ||||
ui.status('%s\n' % node.short(n)) | ||||
else: | ||||
for n in visibletostrip[:maxnodes]: | ||||
ui.status('%s\n' % node.short(n)) | ||||
ui.status(_('...and %d more, use --verbose to list all\n') % | ||||
(len(visibletostrip) - maxnodes)) | ||||
if not force: | ||||
raise error.Abort(_('local changes found'), | ||||
hint=_('use --force-delete-local-changes to ' | ||||
'ignore')) | ||||
|
r41106 | with ui.uninterruptible(): | ||
|
r38547 | if revstostrip: | ||
tostrip = [unfi.changelog.node(r) for r in revstostrip] | ||||
if repo['.'].node() in tostrip: | ||||
# stripping working copy, so move to a different commit first | ||||
urev = max(repo.revs('(::%n) - %ln + null', | ||||
repo['.'].node(), visibletostrip)) | ||||
hg.clean(repo, urev) | ||||
|
r40864 | overrides = {('devel', 'strip-obsmarkers'): False} | ||
with ui.configoverride(overrides, 'narrow'): | ||||
repair.strip(ui, unfi, tostrip, topic='narrow') | ||||
|
r36096 | |||
|
r38547 | todelete = [] | ||
for f, f2, size in repo.store.datafiles(): | ||||
if f.startswith('data/'): | ||||
file = f[5:-2] | ||||
if not newmatch(file): | ||||
todelete.append(f) | ||||
elif f.startswith('meta/'): | ||||
dir = f[5:-13] | ||||
|
r42532 | dirs = sorted(util.dirs({dir})) + [dir] | ||
|
r38547 | include = True | ||
for d in dirs: | ||||
visit = newmatch.visitdir(d) | ||||
if not visit: | ||||
include = False | ||||
break | ||||
if visit == 'all': | ||||
break | ||||
if not include: | ||||
todelete.append(f) | ||||
|
r36096 | |||
|
r38547 | repo.destroying() | ||
|
r36096 | |||
|
r41274 | with repo.transaction('narrowing'): | ||
|
r41057 | # Update narrowspec before removing revlogs, so repo won't be | ||
# corrupt in case of crash | ||||
repo.setnarrowpats(newincludes, newexcludes) | ||||
|
r38547 | for f in todelete: | ||
ui.status(_('deleting %s\n') % f) | ||||
util.unlinkpath(repo.svfs.join(f)) | ||||
repo.store.markremoved(f) | ||||
|
r36096 | |||
|
r41274 | narrowspec.updateworkingcopy(repo, assumeclean=True) | ||
narrowspec.copytoworkingcopy(repo) | ||||
|
r36096 | |||
|
r38547 | repo.destroyed() | ||
|
r36096 | |||
|
r40001 | def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes, | ||
newincludes, newexcludes): | ||||
|
r39999 | # for now we assume that if a server has ellipses enabled, we will be | ||
# exchanging ellipses nodes. In future we should add ellipses as a client | ||||
# side requirement (maybe) to distinguish a client is shallow or not and | ||||
# then send that information to server whether we want ellipses or not. | ||||
# Theoretically a non-ellipses repo should be able to use narrow | ||||
# functionality from an ellipses enabled server | ||||
|
r42605 | remotecap = remote.capabilities() | ||
ellipsesremote = any(cap in remotecap | ||||
for cap in wireprototypes.SUPPORTED_ELLIPSESCAP) | ||||
# check whether we are talking to a server which supports old version of | ||||
# ellipses capabilities | ||||
isoldellipses = (ellipsesremote and wireprototypes.ELLIPSESCAP1 in | ||||
remotecap and wireprototypes.ELLIPSESCAP not in remotecap) | ||||
|
r39999 | |||
|
r36096 | def pullbundle2extraprepare_widen(orig, pullop, kwargs): | ||
orig(pullop, kwargs) | ||||
# The old{in,ex}cludepats have already been set by orig() | ||||
kwargs['includepats'] = newincludes | ||||
kwargs['excludepats'] = newexcludes | ||||
wrappedextraprepare = extensions.wrappedfunction(exchange, | ||||
'_pullbundle2extraprepare', pullbundle2extraprepare_widen) | ||||
# define a function that narrowbundle2 can call after creating the | ||||
# backup bundle, but before applying the bundle from the server | ||||
def setnewnarrowpats(): | ||||
repo.setnarrowpats(newincludes, newexcludes) | ||||
repo.setnewnarrowpats = setnewnarrowpats | ||||
|
r39709 | # silence the devel-warning of applying an empty changegroup | ||
overrides = {('devel', 'all-warnings'): False} | ||||
|
r36096 | |||
|
r42606 | common = commoninc[0] | ||
|
r41106 | with ui.uninterruptible(): | ||
|
r39999 | if ellipsesremote: | ||
ds = repo.dirstate | ||||
p1, p2 = ds.p1(), ds.p2() | ||||
with ds.parentchange(): | ||||
ds.setparents(node.nullid, node.nullid) | ||||
|
r42606 | if isoldellipses: | ||
|
r41926 | with wrappedextraprepare: | ||
|
r42606 | exchange.pull(repo, remote, heads=common) | ||
|
r39999 | else: | ||
|
r42606 | known = [] | ||
if ellipsesremote: | ||||
|
r43214 | known = [ctx.node() for ctx in | ||
|
r42606 | repo.set('::%ln', common) | ||
if ctx.node() != node.nullid] | ||||
|
r40106 | with remote.commandexecutor() as e: | ||
bundle = e.callcommand('narrow_widen', { | ||||
'oldincludes': oldincludes, | ||||
'oldexcludes': oldexcludes, | ||||
'newincludes': newincludes, | ||||
'newexcludes': newexcludes, | ||||
'cgversion': '03', | ||||
'commonheads': common, | ||||
|
r42606 | 'known': known, | ||
'ellipses': ellipsesremote, | ||||
|
r40106 | }).result() | ||
|
r42606 | trmanager = exchange.transactionmanager(repo, 'widen', remote.url()) | ||
with trmanager, repo.ui.configoverride(overrides, 'widen'): | ||||
op = bundle2.bundleoperation(repo, trmanager.transaction, | ||||
source='widen') | ||||
# TODO: we should catch error.Abort here | ||||
bundle2.processbundle(repo, bundle, op=op) | ||||
if ellipsesremote: | ||||
with ds.parentchange(): | ||||
ds.setparents(p1, p2) | ||||
|
r36096 | |||
|
r41273 | with repo.transaction('widening'): | ||
repo.setnewnarrowpats() | ||||
narrowspec.updateworkingcopy(repo) | ||||
narrowspec.copytoworkingcopy(repo) | ||||
|
r36096 | |||
# TODO(rdamazio): Make new matcher format and update description | ||||
@command('tracked', | ||||
[('', 'addinclude', [], _('new paths to include')), | ||||
('', 'removeinclude', [], _('old paths to no longer include')), | ||||
|
r43215 | ('', 'auto-remove-includes', False, | ||
_('automatically choose unused includes to remove')), | ||||
|
r36096 | ('', 'addexclude', [], _('new paths to exclude')), | ||
|
r39471 | ('', 'import-rules', '', _('import narrowspecs from a file')), | ||
|
r36096 | ('', 'removeexclude', [], _('old paths to no longer exclude')), | ||
('', 'clear', False, _('whether to replace the existing narrowspec')), | ||||
('', 'force-delete-local-changes', False, | ||||
_('forces deletion of local changes when narrowing')), | ||||
|
r41072 | ('', 'update-working-copy', False, | ||
_('update working copy when the store has changed')), | ||||
|
r36096 | ] + commands.remoteopts, | ||
_('[OPTIONS]... [REMOTE]'), | ||||
inferrepo=True) | ||||
def trackedcmd(ui, repo, remotepath=None, *pats, **opts): | ||||
"""show or change the current narrowspec | ||||
With no argument, shows the current narrowspec entries, one per line. Each | ||||
line will be prefixed with 'I' or 'X' for included or excluded patterns, | ||||
respectively. | ||||
The narrowspec is comprised of expressions to match remote files and/or | ||||
directories that should be pulled into your client. | ||||
The narrowspec has *include* and *exclude* expressions, with excludes always | ||||
trumping includes: that is, if a file matches an exclude expression, it will | ||||
be excluded even if it also matches an include expression. | ||||
Excluding files that were never included has no effect. | ||||
Each included or excluded entry is in the format described by | ||||
'hg help patterns'. | ||||
The options allow you to add or remove included and excluded expressions. | ||||
If --clear is specified, then all previous includes and excludes are DROPPED | ||||
and replaced by the new ones specified to --addinclude and --addexclude. | ||||
If --clear is specified without any further options, the narrowspec will be | ||||
empty and will not match any files. | ||||
|
r42146 | |||
|
r43215 | If --auto-remove-includes is specified, then those includes that don't match | ||
any files modified by currently visible local commits (those not shared by | ||||
the remote) will be added to the set of explicitly specified includes to | ||||
remove. | ||||
|
r42146 | --import-rules accepts a path to a file containing rules, allowing you to | ||
add --addinclude, --addexclude rules in bulk. Like the other include and | ||||
exclude switches, the changes are applied immediately. | ||||
|
r36096 | """ | ||
|
r36184 | opts = pycompat.byteskwargs(opts) | ||
|
r38871 | if repository.NARROW_REQUIREMENT not in repo.requirements: | ||
|
r41872 | raise error.Abort(_('the tracked command is only supported on ' | ||
|
r43075 | 'repositories cloned with --narrow')) | ||
|
r36096 | |||
# Before supporting, decide whether it "hg tracked --clear" should mean | ||||
# tracking no paths or all paths. | ||||
if opts['clear']: | ||||
|
r41056 | raise error.Abort(_('the --clear option is not yet supported')) | ||
|
r36096 | |||
|
r39471 | # import rules from a file | ||
newrules = opts.get('import_rules') | ||||
if newrules: | ||||
try: | ||||
|
r39843 | filepath = os.path.join(encoding.getcwd(), newrules) | ||
|
r39471 | fdata = util.readfile(filepath) | ||
except IOError as inst: | ||||
raise error.Abort(_("cannot read narrowspecs from '%s': %s") % | ||||
(filepath, encoding.strtolocal(inst.strerror))) | ||||
includepats, excludepats, profiles = sparse.parseconfig(ui, fdata, | ||||
'narrow') | ||||
if profiles: | ||||
raise error.Abort(_("including other spec files using '%include' " | ||||
"is not supported in narrowspec")) | ||||
opts['addinclude'].extend(includepats) | ||||
opts['addexclude'].extend(excludepats) | ||||
|
r36096 | addedincludes = narrowspec.parsepatterns(opts['addinclude']) | ||
removedincludes = narrowspec.parsepatterns(opts['removeinclude']) | ||||
addedexcludes = narrowspec.parsepatterns(opts['addexclude']) | ||||
removedexcludes = narrowspec.parsepatterns(opts['removeexclude']) | ||||
|
r43215 | autoremoveincludes = opts['auto_remove_includes'] | ||
|
r40462 | |||
|
r41072 | update_working_copy = opts['update_working_copy'] | ||
|
r40462 | only_show = not (addedincludes or removedincludes or addedexcludes or | ||
|
r43215 | removedexcludes or newrules or autoremoveincludes or | ||
update_working_copy) | ||||
|
r40462 | |||
oldincludes, oldexcludes = repo.narrowpats | ||||
# filter the user passed additions and deletions into actual additions and | ||||
# deletions of excludes and includes | ||||
|
r40465 | addedincludes -= oldincludes | ||
removedincludes &= oldincludes | ||||
addedexcludes -= oldexcludes | ||||
removedexcludes &= oldexcludes | ||||
|
r40462 | |||
|
r36096 | widening = addedincludes or removedexcludes | ||
narrowing = removedincludes or addedexcludes | ||||
# Only print the current narrowspec. | ||||
if only_show: | ||||
ui.pager('tracked') | ||||
fm = ui.formatter('narrow', opts) | ||||
|
r40802 | for i in sorted(oldincludes): | ||
|
r36096 | fm.startitem() | ||
fm.write('status', '%s ', 'I', label='narrow.included') | ||||
fm.write('pat', '%s\n', i, label='narrow.included') | ||||
|
r40802 | for i in sorted(oldexcludes): | ||
|
r36096 | fm.startitem() | ||
fm.write('status', '%s ', 'X', label='narrow.excluded') | ||||
fm.write('pat', '%s\n', i, label='narrow.excluded') | ||||
fm.end() | ||||
return 0 | ||||
|
r41072 | if update_working_copy: | ||
|
r41265 | with repo.wlock(), repo.lock(), repo.transaction('narrow-wc'): | ||
|
r41212 | narrowspec.updateworkingcopy(repo) | ||
|
r41265 | narrowspec.copytoworkingcopy(repo) | ||
|
r41072 | return 0 | ||
|
r43215 | if not (widening or narrowing or autoremoveincludes): | ||
|
r40462 | ui.status(_("nothing to widen or narrow\n")) | ||
return 0 | ||||
|
r36096 | with repo.wlock(), repo.lock(): | ||
cmdutil.bailifchanged(repo) | ||||
# Find the revisions we have in common with the remote. These will | ||||
# be used for finding local-only changes for narrowing. They will | ||||
# also define the set of revisions to update for widening. | ||||
remotepath = ui.expandpath(remotepath or 'default') | ||||
url, branches = hg.parseurl(remotepath) | ||||
ui.status(_('comparing with %s\n') % util.hidepassword(url)) | ||||
remote = hg.peer(repo, opts, url) | ||||
|
r40000 | |||
# check narrow support before doing anything if widening needs to be | ||||
# performed. In future we should also abort if client is ellipses and | ||||
# server does not support ellipses | ||||
|
r40110 | if widening and wireprototypes.NARROWCAP not in remote.capabilities(): | ||
|
r40000 | raise error.Abort(_("server does not support narrow clones")) | ||
|
r36096 | commoninc = discovery.findcommonincoming(repo, remote) | ||
|
r43215 | if autoremoveincludes: | ||
outgoing = discovery.findcommonoutgoing(repo, remote, | ||||
commoninc=commoninc) | ||||
ui.status(_('looking for unused includes to remove\n')) | ||||
localfiles = set() | ||||
for n in itertools.chain(outgoing.missing, outgoing.excluded): | ||||
localfiles.update(repo[n].files()) | ||||
suggestedremovals = [] | ||||
for include in sorted(oldincludes): | ||||
match = narrowspec.match(repo.root, [include], oldexcludes) | ||||
if not any(match(f) for f in localfiles): | ||||
suggestedremovals.append(include) | ||||
if suggestedremovals: | ||||
for s in suggestedremovals: | ||||
ui.status('%s\n' % s) | ||||
if (ui.promptchoice(_('remove these unused includes (yn)?' | ||||
'$$ &Yes $$ &No')) == 0): | ||||
removedincludes.update(suggestedremovals) | ||||
narrowing = True | ||||
else: | ||||
ui.status(_('found no unused includes\n')) | ||||
|
r36096 | if narrowing: | ||
newincludes = oldincludes - removedincludes | ||||
newexcludes = oldexcludes | addedexcludes | ||||
_narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes, | ||||
newincludes, newexcludes, | ||||
opts['force_delete_local_changes']) | ||||
# _narrow() updated the narrowspec and _widen() below needs to | ||||
# use the updated values as its base (otherwise removed includes | ||||
# and addedexcludes will be lost in the resulting narrowspec) | ||||
oldincludes = newincludes | ||||
oldexcludes = newexcludes | ||||
if widening: | ||||
newincludes = oldincludes | addedincludes | ||||
newexcludes = oldexcludes - removedexcludes | ||||
|
r40001 | _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes, | ||
newincludes, newexcludes) | ||||
|
r36096 | |||
return 0 | ||||