Commit Graph

247 Commits

Author SHA1 Message Date
Boris Feld
9c89c78d23 obsolete: add a devel.user.obsmarker
In tests, we often want to use a different username in obs-markers without
changing the default user. Add a 'devel.user.obsmarker'.
2017-10-06 16:15:24 +02:00
Boris Feld
f17532cf73 effectflag: store an empty effect flag for the moment
The idea behind effect flag is to store additional information in obs-markers
about what changed between a changeset and its successor(s). It's a low-level
information that comes without guarantees.

This information can be computed a posteriori, but only if we have all
changesets locally. This is not the case with distributed workflows where you
work with several people or on several computers (eg: laptop + build server).

Storing the effect-flag as a bitfield has several advantages:

- It's compact, we are using one byte per obs-marker at most for the effect-
  flag.
- It's compoundable, the obsfate log approach needs to display evolve history
  that could spans several obs-markers. Computing the effect-flag between a
  changeset and its grand-grand-grand-successor is simple thanks to the
  bitfield.

The effect-flag design has also some limitations:

- Evolving a changeset and reverting these changes just after would lead to
  two obs-markers with the same effect-flag without information that the first
  and third changesets are the same.

The effect-flag current design is a trade-off between compactness and
usefulness.

Storing this information helps commands to display a more complete and
understandable evolve history. For example, obslog (an Evolve command) use it
to improve its output:

x  62206adfd571 (34302) obscache: skip updating outdated obscache...
|    rewritten(parent) by Matthieu Laneuville <matthieu.laneuville@octobus...
|    rewritten(content) by Boris Feld <boris.feld@octobus.net>

The effect flag is stored in obs-markers metadata while we iterate on the
information we want to store. We plan to extend the existing obsmarkers
bit-field when the effect flag design will be stabilized.

It's different from the CommitCustody concept, effect-flag are not signed and
can be forged. It's also different from the operation metadata as the command
name (for example: amend) could alter a changeset in different ways (changing
the content with hg amend, changing the description with hg amend -e, changing
the user with hg amend -U). Also it's compatible with every custom command
that writes obs-markers without needing to be updated.

The effect-flag is placed behind an experimental flag set to off by default.

Hook the saving of effect flag in create markers, but store only an empty one
for the moment, I will refine the values in effect flag in following patches.

For more information, see:

  https://www.mercurial-scm.org/wiki/ChangesetEvolutionDevel#Record_types_of_operation

Differential Revision: https://phab.mercurial-scm.org/D533
2017-07-06 14:50:17 +02:00
Simon Whitaker
194133f6f9 obsmarker: crash more helpfully when metadata fields are >255bytes (issue5681)
Various mutators fail when attempting to write obsmarkers with
metadata fields longer than 255 bytes, since the length of
mwetadata fields is stored in u8s. This change raises a more
helpful error in such circumstances.

Differential Revision: https://phab.mercurial-scm.org/D865
2017-10-01 12:54:35 +01:00
Boris Feld
980fc3e8a0 obsolete: clean createmarkers part about operation
I will add another experiment in createmarkers, add a comment and some blank
lines for aesthetic sake.

Differential Revision: https://phab.mercurial-scm.org/D532
2017-07-06 14:46:07 +02:00
Augie Fackler
158a3fa960 obsolete: remove superfluous pass statements 2017-09-30 07:44:55 -04:00
Boris Feld
971a5a6f59 obsolete: rename bumped volatile set into phasedivergent volatile set
The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D255
2017-08-04 19:39:34 +02:00
Boris Feld
ac541e6eb8 obsolete: rename divergent volatile set into contentdivergent volatile set
The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D254
2017-08-04 19:36:27 +02:00
Boris Feld
98e598e611 obsolete: rename unstable volatile set into orphan volatile set
The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D253
2017-08-04 19:27:39 +02:00
Boris Feld
7f8b0aa44f config: rename evolution config into stabilization
Use aliases for backward-compatibility. Though I'm not sure how to emit
compatibility warnings with aliases.

Test configuration are updated in the next patch.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D248
2017-08-03 11:38:22 +02:00
Boris Feld
d88d8d1c9e obsutil: rename allprecursors into allpredecessors
Use util.nouideprecwarn because obsstore doesn't have easy access to an ui
object.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D247
2017-08-02 19:49:57 +02:00
Boris Feld
aecab865d6 obsolete: rename precursor into predecessor in obsolete docstrings
The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D246
2017-08-02 19:48:06 +02:00
Boris Feld
edffdda1d5 obsstore: rename precursors into predecessors
Use util.nouideprecwarn because obsstore doesn't have easy access to an ui
object.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D245
2017-08-02 19:39:08 +02:00
Boris Feld
284208b1ed obsolete: rename _addprecursors into _addpredecessors
Use util.nouideprecwarn because _addpredecessors doesn't have easy access to
an ui object.

