Commit Graph

714 Commits

Author SHA1 Message Date
Jun Wu
2946a1c198 codemod: use single blank line
Summary: This makes test-check-code cleaner.

Reviewed By: ryanmce

Differential Revision: D6937934

fbshipit-source-id: 8f92bc32f75b9792ac67db77bb3a8756b37fa941
2018-04-13 21:51:08 -07:00
Phil Cohen
d252e1a488 merge: only abort in IMM if files are actually marked as driver-resolved
Summary:
Before, we would raise whenever the `usemergedriver` condition was set when merging in-memory,
which equated to "any merge with (cd, dc, or m) actions in a repo with a mergedriver script".
This was done to be as conservative as possible.

However, a better solution is to run the preprocess() script and only raise if any files are
marked to actually be driver-resolved. That way we only restart the merge if we absolutely need
to.

Since some of our preprocess() scripts aren't ready yet, I also added
experimental.inmemory.nomergedriver in a previous change so we can deploy this in a build before the preprocess scripts are good to go.

Test Plan: ./run-tests.py

Reviewers: quark, #sourcecontrol

Reviewed By: quark

Subscribers: durham

Differential Revision: https://phabricator.intern.facebook.com/D6668426

Signature: 6668426:1515185050:a640208454caf053f8213b831d0f8e645ebe682c
2018-01-16 17:24:16 -08:00
Phil Cohen
a1c1d5e38d merge: fix default value for experimental.inmemorydisallowedpaths 2018-01-16 17:23:22 -08:00
Phil Cohen
8a4473f1d0 merge: log which files were driver-resolved to scuba
Summary: Log whichever paths were driver-resolved but not in experimental.inmemorydisallowedpaths, so we can update experimental.inmemorydisallowedpaths and keep the experience of rebasing with IMM and
mergedriver a pleasant one.

Test Plan: .

Reviewers: #sourcecontrol

Differential Revision: https://phabricator.intern.facebook.com/D6656159
2018-01-16 17:23:15 -08:00
Phil Cohen
1a2012bad3 merge: raise before running mergedriver if using IMM
Merge driver scripts run in the working copy, so disable with IMM for now.

Differential Revision: https://phab.mercurial-scm.org/D1781
2018-01-03 05:35:56 -08:00
Phil Cohen
d99691c52d merge: don't check for unknown files in IMM
Differential Revision: https://phab.mercurial-scm.org/D1214
2017-12-07 13:20:47 -08:00
Phil Cohen
da19a4a9f4 merge: skip subrepo state, update hooks, and updating the dirstate in IMM
Differential Revision: https://phab.mercurial-scm.org/D1215
2017-12-01 00:07:23 -08:00
Phil Cohen
d0b4ef96b4 merge: remove calls to flushall()
Since D1105, these are unnecessary since IMM will now never use workers.

Differential Revision: https://phab.mercurial-scm.org/D1213
2017-12-01 00:07:23 -08:00
Mark Thomas
107956c510 merge: check created file dirs for path conflicts only once (issue5716)
In large repositories, updates involving the creation of many files check the
same directories repeatedly in the wctx manifest.  Move these checks out to a
separate loop to avoid repeated checks hitting the manifest.

Differential Revision: https://phab.mercurial-scm.org/D1226
2017-11-24 12:53:58 -08:00
Mark Thomas
2f4962c2a4 merge: cache unknown dir checks (issue5716)
As mentioned in D1222, the recent pathconflicts change regresses update
performance in large repositories when many files are being updated.

To mitigate this, we introduce two caches of directories that have
already found to be either:

  - unknown directories, but which are not aliased by files and
    so don't need to be checked if they are files again; and

  - missing directores, which cannot cause path conflicts, and
    cannot contain a file that causes a path conflict.

When checking the paths of a file, testing against this caches means we can
skip tests that involve touching the filesystem.

Differential Revision: https://phab.mercurial-scm.org/D1224
2017-11-24 12:53:58 -08:00
Siddharth Agarwal
1fe04b20a0 merge: add a config option to disable path conflict checking
We've found a severe perf regression in `hg update` caused by the path conflict
checking code. The next patch will disable this by default.

Differential Revision: https://phab.mercurial-scm.org/D1222
2017-10-24 11:14:38 -07:00
muxator
571ba67b50 update: mention long options explicitly in description of merge.update()
The short options "-c" and "-C" may be confusing for a novice reading the
documentation. Let's try to be more explicit, also mentioning the equivalent
long options ("--check" and "--clean") in the comments.
2017-10-04 23:22:34 +02:00
Gregory Szorc
35538a441a fsmonitor: warn when fsmonitor could be used
fsmonitor can significantly speed up operations on large working
directories. But fsmonitor isn't enabled by default, so naive users
may not realize there is a potential to make Mercurial faster.

