Commit Graph

276 Commits

Author SHA1 Message Date
Augie Fackler
6a68930f82 bundle2: convert ints to strings using pycompat.bytestring()
Fixes some Python 3 regressions.

We don't use %d here because the part id is actually an
Optional[int]. It should always be initialized to a non-None value by
the time this code executes, but we shouldn't blindly depend on that
being the case.

Differential Revision: https://phab.mercurial-scm.org/D272
2017-07-24 11:16:32 -04:00
Pulkit Goyal
89fd642a01 pushvars: move fb extension pushvars to core
pushvars extension in fbext adds a --pushvars flag to push command using which
one send strings to server which becomes environment variables there prepended
with HG_USERVAR_. These variables can then be used to run hooks on the server.
The extension is moved directly to core and unbundling of the strings and
converting them to environment variables at server is disabled by default for
security reasons. One can turn that on by following config:

[push]
pushvars.server = true

This patch also adds the test for the extension.

Differential Revision: https://phab.mercurial-scm.org/D210
2017-07-31 09:59:42 +05:30
Yuya Nishihara
fb236e4381 py3: convert arbitrary exception object to byte string more reliably
Our exception types implement __bytes__(), which should be tried first. Do
lossy encoding conversion as a last resort.
2017-08-03 23:02:32 +09:00
Augie Fackler
65bd64ce26 bundle2: obtain repr() of exception in a python3-safe way
This was exposed by other problems in bundle generation, but I'm not
sure how to test it for now.
2017-07-24 11:19:11 -04:00
Augie Fackler
3038df5570 bundle2: use bytestr() instead of str() to convert part id to bytes
This was exposed by trying to run previously-passing Python 3 tests.
2017-07-24 11:28:40 -04:00
Augie Fackler
5d1326116a bundle2: work around zip() being lazy in Python 3 2017-07-24 11:20:08 -04:00
Augie Fackler
070535e700 bundle2: look for __next__ as well as next to identify iterators
In Python 3, next is called __next__ and this was failing to catch
some iterators.
2017-07-24 11:19:45 -04:00
Augie Fackler
6efeab9bb5 bundle2: use modern Python division
This was failing on Python 3 because the / was returning a float,
which was then making the __mul__ on a bytes sad.
2017-07-24 11:17:36 -04:00
Pulkit Goyal
f19a5db742 bundle2: load hookargs from bundleoperation into transaction when started one
When a transaction is started, we must load the hookargs from the
bundleoperation object to the transaction so that they can be used in the
transaction. Also this patch makes sure no more hookargs are added to the
bundleoperation object once the transaction starts.

This is a part of porting fb extension bundle2hooks to core.

Differential Revision: https://phab.mercurial-scm.org/D209
2017-08-02 03:23:06 +05:30
Pulkit Goyal
5534a45217 bundle2: add the capability to store hookargs on bundle operation object
There are extensions like pushrebase, pushvars which run hooks on a server
before taking the lock. Since the lock is not taken, transaction is not there,
so the hookargs can't be stored on the transaction. Adding hooksargs to bundle
operation object will help in running hooks before taking the lock.

This is a part of moving fb's extension bundle2hooks to core.

Differential Revision: https://phab.mercurial-scm.org/D208
2017-08-02 03:08:42 +05:30
Boris Feld
68ddce738e transaction-summary: display the summary for all transactions
Now that we records "all" changes happening in a transaction (in tr.changes)
we will be able to provide better report on various changes (phases turned
public, changeset obsoleted, branch merged or created, etc..)

This is far too late in the cycle to play with this, but having this existing
method called more widely will help extensions to play around with various
options during the 4.4 cycle.

Instead of calling registersummarycallback only for transactions we want, we
always call it and use the transaction name to decide when to report (eg: we
do not want `hg amend` to report new obsoleted changesets). Filtering on
transaction name does not seems great, but seems good enough for the moment.
We can change the API during the next cycle.

The previous manual call during unbundling of the bundle2 "obsmarkers" part is
no longer necessary and has been dropped.
2017-07-16 02:20:06 +02: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
Boris Feld
0949aa4c6f changegroup: stop returning and recording added nodes in 'cg.apply'
cg.apply used to returns the added nodes. Callers doesn't have a use for it
anymore, remove the added node and stops recording it in the current
operation.

