Commit Graph

688 Commits

Author SHA1 Message Date
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
Phil Cohen
4a54f4253f merge: replace repo.wvfs.unlinkpath() with calls to wctx[f].remove()
The code inside workingfilectx.remove() is a straight call to
repo.wvfs.unlinkpath, so this should be a no-op.
2017-06-25 16:58:26 -07:00
Phil Cohen
c6b8b5e8d4 merge: pass wctx to batchremove and batchget
We would like to migrate direct calls of repo.wvfs/wwrite/wread/etc to a
call on the relevant workingfilectx, both as a cleanup (to reduce the number of
working copy functions on `repo`), and also to facilitate an in-memory merge
that doesn't write to the working copy.

In order to do that, the first step is to ensure we pass the target wctx around
and perform our writes and reads on it. Later, this object might become a
memctx.
2017-06-25 16:56:49 -07:00
Pulkit Goyal
de83c26a54 py3: replace dict.iterkeys() with iter(dict)
dict.iterkeys() does not exists on Python 3.
2017-06-16 01:46:47 +05:30
Yuya Nishihara
6194f1a680 merge: use scmutil.intrev() to sort ctx objects
This moves wctx to the last, but that shouldn't matter. Only the order of
stored revisions is important.
2017-06-03 19:01:19 +09:00
Pulkit Goyal
3708dfc09e py3: convert bool variables to bytes before passing into ui.debug()
We can't pass unicodes to ui.debug() and hence we need to convert things to
bytes before passing them.
2017-06-01 02:14:26 +05:30
Pulkit Goyal
4e870f314a py3: replace None with -1 to sort an integer array
In Python 2:

>>> ls = [4, 2, None]
>>> sorted(ls)
[None, 2, 4]

In Python 3:

>>> ls = [4, 2, None]
>>> sorted(ls)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: NoneType() < int()

Therefore we replaced None with -1, and the safe part is that, None and -1 are
only the keys which are used for sorting so we don't need to convert the -1's
back to None.
2017-05-31 23:48:52 +05:30
Martin von Zweigbergk
5e8aba2103 merge: use intersectmatchers() in "m2-vs-ma optimization"
It doesn't seem like this can actually happen, but seems like cleaner
anyway.
2017-05-12 16:33:33 -07:00
Augie Fackler
d3c24043bb merge: migrate to context manager for changing dirstate parents 2017-05-18 17:11:24 -04:00
Phil Cohen
f313a0b236 merge: use repo.wvfs.setflags() instead of util.setflags()
Most merge.py code goes through the vfs instead of maniulating
files directly, so let's do the same here.
2017-05-11 18:38:43 -07:00
Durham Goode
4f79467143 rebase: use matcher to optimize manifestmerge
The old merge code would call manifestmerge and calculate the complete diff
between the source to the destination. In many cases, like rebase, the vast
majority of differences between the source and destination are irrelevant
because they are differences between the destination and the common ancestor
only, and therefore don't affect the merge. Since most actions are 'keep', all
the effort to compute them is wasted.

Instead, let's compute the difference between the source and the common ancestor
and only perform the diff of those files against the merge destination. When
using treemanifest, this lets us avoid loading almost the entire tree when
rebasing from a very old ancestor. This speeds up rebase of an old stack of 27
commits by 20x.

In mozilla-central, without treemanifest, when rebasing a commit from
default~100000 to default, this speeds up the manifestmerge step from 2.6s to
1.2s.  However, the additional diff adds an overhead to all manifestmerge calls,
especially for flat manifests. When rebasing a commit from default~1 to default
it appears to add 100ms in mozilla-central. While we could put this optimization
behind a flag, I think the fact that it makes merge O(number of changes being
applied) instead of O(number of changes between X and Y) justifies it.
2017-05-03 10:43:59 -07:00
Jun Wu
f331a9612f merge: use ProgrammingError 2017-03-26 16:55:56 -07:00
Durham Goode
30b5b6d685 merge: remove unnecessary matcher checks
As part of changing manifest.diff to accept a matcher, a previous patch added
matcher calls to each location in merge.manifestmerge that tested if 'x in mf'
to maintain the same behavior as before. After analyzing it further, this
matcher call isn't needed, and in fact hurts future patches ability to use the
matcher here.