The renaming is done according to
https://www.mercurial-scm.org/wiki/CEDVocabulary.

Differential Revision: https://phab.mercurial-scm.org/D244
2017-08-02 19:34:15 +02:00
Augie Fackler
186f7e37bf obsolete: use bytes() instead of str() so the node is bytes on py3
I'm not sure this is right, since this should either be bytes or str
to match what's going on in the revlog layer.

Differential Revision: https://phab.mercurial-scm.org/D271
2017-07-24 10:37:39 -04:00
Augie Fackler
02f4d7b6be obsolete: reuse _fm1metapair for computing _fm1metapairsize
It's evaluated at import time, so it seems silly to not reuse the
constant name.
2017-07-24 10:21:23 -04:00
Jun Wu
449129999d obsstore: let read marker API take a range of offsets
This allows us to read a customized range of markers, instead of loading all
of them.

The condition of stop is made consistent across C and Python implementation
so we will still read marker when offset=a, stop=a+1.
2017-06-04 10:02:09 -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
Jun Wu
888ed86eaf obsstore: keep self._data updated with _addmarkers
This makes sure obsstore._data is still correct with added markers.

The '_data' propertycache was added in 17ce57b7873f.
2017-06-03 21:56:23 -07:00
Boris Feld
8ccdc6d370 obsolete: pass cache argument of successors set explicitly
We plan to add a new argument to successorsets. But first we need to update
all callers to pass cache argument explicitly to avoid arguments confusion.
2017-06-30 15:02:19 +02:00
Pierre-Yves David
8093995fed transaction: track new obsmarkers in the 'changes' mapping
The obsstore collaborate with transaction to make sure we track all the
obsmarkers added during a transaction. This will be useful for various usages:
hooks, caches, better output, etc.

This is the seconds kind of data added to tr.changes (first one was added revisions)
2017-06-27 02:45:09 +02:00
Pierre-Yves David
246ed93984 configitems: register the 'format.obsstore-version' config 2017-06-30 03:42:27 +02:00
Pierre-Yves David
675e8b857f obsutil: move 'getmarkers' 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 02:06:15 +02:00
Pierre-Yves David
cf50b9bfaf obsutil: move the 'marker' class to the new modules
We have a new 'obsutil' module now. We move high level utility there to bring
'obsolete.py' back to a more reasonable size.
2017-06-27 01:51:40 +02:00
Pierre-Yves David
1360d2c71c obsolete: delete three unused utility functions
None of this function has been used in the past 5 years, so I think it is safe
to just kill them. All code accessing rich markers is using 'getmarkers(...)'
instead (or raw markers).
2017-06-27 01:48:41 +02: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
Pierre-Yves David
719387c7c7 obsutil: move 'allsuccessors' 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:36:20 +02:00
Pierre-Yves David
2f0989dab7 obsutil: move 'allprecursors' 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:31:18 +02:00
Pierre-Yves David
8b519349e8 obsutil: move 'exclusivemarkers' 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:11:56 +02:00
Pierre-Yves David
04205af86b obsutil: move 'successorssets' to the new modules
We have a new 'obsutil' module now. We move this high level utility there to bring
'obsolete.py' back to a more reasonable size.
2017-06-27 01:03:01 +02:00
Pierre-Yves David
3e0bfc6abf obsolete: skip 'changectx' usage in unstable computation
We simplify the unstable computation code, skipping the expensive creation of
changectx object. We focus on efficient set operation and revnumber centric
functions.

In my mercurial development repository, this provides a 3x speedup to the
function:

  before: 5.319 ms
  after:  1.844 ms

repo details:

  total changesets:       40886
  obsolete changesets:     7756
  mutable (not obsolete):   293
  unstable:                  30