This commit introduces a warning to working directory updates when
fsmonitor could be used.

The following conditions must be met:

* Working directory is previously empty
* New working directory adds >= N files (currently 50,000)
* Running on Linux or MacOS
* fsmonitor not enabled
* Warning not disabled via config override

Because of the empty working directory restriction, most users will
only see this warning during `hg clone` (assuming very few users
actually do an `hg up null`).

The addition of a warning may be considered a BC change. However, clone
has printed warnings before. Until recently, Mercurial printed a warning
with the server's certificate fingerprint when it wasn't explicitly
trusted for example. The warning goes to stderr. So it shouldn't
interfere with scripts parsing meaningful output.

The OS restriction was on the advice of Facebook engineers, who only
feel confident with watchman's stability on the supported platforms.

.. feature::

   Print warning when fsmonitor isn't being used on a large repository

Differential Revision: https://phab.mercurial-scm.org/D894
2017-10-18 22:57:15 +02:00
Phil Cohen
a04280b2f9 context: add workingfilectx.markcopied
With in-memory merge, copy information needs to be stored in-memory, not in the
dirstate.

To make this transition easy, move the existing dirstate-based approach to
workingfilectx; that way, other implementations can choose to store it
somewhere else.

Differential Revision: https://phab.mercurial-scm.org/D1106
2017-10-15 20:36:29 -07:00
Phil Cohen
fac2e07a2a merge: don't use workers in in-memory mode
The worker processes can't share memory, so workers should not be used.

Differential Revision: https://phab.mercurial-scm.org/D1105
2017-10-15 20:36:29 -07:00
Ryan McElroy
25aff2d866 merge: ensure that we always commit the mergestate
In future patches, we may halt the merge process based on configuration or
user requests by raising exceptions. We need to ensure that the mergestate
is unconditionally committed even when such an exception is raised.

Depends on D930.

Differential Revision: https://phab.mercurial-scm.org/D931
2017-10-06 06:48:43 -07:00
Mark Thomas
2dff07bf2e merge: improve comments in mergestate._makerecords
Differential Revision: https://phab.mercurial-scm.org/D955
2017-10-05 08:03:57 -07:00
Mark Thomas
75628a759b merge: check for path conflicts when merging (issue5628)
When merging, check for any path conflicts introduced by the manifest
merge and rename the conflicting file to a safe name.

Differential Revision: https://phab.mercurial-scm.org/D784
2017-10-02 14:05:30 -07:00
Mark Thomas
08fafa25e1 merge: improve error messages for path conflicts during update
Differential Revision: https://phab.mercurial-scm.org/D782
2017-10-02 14:05:30 -07:00
Mark Thomas
710343cbad merge: check for path conflicts when updating (issue5628)
When updating to a new revision, check for path conflicts caused by unknown
files in the working directory, and handle these by backing up the file or
directory and replacing it.

Differential Revision: https://phab.mercurial-scm.org/D781
2017-10-02 14:05:30 -07:00
Mark Thomas
8d254022c6 merge: rename conflicts to fileconflicts in _checkunknownfiles
We will need to distinguish between file conflicts and path conflicts.  Rename
the conflicts variable so that it will be clearly distinct from pathconflicts,
which will be introduced in a future commit.

Differential Revision: https://phab.mercurial-scm.org/D780
2017-10-02 14:05:30 -07:00
Mark Thomas
3e2f0b4499 merge: add _checkunknowndirs function for detecting path conflicts
Add a new function which, given a file name, finds the shortest path for which
there is a conflicting file or directory in the working directory.

Differential Revision: https://phab.mercurial-scm.org/D779
2017-10-02 14:05:30 -07:00
Mark Thomas
8fecfcf567 merge: backup conflicting directories when getting files
During batchget, if a target file conflicts with a directory, or if the
directory a target file is in conflicts with a file, backup and remove the
conflicting file or directory before performing the get.

Differential Revision: https://phab.mercurial-scm.org/D778
2017-10-02 14:05:30 -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
Mark Thomas
73522e2f05 merge: add pathconflict merge state
Path conflicts that occur during merges are represented by 'pu' (unresolved)
and 'pr' (resolved) records in the merge state.  These are stored on disk
in 'P' records.

Differential Revision: https://phab.mercurial-scm.org/D774
2017-10-02 14:05:30 -07:00
Boris Feld
40e88b2e2b configitems: register 'merge.checkunknown' and 'merge.checkignored'
They both use the same function defining a default, so we need to update them at
the same time.
2017-10-08 21:48:40 +02:00
Alex Gaynor
88c328dbf9 style: never use a space before a colon or comma
Differential Revision: https://phab.mercurial-scm.org/D954
2017-09-29 15:48:34 +00:00
Boris Feld
4ec25dd5f8 configitems: register the 'merge.preferancestor' config 2017-06-30 03:43:13 +02:00
Pulkit Goyal
eecf1e98ad py3: use pycompat.bytestr instead of str
Differential Revision: https://phab.mercurial-scm.org/D855
2017-09-30 15:48:08 +05:30
Pulkit Goyal
2c8e44b5f7 py3: explicitly convert dict.keys() and dict.items() into a list
Differential Revision: https://phab.mercurial-scm.org/D853
2017-09-30 15:45:15 +05:30
Phil Cohen
1becfadc5c merge: allow a custom working context to be passed to update
This will allow anyone to enable the first in-menmory merge milestone
by wrapping merge.update in an extension and creating an overlayworkingctx.