This information was added in the current release cycle so no extensions
breakage should happens.
2017-07-13 21:08:06 +02:00
Boris Feld
f2e89981fb phases: remove trace of addednodes in the 'phase-heads' handling
updatephases have no use of the 'addednodes' parameter since 44be3dc1fec8.
However caller are still passing it for nothing, remove the parameter and
remove computing of the added nodes in caller.
2017-07-13 21:10:55 +02:00
Boris Feld
20ea8cb94b bundle2: automatically add 'targetphase' parameter in writenewbundle
If we are bundling secret changeset and the bundle will contain phase, we
request the changegroup to be applied as secret.

It will be useful for next patch as we are now sure that secrets changesets
are applied as secret and not applied as draft then forced to secret.
2017-07-11 05:12:03 +02:00
Boris Feld
438579760f bundle2: support the 'targetphase' parameter for the changegroup part
By default unbundled changesets are drafts. We want to reduce the number of
phases changes during unbundling by giving the possibility to the bundle to
indicate the phase of unbundled changesets.

The longer terms goal is to add phase movement tracking in tr.changes and the
'retractboundary' call is making it more complicated than we want.
2017-07-11 05:11:52 +02:00
Pierre-Yves David
5e97e55c0f obsolete: reports the number of local changeset obsoleted when unbundling
This is a first basic visible usage of the changes tracking in the transaction.
We adds a new function computing the pre-existing changesets obsoleted by a
transaction and a transaction call back displaying this information.

Example output:

  added 1 changesets with 1 changes to 1 files (+1 heads)
  3 new obsolescence markers
  obsoleted 1 changesets

The goal is to evolve the transaction summary into something bigger, gathering
existing output there and adding new useful one. This patch is a good first step
on this road. The new output is basic but give a user to the content of
tr.changes['obsmarkers'] and give an idea of the new options we haves. I expect
to revisit the message soon.

The caller recording the transaction summary should also be moved into a more
generic location but further refactoring is needed before it can happen.
2017-06-28 03:54:19 +02:00
Pierre-Yves David
8e3b51a1d6 configitems: register the 'server.concurrent-push-mode' config 2017-06-30 03:44:09 +02:00
Pierre-Yves David
7d6eacd5de config: register the 'devel.bundle2.debug' config
Existing explicit default are dropped now that the default is centralised.
2017-06-28 13:25:33 +02:00
Siddharth Agarwal
218f4e2803 bundle2: add debug info about the number of stream params
Seems like the %i was never substituted.
2017-06-27 14:38:00 -07:00
Siddharth Agarwal
1920cf0324 bundle2: add some debugging information to the not-a-bundle error
I found this useful while trying to debug wireproto-related issues.
2017-06-27 14:30:55 -07:00
Martin von Zweigbergk
51c1cdcd1e bundle: inline applybundle1()
We have now gotten rid of all but one caller, so let's inline it
there.
2017-06-22 21:45:32 -07:00
Martin von Zweigbergk
2625447afc bundle: make applybundle() delegate v1 bundles to applybundle1() 2017-06-22 15:00:19 -07:00
Martin von Zweigbergk
c4273872d0 bundle: make applybundle1() return a bundleoperation
See previous commit for motivation. It already lets us share a little
bit more code in commands.py.
2017-06-21 21:08:48 -07:00
Martin von Zweigbergk
eae1a1d9e5 bundle: add a applybundle1() method
This is one step towards removing a bunch of "if isinstance(gen,
unbundle20)" by treating bundle1 and bundle2 more similarly.

The name may sounds ironic for a method in the bundle2 module, but I
didn't think it was worth it yet to create a new 'bundle' module that
depends on the 'bundle2' module. Besides, we'll inline the method
again later.
2017-06-16 10:25:11 -07:00
Martin von Zweigbergk
efc6bd27e4 bundle: extract _processchangegroup() method
The new method applies the changegroup and fills in op.records,
sharing a little bit of code between the two callers. We'll add
another caller soon.
2017-06-22 15:59:07 -07:00
Martin von Zweigbergk
cd9d73f57a bundle: make combinechangegroupresults() take a bundleoperation
Both callers have a bundleoperation. Passing it in lets us share a bit
more code.
2017-06-22 14:04:13 -07:00
Martin von Zweigbergk
80bc6d2e40 bundle: move combineresults() from changegroup to bundle2
The results only need to be combined if they come from a bundle2. More
importantly, we'll change its argument to a bundleoperation soon, and
then it definitely will no longer belong in changegroup.py.
2017-06-22 13:58:20 -07:00
Martin von Zweigbergk
42aa12b688 bundle: remove 'op' argument from applybundle()
No callers pass in an operation.
2017-06-21 14:42:04 -07:00
Martin von Zweigbergk
6fab52669c bundle: add config option to include phases
This adds an experimental.bundle-phases config option to include phase
information in bundles. As with the recently added support for
bundling obsmarkers, the support for bundling phases is hidden behind
the config option until we decide to make a bundlespec v3 that
includes phases (and obsmarkers and ...).