2017-06-19 01:08:11 +02:00
Pierre-Yves David
be7de168fe obsolete: provide a small function to retrieve all mutable revisions
More obsolescence related algorithm focus on the mutable revision. We provide a
tiny utility function to make it easy to access this set.
2017-06-18 22:38:11 +02:00
Pierre-Yves David
f9505c66f4 obsolete: use ProgrammingError over assert for volatile set registration
We have ProgrammingError now.
2017-05-01 05:57:36 +02:00
Martin von Zweigbergk
46713842b3 pushkey: use False/True for return values from push functions
It was particularly unclear in phases.pushphase() whether the 0/1
returned were the 0/1 for public/draft phase or for False/True
2017-06-12 16:35:57 -07:00
Jun Wu
db8bfa3842 obsstore: do not load all markers to detect duplication
This will make duplication detection something like O(newmarkers) instead of
O(obsstore).
2017-06-02 20:49:42 -07:00
Gregory Szorc
943d55015e obsolete: move obsstore creation logic from localrepo
This code has more to do with obsolete.py than localrepo.py. Let's
move it there.
2017-06-08 21:54:30 -07:00
Jun Wu
7dac9c0bba obsstore: move header encoding to a separate function
This patch moves encodeheader from encodemarkers. So markers and header
could be encoded separately.
2017-06-04 00:38:11 -07:00
Jun Wu
ed047705d9 obsstore: move _version to a propertycache
This makes sure _version is correct even if "_all" is not called.
2017-06-04 08:49:15 -07:00
Jun Wu
3835d8bd52 obsstore: separate marker parsing from obsstore reading
This allows us to get raw obsstore content without parsing any markers.
Reading obsstore is much cheaper than parsing markers.
2017-06-02 20:38:01 -07:00
Jun Wu
4c6d8665c7 obsstore: move marker version reading to a separate function
This allows us to read marker version without reading markers.
2017-06-02 19:34:56 -07:00
Jun Wu
d9a63e5be4 obsstore: minor optimization for the obsolete revset
Use local variables in a loop.
2017-06-02 19:32:27 -07:00
Pierre-Yves David
e928751835 exclusive-markers: update the dedicated test with list of exclusive markers
We now display data about the "exclusive markers" in the test dedicated to
relevant and exclusive markers computation and usage. Each output have been
carefully validated
2017-06-01 08:44:01 +02:00
Pierre-Yves David
66c1fb799c obsolete: add a function to compute "exclusive-markers" for a set of nodes
This set will be used to select the obsmarkers to be stripped alongside the
stripped changesets. See the function docstring for details.

More advanced testing is introduced in the next changesets to keep this one
simpler. That extra testing provides more example.
2017-05-20 15:02:30 +02:00
Pierre-Yves David
0b43826a5f obsolete: raise richer exception on unknown version
We raise a more precise subclass of Abort with details about the faulty
version.  This will be used to detect this case  and display some information
in debugbundle.
2017-05-25 16:50:23 +02:00
Pierre-Yves David
1a819b73cb obsolete: fix relevant-obsmarkers computation on pruned changeset
The markers pruning a node was not directly considered relevant for the pruned
node, only to its parents.

This went unnoticed during obsmarkers exchange because all
ancestors of the pruned node would be included in the computation.
This still affects obsmarkers exchange a bit since "inline" prune markers would
be ignored (see second test case). This went unnoticed, because in such case,
we always push another obsolescence markers for that node.

We add explicit tests covering this case.

(The set of relevant changeset is use in the obsmarkers discovery protocol used
in the evolve experimental extension, the impact will be handled on the
extension side).
2017-05-25 19:37:29 +02:00
Boris Feld
527b4d3011 devel: use default-date config field when creating obsmarkers
Also use the default-date when creating obsmarkers. Currently they are created
with the current date and without any option to force their value.

To test the feature, we remove some of the many 'glob' used to match obsmarker
date in the tests.
2017-05-19 12:08:47 +02:00
Augie Fackler
0e18802e22 merge with stable 2017-05-21 02:45:32 -04:00
Pierre-Yves David
8f0cd8c82a obsolete: invalidate "volatile" set cache after merging marker
Adding markers to the repository might affect the set of obsolete changesets. So we
most remove the "volatile" set who rely in that data. We add two missing
invalidations after merging markers. This was caught by code change in the evolve
extensions tests.

This issues highlight that the current way to do things is a bit fragile,
however we keep things simple for stable.
2017-05-17 15:39:37 +02:00
Yuya Nishihara
4563e16232 parsers: switch to policy importer
# no-check-commit
2016-08-13 12:23:56 +09:00
Pierre-Yves David
1d69629fff obsmarker: add an experimental flag controlling "operation" recording
It seems better to introduce the experiment behind a flag for now as there are
multiple concerns around the feature:

 * Storing operation increase the size of obsolescence markers significantly
   (+10-20%).

 * It performs poorly when exchanging markers (cannot combine command names,
   command name might be unknown remotely, etc)
2017-05-20 03:10:23 +02:00
Pierre-Yves David
457dd23894 obsolete: move the 'isenabled' function at the top of the file
That is a simple and important function so having it at the top next to the
related constant seems better.
2017-05-19 13:12:42 +02:00