Commit Graph

40 Commits

Author SHA1 Message Date
Adam Simpkins
640d8f6565 restore part of the mercurial.sparse module
Summary:
D12811551 removed the mercurial.sparse module.  However, actual removal of the
module needs to wait until the changes to the eden dirstate extension get
deployed.

This restores the module until all of the Eden changes have been deployed.
This module only contains a dummy `matcher()` function, which is the only part
of the module that Eden referenced.

This was causing exceptions for users with the a mercurial release from today,
but an Eden release from yesterday.  The exception would normally only occur
when using chg, since chg disables lazyimport.  Without chg, lazyimport would
allow the import to succeed even though the module does not exist.

Reviewed By: DurhamG

Differential Revision: D12877889

fbshipit-source-id: 2c6afd9bcb7031727b40fd32fbc1ba1356eb6e9b
2018-10-31 19:48:30 -07:00
Jun Wu
78584bd37d sparse: drop mercurial.sparse
Summary: We're using hgext.sparse.

Reviewed By: phillco

Differential Revision: D12811551

fbshipit-source-id: 780ec51350a62f4cfd3a31a0d4a1c61f6f72e38a
2018-10-30 19:22:43 -07:00
Durham Goode
a0fdd80720 dirstate: rename drop to untrack
Summary:
dirstate.drop() is used in two different situations. 1. To make a
tracked file become untracked, and 2. To remove a file from the dirstate
entirely. In the treestate case, this difference matters, so let's split drop
into two functions, one to mark something as untracked, and the other to remove
it entirely.

In this first patch we just rename drop to untrack. The next patch will
introduce the new delete function.

Reviewed By: ikostia

Differential Revision: D10317738

fbshipit-source-id: 85950ab1b1a10cd481edcfbba6da445b3dbf6397
2018-10-12 09:38:25 -07:00
Mark Thomas
483e3a915c hg: use repo.localvfs instead of repo.vfs
Summary: Update references in core mercurial to use repo.localvfs instead of repo.vfs.

Reviewed By: quark-zju

Differential Revision: D9699162

fbshipit-source-id: 0401677d2b0a1340e66cffb7ee907a0d93aa6717
2018-09-28 07:23:00 -07:00
Jun Wu
584656dff3 codemod: join the auto-formatter party
Summary:
Turned on the auto formatter. Ran `arc lint --apply-patches --take BLACK **/*.py`.
Then run `arc lint` again so some other autofixers like spellchecker etc. looked
at the code base. Manually accept the changes whenever they make sense, or use
a workaround (ex. changing "dict()" to "dict constructor") where autofix is false
positive. Disabled linters on files that are hard (i18n/polib.py) to fix, or less
interesting to fix (hgsubversion tests), or cannot be fixed without breaking
OSS build (FBPYTHON4).

Conflicted linters (test-check-module-imports.t, part of test-check-code.t,
test-check-pyflakes.t) are removed or disabled.

Duplicated linters (test-check-pyflakes.t, test-check-pylint.t) are removed.

An issue of the auto-formatter is lines are no longer guarnateed to be <= 80
chars. But that seems less important comparing with the benefit auto-formatter
provides.

As we're here, also remove test-check-py3-compat.t, as it is currently broken
if `PYTHON3=/bin/python3` is set.

Reviewed By: wez, phillco, simpkins, pkaush, singhsrb

Differential Revision: D8173629

fbshipit-source-id: 90e248ae0c5e6eaadbe25520a6ee42d32005621b
2018-05-25 22:17:29 -07:00
Mark Thomas
510a16a534 merge: add merge action 'pr' to rename files during update
Add a new merge action to handle a path conflict by renaming the conflicting
file to a safe name.

The rename is just to avoid problems on the filesystem.  The conflict is still
considered unresolved until the user marks the original path as resolved.

Differential Revision: https://phab.mercurial-scm.org/D777
2017-10-02 14:05:30 -07:00
Mark Thomas
813b547c22 merge: add merge action 'p' to record path conflicts during update
Add a new merge action to record path conflicts.  A status message is
printed, and the path conflict is added to the merge state.

