hgext/commitextras.py
author Gregory Szorc <gregory.szorc@gmail.com>
Tue, 18 Sep 2018 15:29:42 -0700
changeset 39764 e4e881572382
parent 39295 3a60416c4fd8
child 41532 bd3f03d8cc9f
permissions -rw-r--r--
localrepo: iteratively derive local repository type This commit implements the dynamic local repository type derivation that was explained in the recent commit bfeab472e3c0 "localrepo: create new function for instantiating a local repo object." Instead of a static localrepository class/type which must be customized after construction, we now dynamically construct a type by building up base classes/types to represent specific repository interfaces. Conceptually, the end state is similar to what was happening when various extensions would monkeypatch the __class__ of newly-constructed repo instances. However, the approach is inverted. Instead of making the instance then customizing it, we do the customization up front by influencing the behavior of the type then we instantiate that custom type. This approach gives us much more flexibility. For example, we can use completely separate classes for implementing different aspects of the repository. For example, we could have one class representing revlog-based file storage and another representing non-revlog based file storage. When then choose which implementation to use based on the presence of repo requirements. A concern with this approach is that it creates a lot more types and complexity and that complexity adds overhead. Yes, it is true that this approach will result in more types being created. Yes, this is more complicated than traditional "instantiate a static type." However, I believe the alternatives to supporting alternate storage backends are just as complicated. (Before I arrived at this solution, I had patches storing factory functions on local repo instances for e.g. constructing a file storage instance. We ended up having a handful of these. And this was logically identical to assigning custom methods. Since we were logically changing the type of the instance, I figured it would be better to just use specialized types instead of introducing levels of abstraction at run-time.) On the performance front, I don't believe that having N base classes has any significant performance overhead compared to just a single base class. Intuition says that Python will need to iterate the base classes to find an attribute. However, CPython caches method lookups: as long as the __class__ or MRO isn't changing, method attribute lookup should be constant time after first access. And non-method attributes are stored in __dict__, of which there is only 1 per object, so the number of base classes for __dict__ is irrelevant. Anyway, this commit splits up the monolithic completelocalrepository interface into sub-interfaces: 1 for file storage and 1 representing everything else. We've taught ``makelocalrepository()`` to call a series of factory functions which will produce types implementing specific interfaces. It then calls type() to create a new type from the built-up list of base types. This commit should be considered a start and not the end state. I suspect we'll hit a number of problems as we start to implement alternate storage backends: * Passing custom arguments to __init__ and setting custom attributes on __dict__. * Customizing the set of interfaces that are needed. e.g. the "readonly" intent could translate to not requesting an interface providing methods related to writing. * More ergonomic way for extensions to insert themselves so their callbacks aren't unconditionally called. * Wanting to modify vfs instances, other arguments passed to __init__. That being said, this code is usable in its current state and I'm convinced future commits will demonstrate the value in this approach. Differential Revision: https://phab.mercurial-scm.org/D4642
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     1
# commitextras.py
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     2
#
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     3
# Copyright 2013 Facebook, Inc.
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     4
#
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     7
33562
3cfabb6cfd51 commitextras: mark the extension as ADVANCED
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33547
diff changeset
     8
'''adds a new flag extras to commit (ADVANCED)'''
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
     9
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    10
from __future__ import absolute_import
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    11
33602
27fbca750b4d commitextras: make sure keys contains ascii letters, numbers, '_' and '-' only
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33562
diff changeset
    12
import re
27fbca750b4d commitextras: make sure keys contains ascii letters, numbers, '_' and '-' only
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33562
diff changeset
    13
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    14
from mercurial.i18n import _
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    15
from mercurial import (
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    16
    commands,
33547
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    17
    error,
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    18
    extensions,
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    19
    registrar,
39294
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    20
    util,
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    21
)
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    22
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    23
cmdtable = {}
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    24
command = registrar.command(cmdtable)
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    25
testedwith = 'ships-with-hg-core'
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    26
33547
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    27
usedinternally = {
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    28
    'amend_source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    29
    'branch',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    30
    'close',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    31
    'histedit_source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    32
    'topic',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    33
    'rebase_source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    34
    'intermediate-source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    35
    '__touch-noise__',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    36
    'source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    37
    'transplant_source',
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    38
}
a6af8560494e commitextras: check the format of the arguments and no internal key is used
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33546
diff changeset
    39
33546
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    40
def extsetup(ui):
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    41
    entry = extensions.wrapcommand(commands.table, 'commit', _commit)
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    42
    options = entry[1]
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    43
    options.append(('', 'extra', [],
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    44
        _('set a changeset\'s extra values'), _("KEY=VALUE")))
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    45
77c0c36654c8 commitextras: move fb extension to core which add extras to a commit
Pulkit Goyal <7895pulkit@gmail.com>
parents:
diff changeset
    46
def _commit(orig, ui, repo, *pats, **opts):
39294
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    47
    if util.safehasattr(repo, 'unfiltered'):
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    48
        repo = repo.unfiltered()
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    49
    class repoextra(repo.__class__):
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    50
        def commit(self, *innerpats, **inneropts):
34975
901a18b03e00 py3: handle keyword arguments in hgext/commitextras.py
Pulkit Goyal <7895pulkit@gmail.com>
parents: 33606
diff changeset
    51
            extras = opts.get(r'extra')
39295
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    52
            for raw in extras:
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    53
                if '=' not in raw:
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    54
                    msg = _("unable to parse '%s', should follow "
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    55
                            "KEY=VALUE format")
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    56
                    raise error.Abort(msg % raw)
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    57
                k, v = raw.split('=', 1)
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    58
                if not k:
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    59
                    msg = _("unable to parse '%s', keys can't be empty")
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    60
                    raise error.Abort(msg % raw)
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    61
                if re.search('[^\w-]', k):
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    62
                    msg = _("keys can only contain ascii letters, digits,"
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    63
                            " '_' and '-'")
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    64
                    raise error.Abort(msg)
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    65
                if k in usedinternally:
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    66
                    msg = _("key '%s' is used internally, can't be set "
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    67
                            "manually")
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    68
                    raise error.Abort(msg % k)
3a60416c4fd8 commitextras: no need to special case extras=[]
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 39294
diff changeset
    69
                inneropts[r'extra'][k] = v
39294
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    70
            return super(repoextra, self).commit(*innerpats, **inneropts)
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    71
    repo.__class__ = repoextra
1cb7c9777852 commitextras: work nicely with other extensions
Valentin Gatien-Baron <vgatien-baron@janestreet.com>
parents: 36419
diff changeset
    72
    return orig(ui, repo, *pats, **opts)