We could perhaps use the listkeys format for this, but that's
considered obsolete according to Pierre-Yves. Instead, we introduce a
new "phase-heads" bundle part. The new part contains the phase heads
among the set of bundled revisions. It does not include those in
secret phase; any head in the bundle that is not mentioned in the
phase-heads part is assumed to be secret. As a special case, an empty
phase-heads part thus means that any changesets should be added in
secret phase. (If we ever add a fourth phase, we'll include secret in
the part and we'll add a version number.)

For now, phases are only included by "hg bundle", and not by
e.g. strip and rebase.
2017-06-22 10:10:02 -07:00
Martin von Zweigbergk
3015c0a9a8 bundle2: record changegroup data in 'op.records' (API)
When adding support for bundling and unbundling phases, it will be
useful to have the list of added changesets. To do that, we return the
list from changegroup.apply().
2017-06-16 16:56:16 -07:00
Martin von Zweigbergk
18bcf3de5c bundle2: don't use debug message "no-transaction" with transaction 2017-06-20 16:33:13 -07:00
Martin von Zweigbergk
560e5ce4f1 changegroup: let callers pass in transaction to apply() (API)
I think passing in the transaction makes it a little clearer and more
consistent with bundle2.
2017-06-15 22:46:38 -07:00
Pierre-Yves David
92f804bd36 check-concurrency: expose the feature as 'concurrent-push-mode'
We move the feature to a proper configuration and document it. The config goes
in the 'server' section because it feels like something the server owner would
want to decide. We pick and open field because it seems likely that other
checking levels will emerge in the future. (eg: server like the mozilla-try
server will likely wants a "none" value)

The option name contains 'push' since this affects 'push' only. The option value
'check-related' is preferred over one explicitly containing 'allow' or 'deny'
because the client still have a strong decision power here. Here, the server is
just advising the client on the check mode to use.
2017-05-28 00:12:38 +02:00
Pierre-Yves David
a74c131fb4 push: add a way to allow concurrent pushes on unrelated heads
Client has a mechanism for the server to check that nothing changed server side
since the client prepared a push. That check is wide and any head changed on
the server will lead to an aborted push. We introduce a way for the client to
send a less strict checking. That logic will check that no heads impacted by
the push have been affected. If other unrelated heads (including named branches
heads) have been affected, the push will proceed.

This is very helpful for repositories with high developers traffic on different
heads, a common setup.

That behavior is currently controlled by an experimental option. The config
should live in the "server" section but bike-shedding of the name will happen
in the next changesets. Servers advertise this capability through a new bundle2
capability 'checkeads', using the value 'related'.

The 'test-push-race.t' is updated to check that new capabilities on the
documented cases.
2017-05-29 05:53:58 +02:00
Pierre-Yves David
8873a232c1 bundle: add an experimental knob to include obsmarkers in bundle
The "hg bundle" command is a good place to test if the inclusion of obsmarkers
within a bundle is working well (part exists, content is correct etc). So we
add a way to have them included.

Ideally, this would be controlled by a change around bundlespec (bundlespec
"v3" + arguments). However, my main goal is to have obsmarkers included in
bundle created by the 'hg strip' command, not the 'hg bundle' so for now I'm
avoiding the detour through bundlespec rework territory.

Better debug output for obsmarkers in 'debugbundle' will be added in later
changesets. The 'test-obsolete-bundle-strip.t' test will also get updated in a
later changeset to keep the current changeset smaller.
2017-05-28 11:50:43 -07:00
Pierre-Yves David
ffd8983573 bundle2: move function building obsmarker-part in the bundle2 module
We move it next to similar part building functions. We will need it for the
"writenewbundle" logic. This will allow us to easily include obsmarkers in
on-disk bundle, a necessary step before having `hg strip` also operate on
markers.

(Yes, the bundle2 module was already too large, but there any many
interdependencies between its components so it is non-trivial to split, this is
a quest for another adventure.)
2017-05-28 11:48:18 -07: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
Gregory Szorc
79225ac4bc bundle2: ignore errors seeking a bundle after an exception (issue4784)
Many have seen a "stream ended unexpectedly" error. This message is
raised from changegroup.readexactly() when a read(n) operation fails
to return exactly N bytes.

I believe most occurrences of this error in the wild stem from
the code changed in this patch. Before, if bundle2's part applicator
raised an Exception when processing/applying parts, the exception
handler would attempt to iterate the remaining parts. If I/O
during this iteration failed, it would likely raise the
"stream ended unexpectedly" exception.

The problem with this approach is that if we already encountered
an I/O error iterating the bundle2 data during application, then
any further I/O would almost certainly fail. If the original stream
were closed, changegroup.readexactly() would obtain an empty
string, triggering "stream ended unexpectedly" with "got 0." This is
the error message that users would see. What's worse is that the
original I/O related exception would be lost since a new exception
would be raised. This made debugging the actual I/O failure
effectively impossible.

This patch changes the exception handler for bundle2 application to
ignore errors when seeking the underlying stream. When the underlying
error is I/O related, the seek should fail fast and the original
exception will be re-raised. The output changes in
test-http-bad-server.t demonstrate this.

When the underlying error is not I/O related and the stream can be
seeked, the old behavior is preserved.
2017-04-16 11:55:08 -07:00
Pierre-Yves David
a8ff8b5088 bundle2: move 'seek' and 'tell' methods off the unpackermixin class
These methods are unrelated to unpacking. They are used internally by the
'unbundlepart' class only. So me move them there as private methods.

In the same go, we clarify their internal role in the their docstring.
2017-04-09 19:09:07 +02:00
Pierre-Yves David
c1bb70eed7 bundle2: move the 'close' method off the unpackermixin
This method is unrelated to unpacking and only used by the unbundle20 class.
We move it there for clarity.
2017-04-09 19:08:24 +02:00
Pierre-Yves David
72eb1f753d bundle2: clarify the docstring of unpackermixin methods
The unpackermixin is a utility used to implement the bundle2 protocol. It should
not be used when writing part handlers. We update the docstring to clarify this.
2017-04-09 18:12:54 +02:00
Pierre-Yves David
c1568c1200 bundle2: add documention to 'part.addparams'
There are some non-obvious limitations on the parameters of this method.
Add some documentation where people will likely look to understand how
to use this API.
2017-04-09 18:16:09 +02:00
Jun Wu
b99bc99ff6 bundle2: use ProgrammingError 2017-03-26 16:57:25 -07:00
Pierre-Yves David
68b43a6db4 bundle: add optional 'tagsfnodecache' data to on disk bundle (issue5543)
This should help performance when unbundling.
2017-05-05 17:31:15 +02:00
Pierre-Yves David
a8c6292f9c bundle2: move tagsfnodecache generation in a generic function
This will help us reusing the logic for `hg bundle`.
2017-05-05 17:28:52 +02:00
Pierre-Yves David
6777304ca7 bundle: introduce an higher level function to write bundle on disk
The current function ('writebundle') is focussing on getting an existing
changegroup to disk. It is no easy ways to includes more part in the generated
bundle2. So we introduce a slightly higher level function that is fed the
'outgoing' object (that defines the bundled spec) and the bundlespec parameters
(to control the changegroup generation and inclusion of other parts).

This is creating the third logic dedicated to create a consistent bundle2 (the
other 2 are the push code and the getbundle code). We should probably reconcile
them at some points but they all takes different types of input. So we need to
introduce an intermediate "object" that each different input could be converted
to. Such unified "bundle2 specification" could be fed to some unified code.

We start by having the `hg bundle` related code on its own to helps defines its
specific needs first. Once the common and specific parts of each logic will be
known we can start unification.
2017-05-05 17:09:47 +02:00
Yuya Nishihara
ffd3ffbc1a pycompat: extract helper to raise exception with traceback
It uses "raise excobj, None, tb" form which I think is simpler and more
useful than "raise exctype, args, tb".
2017-04-20 22:16:12 +09:00
Gregory Szorc
5ca0f908bf py3: add __bool__ to every class defining __nonzero__
__nonzero__ was renamed to __bool__ in Python 3. This patch simply
aliases __bool__ to __nonzero__ for every class implementing
__nonzero__.
2017-03-13 12:40:14 -07:00