[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