Basically, all these 'if x in mf' checks were checking if a matched file's copy
source was in the matcher as well. This meant if you passed a matcher for just
file foo, it would not return file bar even if foo was a copy of bar. Since
manifestmerge cares about copy information, let's allow all lookups of copy
sources.

We also update one spot with a 'is not None' check, since it wasn't obvious that
the value could sometimes be None before, which broke when we called
matcher(None).

A future patch adds matcher optimizations to manifestmerge which causes this
code path to get covered by existing tests.
2017-03-19 11:42:17 -07:00
Martin von Zweigbergk
31bb49fb7d merge: also allow 'e' action with experimental.updatecheck=noconflict
With experimental.updatecheck=noconflict set, if one checks out
e1870a31325e (tests: add execute bit and fix shbang line, 2015-12-22)
and then try to check out its parent, hg will complain about
conflicting changes, even though the working directory is clean. We
need to also allow the 'e' action in merge.py. The 'e' action is used
when moving to a commit where the only change to the file is to its
executable flag, so it's just an optimized 'g' action.

Doesn't seem to be worth writing a test for, since the existing setup
in test-update-branches.t does not set any flags.
2017-03-13 21:58:43 -07:00
Mads Kiilerich
ea3cfbc6fb merge: check current wc branch for 'nothing to merge', not its p1
The working directory will usually be clean or very clean, and wc will usually
have the same branch as its parent. This change will thus usually not make any
difference and is done as a separate change to show that. It will be used in a
later change.
2017-03-12 16:41:46 -07:00
Pierre-Yves David
98f81e8c4f merge: directly use repo.vfs.join
The 'repo.join' method is about to be deprecated.
2017-03-08 16:53:32 -08:00
Mads Kiilerich
01ac38526d merge: use repo.wvfs.unlinkpath 2015-01-14 01:15:26 +01:00
Mads Kiilerich
a936a7f3a7 vfs: use repo.wvfs.unlinkpath 2015-01-14 01:15:26 +01:00
Durham Goode
5d0340324a merge: remove uses of manifest.matches
This gets rid of the manifest.matches calls in merge.py in favor of the new api.
This is part of getting rid of manifest.matches since it is O(manifest).
2017-03-07 18:38:20 -08:00
Martin von Zweigbergk
4ad88a61e0 update: for "noconflict" updates, print "conflicting changes" on conflict
With experimental.updatecheck=noconflict, if the update is aborted
because of conlicts, "uncommitted changes" is not quite
accurate. Let's use "conflicting changes" instead. Also fix the hint
to recomment --clean, not --merge, since that's what we do for other
failed updates.
2017-03-06 23:21:27 -08:00
Martin von Zweigbergk
066bf0dbb3 update: allow setting default update check to "noconflict"
The new value allows update (linear or not) as long as they don't
result in file merges.

I'm hoping that this value can some day become the default.
2017-02-13 00:05:55 -08:00
Martin von Zweigbergk
5b3eb48725 update: accept --merge to allow merging across topo branches (issue5125) 2017-02-13 12:58:37 -08:00
Martin von Zweigbergk
4904a44779 merge: combine the "merge" cases in docstring table 2017-02-27 15:09:19 -08:00
Martin von Zweigbergk
dbab66ea10 merge: combine "dirty" cases in docstring table 2017-02-27 15:07:01 -08:00
Martin von Zweigbergk
f2dcab33cd merge: clarify non-linear default updates in docstring table
Non-linear updates won't happen by default, regardless of -c/-C.
2017-02-27 15:29:34 -08:00
Martin von Zweigbergk
2230022a53 merge: combine the two "can't happen" cases in docstring table 2017-02-27 15:02:36 -08:00
Martin von Zweigbergk
2b1e4cc1c6 merge: move "incompatible options" case first in docstring table 2017-02-27 15:00:13 -08:00