Differential Revision: https://phab.mercurial-scm.org/D682
2017-09-14 13:14:32 -07:00
Phil Cohen
be35a1685b merge: move cwd-missing detection to helper functions
This will exist in two places with defered writes, so we want to avoid
duplication.

Differential Revision: https://phab.mercurial-scm.org/D626
2017-09-12 19:27:01 -07:00
Phil Cohen
b481a15354 merge: flush any deferred writes just before recordupdates()
``recordupdates`` calls into the dirstate which requires the files to be
there, so this is the last possible moment we can flush anything.

Differential Revision: https://phab.mercurial-scm.org/D673
2017-09-11 13:17:43 -07:00
Phil Cohen
0e9cb373ec merge: flush any deferred writes before, and after, running any workers
Since we fork to create workers, any changes they queue up will be lost after
the worker terminates, so the easiest solution is to have each worker flush
the writes they accumulate--we are close to the end of the merge in any case.

To prevent duplicated writes, we also have the master processs flush before
forking.

In an in-memory merge (M2), we'll instead disable the use of workers.

Differential Revision: https://phab.mercurial-scm.org/D628
2017-09-11 13:03:27 -07:00
Phil Cohen
a1eba8f292 merge: pass wctx to premerge, filemerge
In the in-memory merge branch. we'll need to call a function (``flushall``) on
the wctx inside of _xmerge.

This prepares the way so it can be done without hacks like ``fcd.ctx()``.

Differential Revision: https://phab.mercurial-scm.org/D449
2017-09-11 13:03:27 -07:00
Phil Cohen
630437a97b merge: move some of the logic in batchget() to workingfilectx
We will use this logic in two places with in-memory merge.

Differential Revision: https://phab.mercurial-scm.org/D444
2017-08-31 11:28:59 -07:00
Alex Gaynor
ea20251106 merge: removed sorting in casefolding detection, for a slight performance win
It was not required for the correctness of the algorithm.

Differential Revision: https://phab.mercurial-scm.org/D30
2017-07-14 19:27:28 +00: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
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
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
Martin von Zweigbergk
1f47781cfe mergestate: implement unresolvedcount() in terms of unresolved()
This simplifies the method slightly. It does create a full list of
paths while doing so, but it's not a lot of data anyway (besides, I
would think references to strings are no larger than (references to?)
True).
2015-12-01 09:26:33 -08:00
Martin von Zweigbergk
490806f981 mergestate: make unresolved() use iteritems()
mergestate.unresolved() is a generator, so it seems better for it to
rely on iteritems() than items(), although it also seems unlikely for
it to make a noticeable difference.
2015-12-01 09:26:10 -08:00
Phil Cohen
a65f43e3e4 workingfilectx: add exists, lexists
Switch the lone call in merge.py to use it.

As with past refactors, the goal is to make wctx hot-swappable with an
in-memory context in the future. This change should be a no-op today.
2017-07-04 22:35:52 -07:00
Pierre-Yves David
ab57951fd7 obsutil: move 'foreground' to the new modules
We have a new 'obsutil' module now. We move the high level utility there to
bring 'obsolete.py' back to a more reasonable size.
2017-06-27 01:40:34 +02:00
Pulkit Goyal
35cedf7901 py3: use hex() to convert the hash to bytes 2017-06-26 17:20:46 +05:30
Phil Cohen
22861c5e86 workingfilectx: add audit() as a wrapper for wvfs.audit() 2017-06-25 22:30:14 -07:00
Phil Cohen
8b63d9e6f0 workingfilectx: add backgroundclose as a kwarg to write()
This is necessary because some callers in merge.py pass backgroundclose=True
when writing.

As with previous changes in this series, this should be a no-op.
2017-06-25 22:30:14 -07:00
Phil Cohen
67430178c3 merge: change repo.wvfs.setflags calls to a new wctx[f].setflags function
As with previous changes in this series, this should be a no-op.
2017-06-25 22:29:09 -07:00
Phil Cohen
522fc7d98c merge: convert repo.wwrite() calls to wctx[f].write()
As with the previous patch in this series, workingfilectx.write() is a direct
call to repo.wwrite(), so this change should be a no-op.
2017-06-25 17:00:15 -07:00