Differential Revision: https://phab.mercurial-scm.org/D776
2017-10-02 14:05:30 -07:00
Kostia Balytskyi
ebe8e04f0d sparse: treat paths as cwd-relative
This commit makes it so sparse treats passed paths as CWD-relative,
not repo-root-realive. This is a more intuitive behavior in my (and some
other FB people's) opinion.

This is breaking change however. My hope here is that since sparse is
experimental, it's ok to introduce BCs.

The reason (glob)s are needed in the test is this: in these two cases we
do not supply path together with slashes, but `os.path.join` adds them, which
means that under Windows they can be backslashes. To demonstrate this behavior,
one could remove the (glob)s and run `./run-tests.py test-sparse.t` from
MinGW's terminal on Windows.
2017-08-04 05:38:22 -07:00
Kostia Balytskyi
158045d6f2 sparse: properly error out when absolute paths are used
Current logic is misleading (it says it drops only absolute paths, but
it actually drops all of them), not cross-platform (does not support Windows)
and IMO just wrong (as it should just error out if absolute paths are given).

This commit fixes it.
2017-08-02 15:05:21 -07:00
Gregory Szorc
8509056f34 sparse: add a requirement when a repository uses sparse (BC)
The presence of a sparse checkout can confuse legacy clients or
clients without sparse enabled for reasons that should be obvious.

This commit introduces a new repository requirement that tracks
whether sparse is enabled. The requirement is added when a sparse
config is activated and removed when the sparse config is reset.

The localrepository constructor has been taught to not open repos
with this requirement unless the sparse feature is enabled. It yields
a more actionable error message than what you would get if the
lockout were handled strictly at the requirements verification phase.
Old clients that aren't sparse aware will see the generic
"repository requires features unknown to this Mercurial" error,
however.

The new requirement has "exp" in its name to reflect the
experimental nature of sparse. There's a chance that the eventual
non-experimental feature won't change significantly and we could
have squatted on the "sparse" requirement without ill effect. If
that happens, we can teach new clients to still recognize the old
name. But I suspect we'll sneak in some BC and we'll want a new
requirement to convey new meaning.

Differential Revision: https://phab.mercurial-scm.org/D110
2017-07-17 11:45:38 -07:00
Gregory Szorc
e4e4915b76 sparse: consolidate common code for writing sparse config
In 3 functions we were writing the sparse config and updating the
working directory. In two of them we had a transaction-like process
for restoring the sparse config in case of wdir update fail.

Because the pattern is common, we've already made mistakes, and the
complexity will increase in the near future, let's consolidate the
code into a reusable function.

As part of this refactor, we end up reading the "sparse" file twice
when updating it. This is a bit sub-optimal. But I don't think it
is worth the code complexity to pass around the variables to avoid
the redundancy.

Differential Revision: https://phab.mercurial-scm.org/D109
2017-07-17 11:21:23 -07:00
Gregory Szorc
fde2177334 sparse: require [section] in sparse config files (BC)
Previously, [include] was implicit and pattern lines before a
[section] were added to includes.

Because the format may change in the future and explicit behavior,
well, more explicit, this commit changes the config parser to
reject pattern lines that don't occur in a [section].

Differential Revision: https://phab.mercurial-scm.org/D96
2017-07-15 13:21:23 -07:00
Gregory Szorc
edf6de3f78 sparse: use set for capturing profiles
Order doesn't need to be preserved. A set is acceptable.

Differential Revision: https://phab.mercurial-scm.org/D95
2017-07-15 13:07:57 -07:00
Jun Wu
e47f7dc2fa codemod: register core configitems using a script
This is done by a script [2] using RedBaron [1], a tool designed for doing
code refactoring. All "default" values are decided by the script and are
strongly consistent with the existing code.

There are 2 changes done manually to fix tests:

  [warn] mercurial/exchange.py: experimental.bundle2-output-capture: default needs manual removal
  [warn] mercurial/localrepo.py: experimental.hook-track-tags: default needs manual removal

Since RedBaron is not confident about how to indent things [2].

[1]: https://github.com/PyCQA/redbaron
[2]: https://github.com/PyCQA/redbaron/issues/100
[3]:

#!/usr/bin/env python
# codemod_configitems.py - codemod tool to fill configitems
#
# Copyright 2017 Facebook, 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, print_function

import os
import sys

import redbaron

def readpath(path):
    with open(path) as f:
        return f.read()

def writepath(path, content):
    with open(path, 'w') as f:
        f.write(content)

_configmethods = {'config', 'configbool', 'configint', 'configbytes',
                  'configlist', 'configdate'}

def extractstring(rnode):
    """get the string from a RedBaron string or call_argument node"""
    while rnode.type != 'string':
        rnode = rnode.value
    return rnode.value[1:-1]  # unquote, "'str'" -> "str"

def uiconfigitems(red):
    """match *.ui.config* pattern, yield (node, method, args, section, name)"""
    for node in red.find_all('atomtrailers'):
        entry = None
        try:
            obj = node[-3].value
            method = node[-2].value
            args = node[-1]
            section = args[0].value
            name = args[1].value
            if (obj in ('ui', 'self') and method in _configmethods
                and section.type == 'string' and name.type == 'string'):
                entry = (node, method, args, extractstring(section),
                         extractstring(name))
        except Exception:
            pass
        else:
            if entry:
                yield entry

def coreconfigitems(red):
    """match coreconfigitem(...) pattern, yield (node, args, section, name)"""
    for node in red.find_all('atomtrailers'):
        entry = None
        try:
            args = node[1]
            section = args[0].value
            name = args[1].value
            if (node[0].value == 'coreconfigitem' and section.type == 'string'
                and name.type == 'string'):
                entry = (node, args, extractstring(section),
                         extractstring(name))
        except Exception:
            pass
        else:
            if entry:
                yield entry

def registercoreconfig(cfgred, section, name, defaultrepr):
    """insert coreconfigitem to cfgred AST

    section and name are plain string, defaultrepr is a string
    """
    # find a place to insert the "coreconfigitem" item
    entries = list(coreconfigitems(cfgred))
    for node, args, nodesection, nodename in reversed(entries):
        if (nodesection, nodename) < (section, name):
            # insert after this entry
            node.insert_after(
                'coreconfigitem(%r, %r,\n'
                '    default=%s,\n'
                ')' % (section, name, defaultrepr))
            return

def main(argv):
    if not argv:
        print('Usage: codemod_configitems.py FILES\n'
              'For example, FILES could be "{hgext,mercurial}/*/**.py"')
    dirname = os.path.dirname
    reporoot = dirname(dirname(dirname(os.path.abspath(__file__))))

    # register configitems to this destination
    cfgpath = os.path.join(reporoot, 'mercurial', 'configitems.py')
    cfgred = redbaron.RedBaron(readpath(cfgpath))

    # state about what to do
    registered = set((s, n) for n, a, s, n in coreconfigitems(cfgred))
    toregister = {} # {(section, name): defaultrepr}
    coreconfigs = set() # {(section, name)}, whether it's used in core

    # first loop: scan all files before taking any action
    for i, path in enumerate(argv):
        print('(%d/%d) scanning %s' % (i + 1, len(argv), path))
        iscore = ('mercurial' in path) and ('hgext' not in path)
        red = redbaron.RedBaron(readpath(path))
        # find all repo.ui.config* and ui.config* calls, and collect their
        # section, name and default value information.
        for node, method, args, section, name in uiconfigitems(red):
            if section == 'web':
                # [web] section has some weirdness, ignore them for now
                continue
            defaultrepr = None
            key = (section, name)
            if len(args) == 2:
                if key in registered:
                    continue
                if method == 'configlist':
                    defaultrepr = 'list'
                elif method == 'configbool':
                    defaultrepr = 'False'
                else:
                    defaultrepr = 'None'
            elif len(args) >= 3 and (args[2].target is None or
                                     args[2].target.value == 'default'):
                # try to understand the "default" value
                dnode = args[2].value
                if dnode.type == 'name':
                    if dnode.value in {'None', 'True', 'False'}:
                        defaultrepr = dnode.value
                elif dnode.type == 'string':
                    defaultrepr = repr(dnode.value[1:-1])
                elif dnode.type in ('int', 'float'):
                    defaultrepr = dnode.value
            # inconsistent default
            if key in toregister and toregister[key] != defaultrepr:
                defaultrepr = None
            # interesting to rewrite
            if key not in registered:
                if defaultrepr is None:
                    print('[note] %s: %s.%s: unsupported default'
                          % (path, section, name))
                    registered.add(key) # skip checking it again
                else:
                    toregister[key] = defaultrepr
                    if iscore:
                        coreconfigs.add(key)

    # second loop: rewrite files given "toregister" result
    for path in argv:
        # reconstruct redbaron - trade CPU for memory
        red = redbaron.RedBaron(readpath(path))
        changed = False
        for node, method, args, section, name in uiconfigitems(red):
            key = (section, name)
            defaultrepr = toregister.get(key)
            if defaultrepr is None or key not in coreconfigs:
                continue
            if len(args) >= 3 and (args[2].target is None or
                                   args[2].target.value == 'default'):
                try:
                    del args[2]
                    changed = True
                except Exception:
                    # redbaron fails to do the rewrite due to indentation
                    # see https://github.com/PyCQA/redbaron/issues/100
                    print('[warn] %s: %s.%s: default needs manual removal'
                          % (path, section, name))
            if key not in registered:
                print('registering %s.%s' % (section, name))
                registercoreconfig(cfgred, section, name, defaultrepr)
                registered.add(key)
        if changed:
            print('updating %s' % path)
            writepath(path, red.dumps())

    if toregister:
        print('updating configitems.py')
        writepath(cfgpath, cfgred.dumps())

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
2017-07-14 14:22:40 -07:00
Martin von Zweigbergk
3c2e121c63 match: write forceincludematcher using unionmatcher
The forceincludematcher is simply a unionmatcher of a includematcher
(matching paths recursively) with the given matcher. Since the
forceincludematcher is only used by sparse, move it there.

I don't have a good sparse repo setup to test performance impact on.

Differential Revision: https://phab.mercurial-scm.org/D57
2017-07-07 14:39:59 -07:00
Gregory Szorc
2777ae7399 sparse: shorten try..except block in updateconfig()
It now only covers refreshwdir(). This is what importfromfiles()
does. I think it is the more appropriate behavior.
2017-07-10 21:55:43 -07:00
Gregory Szorc
28576724a2 sparse: clean up updateconfig()
* Use context manager for wlock
* Rename oldsparsematch to oldmatcher
* Always call parseconfig() because parsing an empty string yields
  the same result as the old code
2017-07-10 21:43:19 -07:00
Gregory Szorc
0ee6ecfbec sparse: move config updating function into core
As part of the move, the ui argument was dropped.

Additional fixups will be made in a follow-up commit.
2017-07-10 21:39:49 -07:00
Gregory Szorc
6155516e71 sparse: move code for importing rules from files into core
This is a pretty straightforward port. Some code cleanup was
performed. But no major changes to the logic were made.

I'm not a huge fan of this function because it does multiple
things. I'd like to get things into core first to facilitate
refactoring later.

Please also note the added inline comment about the oddities
of writeconfig() and the try..except to undo it. This is because
of the hackiness in which the sparse matcher is obtained by
various consumers, notably dirstate. We'll need a massive
refactor to address this. That refactor is effectively blocked
on having the sparse dirstate hacks live in core.
2017-07-08 14:15:07 -07:00
Gregory Szorc
21d9237e1c sparse: refactor activeprofiles into a generic function (API)
activeprofiles() is a special case of a more generic function.
Furthermore, that generic function is essentially already
implemented inline in the sparse extension.

So, refactor activeprofiles() to a generic activeconfig(). Change
the only consumer of activeprofiles() to use it. And have the
inline implementation in the sparse extension use it.
2017-07-08 14:01:32 -07:00
Martin von Zweigbergk
fa470fc7e5 sparse: access status fields by name instead of deconstructing it
The status tuples has had named fields for a few years now.
2017-07-06 22:20:38 -07:00
Gregory Szorc
d86e3657d2 sparse: move printing of sparse config changes function into core
As part of the port, all arguments now have default values of 0.
Strings are now also given the i18n treatment.
2017-07-08 13:34:19 -07:00
Gregory Szorc
519ece1048 sparse: move code for clearing rules to core
This is a pretty straightforward port.
2017-07-08 13:19:38 -07:00
Gregory Szorc
2689134340 sparse: move post commit actions into core
Instead of wrapping committablectx.markcommitted(), we inline
the call into workingctx.markcommitted().

Per smf's review, workingctx is the proper location for this
code, as committablectx is the shared base class for it and
memctx. Since this code touches the working directory, it belongs
in workingctx.
2017-07-07 11:51:10 -07:00
Gregory Szorc
87044d7937 sparse: inline signature cache clearing
It is a trivial one-liner. No need to have a separate function.
2017-07-06 16:10:28 -07:00
Gregory Szorc
793c8fb431 sparse: move working directory refreshing into core
This is a pretty straightforward move of the code.

I converted the "force" argument to a keyword argument.

Like other recent changes, this code is tightly coupled with
working directory update code in merge.py. I suspect the code
will become more tightly coupled over time, possibly even moved
to merge.py. For now, let's get the code in core.
2017-07-06 14:53:08 -07:00
Gregory Szorc
7fec603f86 sparse: refactor update actions filtering and call from core
merge.calculateupdates() now filters the update actions through sparse
by default.

The filtering no-ops if sparse isn't enabled or no sparse config
is defined.

The function has been refactored to behave more like a filter
instead of a wrapper of merge.calculateupdates().

We should arguably take sparse into account earlier in
merge.calculateupdates(). This patch preserves the old behavior
of applying sparse at the end of update calculation, which is the
simplest and safest approach.
2017-07-06 16:29:31 -07:00
Gregory Szorc
7fff0417c9 sparse: move update action filtering into core
This is a relatively straight port of the function. It is pretty large.
So refactoring will be postponed to a subsequent commit.
2017-07-06 16:17:35 -07:00
Gregory Szorc
6dce563cd3 sparse: move pruning of temporary includes into core
This was our last method on the custom repo type, meaning we could
remove that custom type and inline the 2 lines of code into
reposetup().

As part of the move, instead of wrapping merge.update() from
the sparse extension, we inline the function call. The ported
function now no-ops if sparse isn't enabled, making it safe to
always call.

The call site in update() may not be the most appropriate. But
it matches the previous behavior, which is the safest thing
to do. It can be improved later.
2017-07-06 14:33:18 -07:00
Gregory Szorc
26fd8a7af7 sparse: move function for resolving sparse matcher into core
As part of the move, the function arguments changed so revs are
passed as a list instead of *args. This allows us to use keyword
arguments properly.

Since the plan is to integrate sparse into core and have it
enabled by default, we need to prepare for a sparse matcher
to always be obtained and operated on. As part of the move,
we inserted code that returns an always matcher if sparse
isn't enabled. Some callers in the sparse extension take this
into account and conditionally perform matching depending on
whether the special always matcher is seen. I /think/ this
may have sped up some operations where the extension is
installed but no sparse config is activated.

One thing I'm ensure of in this code is whether os.path.dirname()
is semantically correct. os.posixpath.dirname() (which is
exported as pathutil.dirname) might be a better choise because
all patterns should be using posix directory separators (/)
instead of Windows (\). There's an inline comment that implies
Windows was tested. So hopefully it won't be a problem. We
can improve this in a follow-up. I've added a TODO to track it.
2017-07-06 17:41:45 -07:00
Gregory Szorc
6b6712c33f sparse: clean up config signature code
Before, 0 was being used as the default signature value and we cast
the int to a string. We also handled I/O exceptions manually.

The new code uses cfs.tryread() so we always feed data into the
hasher. The empty string does hash and and should be suitable
for input into a cache key.

The changes made the code simple enough that the separate checksum
function could be inlined.
2017-07-06 16:01:36 -07:00
Gregory Szorc
0338e1f32a sparse: move config signature logic into core
This is a pretty straightforward port. It will be cleaned up in
a subsequent commit.
2017-07-06 16:11:56 -07:00
Gregory Szorc
fa7c02cef4 sparse: move some temporary includes functions into core
Functions for reading and writing the tempsparse file have been
moved. prunetemporaryincludes() will be moved separately
because it is non-trivial.
2017-07-06 14:48:16 -07:00
Gregory Szorc
23bd6434bf sparse: move config file writing into core
The code was refactored during the move to be more procedural
instead of using string formatting. This has the benefit of not
writing empty sections, which changed tests.
2017-07-06 12:24:55 -07:00
Gregory Szorc
0cd417305b localrepo: add sparse caches
The sparse extension maintains caches for the sparse files
to a signature and a signature to a matcher. This allows the
sparse matchers to be resolved quickly, which is apparently
something that can occur in loops.

This patch ports the sparse caches to the localrepo class
pretty much as-is. There is potentially room to improve the
caching mechanism. But that can be done as a follow-up.

The default invalidatecaches() now clears the relevant sparse
cache. invalidatesignaturecache() has been moved to sparse.py.
2017-07-06 12:20:53 -07:00
Gregory Szorc
82797a75d4 sparse: move active profiles function into core
Also includes some light formatting changes.
2017-07-06 12:26:04 -07:00
Gregory Szorc
b77eafa212 sparse: move resolving of sparse patterns for rev into core
This method is reasonably well-contained and simple to move.

As part of the move, some light formatting was performed.

A "working copy" reference in an error message was changed to
"working directory."

The biggest change was to _refreshoncommit() in sparse.py. It
was previously checking for the existence of an attribute on
the repo instance. Since the moved function now returns empty
data if sparse isn't enabled, we unconditionally call the
new function. However, we do have to protect another method
call in that function. This will all be unhacked eventually.
2017-07-06 12:15:14 -07:00
Gregory Szorc
19d9143b89 sparse: variable to track if sparse is enabled
Currently, the sparse extension sniffs repo instances for
attributes defined by the sparse extension to determine if
sparse is enabled. As we move code away from repo instances,
these checks will be a bit more brittle.

We introduce a module-level variable to track whether sparse is
enabled as a temporary workaround.
2017-07-06 12:06:37 -07:00
Gregory Szorc
c16ee0ee8c sparse: move profile reading into core
One more step towards weaning off methods on repo instances and
moving code to core. While this function is only used once and
is simple, it needs to exist on its own so Facebook can monkeypatch
it to enable simplecache integration.
2017-07-06 12:14:12 -07:00
Gregory Szorc
2316ea9a38 sparse: move config parsing into core
This patch marks the beginning of moving code from the sparse
extension into core. The goal is to move as much of the
functionality as possible into core, where it will be an
experimental feature. The extension will likely continue to
exist to enable the feature and provide UI elements.

As part of the move, the repo method was converted to a module
function. It doesn't need to exist on repos.

An error message was also updated to reflect that an error isn't
necessarily from the .hg/sparse file. The API should be updated
later to pass in a filename so the error can be more descriptive.

Copyright of the added file was copied from the sparse extension.
2017-07-06 12:14:03 -07:00