[PATCH 2 of 5 filtering part 2 V2] clfilter: add actual repo filtering mechanism

Pierre-Yves David pierre-yves.david at ens-lyon.org
Thu Dec 13 20:22:48 CST 2012


On 12 déc. 2012, at 00:48, Pierre-Yves David wrote:

> 
> On 10 déc. 2012, at 23:45, Kevin Bullock wrote:
> 
>> On 10 Dec 2012, at 11:30 AM, pierre-yves.david at logilab.fr wrote:
>> 
>>> # HG changeset patch
>>> # User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
>>> # Date 1355157839 -3600
>>> # Node ID 5a3d1c83343b403cf1e1393cdf2dde4f8512309b
>>> # Parent  9280d7beadd6f38c8623ea3137f2028c57cc349c
>>> clfilter: add actual repo filtering mechanism
>>> 
>>> We add a `filtered` method on repo. This method return instance of `repoproxy`
>>                                                      an instance
>>> that behave exactly as the original repository but with a filtered changelog
>>      behaves
>>> attribute. Filter are identified by a "name". Planed filter are `unserved`,
>>            Filters                            Planned filters
>>                                      [^^ " instead of ` suddenly?]
>>> `hidden` and `mutable`.  See the `repoproxier` docstring for details.
>>                                  `repoproxy`
>>> Mechanism to compute filtered revision are also installed. Some cache will be
>> A mechanism                   revisions is
>>> installed in later commit.
>>> 
>>> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
>>> --- a/mercurial/localrepo.py
>>> +++ b/mercurial/localrepo.py
>>> @@ -13,10 +13,11 @@ import scmutil, util, extensions, hook, 
>>> import match as matchmod
>>> import merge as mergemod
>>> import tags as tagsmod
>>> from lock import release
>>> import weakref, errno, os, time, inspect
>>> +import copy
>>> propertycache = util.propertycache
>>> filecache = scmutil.filecache
>>> 
>>> class repofilecache(filecache):
>>>   """All filecache usage on repo are done for logic that should be unfiltered
>>> @@ -55,10 +56,73 @@ def unfilteredmethod(orig):
>>>   """decorate method that always need to be run on unfiltered version"""
>>>   def wrapper(repo, *args, **kwargs):
>>>       return orig(repo.unfiltered(), *args, **kwargs)
>>>   return wrapper
>>> 
>>> +# function to compute filtered set
>>> +computefiltered = {}
>>> +
>>> +def _filteredrevs(repo, filtername):
>>> +    """returns set of filtered revision for this filter name"""
>>> +    return computefiltered[filtername](repo.unfiltered())
>>> +
>>> +class repoproxy(object):
>>> +    """Changelog filterered localrepo proxy object
>>> +
>>> +    This object act is a proxy for attribute operation. setattr is done on the
>>> +    initial repo, getattr is get from the parent (unless defined on proxy) and
>>> +    delattr operate on proxied repo.
>>> +
>>> +    The changelog attribute is overridden to return a copy of the original
>>> +    changelog but with some revision filtered.
>>> +
>>> +    You have to mix this class with the actual localrepo subclass to be able to
>>> +    use the very same logic than the proxied repo. See `filtered` method of
>>> +    local repo for details."""
>> 
>> This method of decorating the repo seems pretty awkward, particularly using a global dict to hold functions. If there are really going to only be a handful of filters, can't we have a handful of actual named mixin classes (derived from repoproxy)?
> 
> After discussion on IRC with Kevin the current proposition:
> 
> - The `computefiltered` dictionary will be moved in the repoproxy class
> - The filtering method will be added to the dictionnary by decoration as in mercurial/obsolete.py
> 
> I'll think about it and send a followup soon.

After long discussion with Kevin Bullock:

- The code is structure is unchanged,
- The class is now called `repoview` to better highlight the fact it manipulate the underlying repo almost directly.
- The new documentation is:

class repoview(object):
    """Provide a read/write view of a repo through a filtered changelog

    This object is used to access a filtered version of a repository without
    altering the original repository object itself. We can not alter the
    original object for two main reasons:
    - It prevents the use of a repo with multiple filters at the same time. In
      particular when multiple threads are involved.
    - It makes scope of the filtering harder to control.

    This object behaves very closely to the original repository. All attribute
    operations are done on the original repository:
    - An access to `repoview.someattr` actually returns `repo.someattr`,
    - A write to `repoview.someattr` actually sets value of `repo.someattr`,
    - A deletion of `repoview.someattr` actually drops `someattr`
      from `repo.__dict__`.

    The only exception is the `changelog` property. It is overridden to return
    a (surface) copy of `repo.changelog` with some revisions filtered. The
    `filtername` attribute of the view control the revisions that need to be
    filtered.  (the fact the changelog is copied is an implementation detail).

    Unlike attributes, this object intercepts all method calls. This means that
    all methods are run on the `repoview` object with the filtered `changelog`
    property. For this purpose the simple `repoview` class must be mixed with
    the actual class of the repository. This ensures that the resulting
    `repoview` object have the very same methods than the repo object. This
    leads to the property below.

        repoview.method() --> repo.__class__.method(repoview)

    The inheritance has to be done dynamically because `repo` can be of any
    subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`.
    """

-- 
Pierre-Yves David




More information about the Mercurial-devel mailing list