hgext/sparse.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43087 66f2cc210a29
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    90 
    90 
    91 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    91 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    92 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    92 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    93 # be specifying the version(s) of Mercurial they are tested with, or
    93 # be specifying the version(s) of Mercurial they are tested with, or
    94 # leave the attribute unspecified.
    94 # leave the attribute unspecified.
    95 testedwith = 'ships-with-hg-core'
    95 testedwith = b'ships-with-hg-core'
    96 
    96 
    97 cmdtable = {}
    97 cmdtable = {}
    98 command = registrar.command(cmdtable)
    98 command = registrar.command(cmdtable)
    99 
    99 
   100 
   100 
   119             break
   119             break
   120         cls = cls.__bases__[0]
   120         cls = cls.__bases__[0]
   121 
   121 
   122     if cls is object:
   122     if cls is object:
   123         raise AttributeError(
   123         raise AttributeError(
   124             _("type '%s' has no property '%s'") % (origcls, propname)
   124             _(b"type '%s' has no property '%s'") % (origcls, propname)
   125         )
   125         )
   126 
   126 
   127 
   127 
   128 def _setuplog(ui):
   128 def _setuplog(ui):
   129     entry = commands.table['log|history']
   129     entry = commands.table[b'log|history']
   130     entry[1].append(
   130     entry[1].append(
   131         (
   131         (
   132             '',
   132             b'',
   133             'sparse',
   133             b'sparse',
   134             None,
   134             None,
   135             "limit to changesets affecting the sparse checkout",
   135             b"limit to changesets affecting the sparse checkout",
   136         )
   136         )
   137     )
   137     )
   138 
   138 
   139     def _initialrevs(orig, repo, opts):
   139     def _initialrevs(orig, repo, opts):
   140         revs = orig(repo, opts)
   140         revs = orig(repo, opts)
   141         if opts.get('sparse'):
   141         if opts.get(b'sparse'):
   142             sparsematch = sparse.matcher(repo)
   142             sparsematch = sparse.matcher(repo)
   143 
   143 
   144             def ctxmatch(rev):
   144             def ctxmatch(rev):
   145                 ctx = repo[rev]
   145                 ctx = repo[rev]
   146                 return any(f for f in ctx.files() if sparsematch(f))
   146                 return any(f for f in ctx.files() if sparsematch(f))
   147 
   147 
   148             revs = revs.filter(ctxmatch)
   148             revs = revs.filter(ctxmatch)
   149         return revs
   149         return revs
   150 
   150 
   151     extensions.wrapfunction(logcmdutil, '_initialrevs', _initialrevs)
   151     extensions.wrapfunction(logcmdutil, b'_initialrevs', _initialrevs)
   152 
   152 
   153 
   153 
   154 def _clonesparsecmd(orig, ui, repo, *args, **opts):
   154 def _clonesparsecmd(orig, ui, repo, *args, **opts):
   155     include_pat = opts.get(r'include')
   155     include_pat = opts.get(r'include')
   156     exclude_pat = opts.get(r'exclude')
   156     exclude_pat = opts.get(r'exclude')
   165         exclude = True
   165         exclude = True
   166     if enableprofile_pat:
   166     if enableprofile_pat:
   167         pat = enableprofile_pat
   167         pat = enableprofile_pat
   168         enableprofile = True
   168         enableprofile = True
   169     if sum([include, exclude, enableprofile]) > 1:
   169     if sum([include, exclude, enableprofile]) > 1:
   170         raise error.Abort(_("too many flags specified."))
   170         raise error.Abort(_(b"too many flags specified."))
   171     # if --narrow is passed, it means they are includes and excludes for narrow
   171     # if --narrow is passed, it means they are includes and excludes for narrow
   172     # clone
   172     # clone
   173     if not narrow_pat and (include or exclude or enableprofile):
   173     if not narrow_pat and (include or exclude or enableprofile):
   174 
   174 
   175         def clonesparse(orig, self, node, overwrite, *args, **kwargs):
   175         def clonesparse(orig, self, node, overwrite, *args, **kwargs):
   182                 enableprofile=enableprofile,
   182                 enableprofile=enableprofile,
   183                 usereporootpaths=True,
   183                 usereporootpaths=True,
   184             )
   184             )
   185             return orig(self, node, overwrite, *args, **kwargs)
   185             return orig(self, node, overwrite, *args, **kwargs)
   186 
   186 
   187         extensions.wrapfunction(hg, 'updaterepo', clonesparse)
   187         extensions.wrapfunction(hg, b'updaterepo', clonesparse)
   188     return orig(ui, repo, *args, **opts)
   188     return orig(ui, repo, *args, **opts)
   189 
   189 
   190 
   190 
   191 def _setupclone(ui):
   191 def _setupclone(ui):
   192     entry = commands.table['clone']
   192     entry = commands.table[b'clone']
   193     entry[1].append(('', 'enable-profile', [], 'enable a sparse profile'))
   193     entry[1].append((b'', b'enable-profile', [], b'enable a sparse profile'))
   194     entry[1].append(('', 'include', [], 'include sparse pattern'))
   194     entry[1].append((b'', b'include', [], b'include sparse pattern'))
   195     entry[1].append(('', 'exclude', [], 'exclude sparse pattern'))
   195     entry[1].append((b'', b'exclude', [], b'exclude sparse pattern'))
   196     extensions.wrapcommand(commands.table, 'clone', _clonesparsecmd)
   196     extensions.wrapcommand(commands.table, b'clone', _clonesparsecmd)
   197 
   197 
   198 
   198 
   199 def _setupadd(ui):
   199 def _setupadd(ui):
   200     entry = commands.table['add']
   200     entry = commands.table[b'add']
   201     entry[1].append(
   201     entry[1].append(
   202         (
   202         (
   203             's',
   203             b's',
   204             'sparse',
   204             b'sparse',
   205             None,
   205             None,
   206             'also include directories of added files in sparse config',
   206             b'also include directories of added files in sparse config',
   207         )
   207         )
   208     )
   208     )
   209 
   209 
   210     def _add(orig, ui, repo, *pats, **opts):
   210     def _add(orig, ui, repo, *pats, **opts):
   211         if opts.get(r'sparse'):
   211         if opts.get(r'sparse'):
   214                 dirname, basename = util.split(pat)
   214                 dirname, basename = util.split(pat)
   215                 dirs.add(dirname)
   215                 dirs.add(dirname)
   216             sparse.updateconfig(repo, list(dirs), opts, include=True)
   216             sparse.updateconfig(repo, list(dirs), opts, include=True)
   217         return orig(ui, repo, *pats, **opts)
   217         return orig(ui, repo, *pats, **opts)
   218 
   218 
   219     extensions.wrapcommand(commands.table, 'add', _add)
   219     extensions.wrapcommand(commands.table, b'add', _add)
   220 
   220 
   221 
   221 
   222 def _setupdirstate(ui):
   222 def _setupdirstate(ui):
   223     """Modify the dirstate to prevent stat'ing excluded files,
   223     """Modify the dirstate to prevent stat'ing excluded files,
   224     and to prevent modifications to files outside the checkout.
   224     and to prevent modifications to files outside the checkout.
   230         em = matchmod.exact(match.files())
   230         em = matchmod.exact(match.files())
   231         sm = matchmod.unionmatcher([self._sparsematcher, em])
   231         sm = matchmod.unionmatcher([self._sparsematcher, em])
   232         match = matchmod.intersectmatchers(match, sm)
   232         match = matchmod.intersectmatchers(match, sm)
   233         return orig(self, match, subrepos, unknown, ignored, full)
   233         return orig(self, match, subrepos, unknown, ignored, full)
   234 
   234 
   235     extensions.wrapfunction(dirstate.dirstate, 'walk', walk)
   235     extensions.wrapfunction(dirstate.dirstate, b'walk', walk)
   236 
   236 
   237     # dirstate.rebuild should not add non-matching files
   237     # dirstate.rebuild should not add non-matching files
   238     def _rebuild(orig, self, parent, allfiles, changedfiles=None):
   238     def _rebuild(orig, self, parent, allfiles, changedfiles=None):
   239         matcher = self._sparsematcher
   239         matcher = self._sparsematcher
   240         if not matcher.always():
   240         if not matcher.always():
   248                 dirstatefilestoremove = set(f for f in self if not matcher(f))
   248                 dirstatefilestoremove = set(f for f in self if not matcher(f))
   249                 changedfiles = dirstatefilestoremove.union(changedfiles)
   249                 changedfiles = dirstatefilestoremove.union(changedfiles)
   250 
   250 
   251         return orig(self, parent, allfiles, changedfiles)
   251         return orig(self, parent, allfiles, changedfiles)
   252 
   252 
   253     extensions.wrapfunction(dirstate.dirstate, 'rebuild', _rebuild)
   253     extensions.wrapfunction(dirstate.dirstate, b'rebuild', _rebuild)
   254 
   254 
   255     # Prevent adding files that are outside the sparse checkout
   255     # Prevent adding files that are outside the sparse checkout
   256     editfuncs = ['normal', 'add', 'normallookup', 'copy', 'remove', 'merge']
   256     editfuncs = [
       
   257         b'normal',
       
   258         b'add',
       
   259         b'normallookup',
       
   260         b'copy',
       
   261         b'remove',
       
   262         b'merge',
       
   263     ]
   257     hint = _(
   264     hint = _(
   258         'include file with `hg debugsparse --include <pattern>` or use '
   265         b'include file with `hg debugsparse --include <pattern>` or use '
   259         + '`hg add -s <file>` to include file directory while adding'
   266         + b'`hg add -s <file>` to include file directory while adding'
   260     )
   267     )
   261     for func in editfuncs:
   268     for func in editfuncs:
   262 
   269 
   263         def _wrapper(orig, self, *args, **kwargs):
   270         def _wrapper(orig, self, *args, **kwargs):
   264             sparsematch = self._sparsematcher
   271             sparsematch = self._sparsematcher
   265             if not sparsematch.always():
   272             if not sparsematch.always():
   266                 for f in args:
   273                 for f in args:
   267                     if f is not None and not sparsematch(f) and f not in self:
   274                     if f is not None and not sparsematch(f) and f not in self:
   268                         raise error.Abort(
   275                         raise error.Abort(
   269                             _(
   276                             _(
   270                                 "cannot add '%s' - it is outside "
   277                                 b"cannot add '%s' - it is outside "
   271                                 "the sparse checkout"
   278                                 b"the sparse checkout"
   272                             )
   279                             )
   273                             % f,
   280                             % f,
   274                             hint=hint,
   281                             hint=hint,
   275                         )
   282                         )
   276             return orig(self, *args, **kwargs)
   283             return orig(self, *args, **kwargs)
   277 
   284 
   278         extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
   285         extensions.wrapfunction(dirstate.dirstate, func, _wrapper)
   279 
   286 
   280 
   287 
   281 @command(
   288 @command(
   282     'debugsparse',
   289     b'debugsparse',
   283     [
   290     [
   284         ('I', 'include', False, _('include files in the sparse checkout')),
   291         (b'I', b'include', False, _(b'include files in the sparse checkout')),
   285         ('X', 'exclude', False, _('exclude files in the sparse checkout')),
   292         (b'X', b'exclude', False, _(b'exclude files in the sparse checkout')),
   286         ('d', 'delete', False, _('delete an include/exclude rule')),
   293         (b'd', b'delete', False, _(b'delete an include/exclude rule')),
   287         (
   294         (
   288             'f',
   295             b'f',
   289             'force',
   296             b'force',
   290             False,
   297             False,
   291             _('allow changing rules even with pending changes'),
   298             _(b'allow changing rules even with pending changes'),
   292         ),
   299         ),
   293         ('', 'enable-profile', False, _('enables the specified profile')),
   300         (b'', b'enable-profile', False, _(b'enables the specified profile')),
   294         ('', 'disable-profile', False, _('disables the specified profile')),
   301         (b'', b'disable-profile', False, _(b'disables the specified profile')),
   295         ('', 'import-rules', False, _('imports rules from a file')),
   302         (b'', b'import-rules', False, _(b'imports rules from a file')),
   296         ('', 'clear-rules', False, _('clears local include/exclude rules')),
   303         (b'', b'clear-rules', False, _(b'clears local include/exclude rules')),
   297         (
   304         (
   298             '',
   305             b'',
   299             'refresh',
   306             b'refresh',
   300             False,
   307             False,
   301             _('updates the working after sparseness changes'),
   308             _(b'updates the working after sparseness changes'),
   302         ),
   309         ),
   303         ('', 'reset', False, _('makes the repo full again')),
   310         (b'', b'reset', False, _(b'makes the repo full again')),
   304     ]
   311     ]
   305     + commands.templateopts,
   312     + commands.templateopts,
   306     _('[--OPTION] PATTERN...'),
   313     _(b'[--OPTION] PATTERN...'),
   307     helpbasic=True,
   314     helpbasic=True,
   308 )
   315 )
   309 def debugsparse(ui, repo, *pats, **opts):
   316 def debugsparse(ui, repo, *pats, **opts):
   310     """make the current checkout sparse, or edit the existing checkout
   317     """make the current checkout sparse, or edit the existing checkout
   311 
   318 
   346     any enabled profiles in place.
   353     any enabled profiles in place.
   347 
   354 
   348     Returns 0 if editing the sparse checkout succeeds.
   355     Returns 0 if editing the sparse checkout succeeds.
   349     """
   356     """
   350     opts = pycompat.byteskwargs(opts)
   357     opts = pycompat.byteskwargs(opts)
   351     include = opts.get('include')
   358     include = opts.get(b'include')
   352     exclude = opts.get('exclude')
   359     exclude = opts.get(b'exclude')
   353     force = opts.get('force')
   360     force = opts.get(b'force')
   354     enableprofile = opts.get('enable_profile')
   361     enableprofile = opts.get(b'enable_profile')
   355     disableprofile = opts.get('disable_profile')
   362     disableprofile = opts.get(b'disable_profile')
   356     importrules = opts.get('import_rules')
   363     importrules = opts.get(b'import_rules')
   357     clearrules = opts.get('clear_rules')
   364     clearrules = opts.get(b'clear_rules')
   358     delete = opts.get('delete')
   365     delete = opts.get(b'delete')
   359     refresh = opts.get('refresh')
   366     refresh = opts.get(b'refresh')
   360     reset = opts.get('reset')
   367     reset = opts.get(b'reset')
   361     count = sum(
   368     count = sum(
   362         [
   369         [
   363             include,
   370             include,
   364             exclude,
   371             exclude,
   365             enableprofile,
   372             enableprofile,
   370             clearrules,
   377             clearrules,
   371             reset,
   378             reset,
   372         ]
   379         ]
   373     )
   380     )
   374     if count > 1:
   381     if count > 1:
   375         raise error.Abort(_("too many flags specified"))
   382         raise error.Abort(_(b"too many flags specified"))
   376 
   383 
   377     if count == 0:
   384     if count == 0:
   378         if repo.vfs.exists('sparse'):
   385         if repo.vfs.exists(b'sparse'):
   379             ui.status(repo.vfs.read("sparse") + "\n")
   386             ui.status(repo.vfs.read(b"sparse") + b"\n")
   380             temporaryincludes = sparse.readtemporaryincludes(repo)
   387             temporaryincludes = sparse.readtemporaryincludes(repo)
   381             if temporaryincludes:
   388             if temporaryincludes:
   382                 ui.status(_("Temporarily Included Files (for merge/rebase):\n"))
   389                 ui.status(
   383                 ui.status(("\n".join(temporaryincludes) + "\n"))
   390                     _(b"Temporarily Included Files (for merge/rebase):\n")
       
   391                 )
       
   392                 ui.status((b"\n".join(temporaryincludes) + b"\n"))
   384             return
   393             return
   385         else:
   394         else:
   386             raise error.Abort(
   395             raise error.Abort(
   387                 _(
   396                 _(
   388                     'the debugsparse command is only supported on'
   397                     b'the debugsparse command is only supported on'
   389                     ' sparse repositories'
   398                     b' sparse repositories'
   390                 )
   399                 )
   391             )
   400             )
   392 
   401 
   393     if include or exclude or delete or reset or enableprofile or disableprofile:
   402     if include or exclude or delete or reset or enableprofile or disableprofile:
   394         sparse.updateconfig(
   403         sparse.updateconfig(