Show More
@@ -1,97 +1,108 b'' | |||||
1 | # repoview.py - Filtered view of a localrepo object |
|
1 | # repoview.py - Filtered view of a localrepo object | |
2 | # |
|
2 | # | |
3 | # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
|
3 | # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org> | |
4 | # Logilab SA <contact@logilab.fr> |
|
4 | # Logilab SA <contact@logilab.fr> | |
5 | # |
|
5 | # | |
6 | # This software may be used and distributed according to the terms of the |
|
6 | # This software may be used and distributed according to the terms of the | |
7 | # GNU General Public License version 2 or any later version. |
|
7 | # GNU General Public License version 2 or any later version. | |
8 |
|
8 | |||
9 | import copy |
|
9 | import copy | |
|
10 | import phases | |||
|
11 | ||||
|
12 | def computeunserved(repo): | |||
|
13 | """compute the set of revision that should be filtered when used a server | |||
|
14 | ||||
|
15 | Secret and hidden changeset should not pretend to be here.""" | |||
|
16 | assert not repo.changelog.filteredrevs | |||
|
17 | # fast path in simple case to avoid impact of non optimised code | |||
|
18 | if phases.hassecret(repo) or repo.obsstore: | |||
|
19 | return frozenset(repo.revs('hidden() + secret()')) | |||
|
20 | return () | |||
10 |
|
21 | |||
11 | # function to compute filtered set |
|
22 | # function to compute filtered set | |
12 | filtertable = {} |
|
23 | filtertable = {'unserved': computeunserved} | |
13 |
|
24 | |||
14 | def filteredrevs(repo, filtername): |
|
25 | def filteredrevs(repo, filtername): | |
15 | """returns set of filtered revision for this filter name""" |
|
26 | """returns set of filtered revision for this filter name""" | |
16 | if filtername not in repo.filteredrevcache: |
|
27 | if filtername not in repo.filteredrevcache: | |
17 | func = filtertable[filtername] |
|
28 | func = filtertable[filtername] | |
18 | repo.filteredrevcache[filtername] = func(repo.unfiltered()) |
|
29 | repo.filteredrevcache[filtername] = func(repo.unfiltered()) | |
19 | return repo.filteredrevcache[filtername] |
|
30 | return repo.filteredrevcache[filtername] | |
20 |
|
31 | |||
21 | class repoview(object): |
|
32 | class repoview(object): | |
22 | """Provide a read/write view of a repo through a filtered changelog |
|
33 | """Provide a read/write view of a repo through a filtered changelog | |
23 |
|
34 | |||
24 | This object is used to access a filtered version of a repository without |
|
35 | This object is used to access a filtered version of a repository without | |
25 | altering the original repository object itself. We can not alter the |
|
36 | altering the original repository object itself. We can not alter the | |
26 | original object for two main reasons: |
|
37 | original object for two main reasons: | |
27 | - It prevents the use of a repo with multiple filters at the same time. In |
|
38 | - It prevents the use of a repo with multiple filters at the same time. In | |
28 | particular when multiple threads are involved. |
|
39 | particular when multiple threads are involved. | |
29 | - It makes scope of the filtering harder to control. |
|
40 | - It makes scope of the filtering harder to control. | |
30 |
|
41 | |||
31 | This object behaves very closely to the original repository. All attribute |
|
42 | This object behaves very closely to the original repository. All attribute | |
32 | operations are done on the original repository: |
|
43 | operations are done on the original repository: | |
33 | - An access to `repoview.someattr` actually returns `repo.someattr`, |
|
44 | - An access to `repoview.someattr` actually returns `repo.someattr`, | |
34 | - A write to `repoview.someattr` actually sets value of `repo.someattr`, |
|
45 | - A write to `repoview.someattr` actually sets value of `repo.someattr`, | |
35 | - A deletion of `repoview.someattr` actually drops `someattr` |
|
46 | - A deletion of `repoview.someattr` actually drops `someattr` | |
36 | from `repo.__dict__`. |
|
47 | from `repo.__dict__`. | |
37 |
|
48 | |||
38 | The only exception is the `changelog` property. It is overridden to return |
|
49 | The only exception is the `changelog` property. It is overridden to return | |
39 | a (surface) copy of `repo.changelog` with some revisions filtered. The |
|
50 | a (surface) copy of `repo.changelog` with some revisions filtered. The | |
40 | `filtername` attribute of the view control the revisions that need to be |
|
51 | `filtername` attribute of the view control the revisions that need to be | |
41 | filtered. (the fact the changelog is copied is an implementation detail). |
|
52 | filtered. (the fact the changelog is copied is an implementation detail). | |
42 |
|
53 | |||
43 | Unlike attributes, this object intercepts all method calls. This means that |
|
54 | Unlike attributes, this object intercepts all method calls. This means that | |
44 | all methods are run on the `repoview` object with the filtered `changelog` |
|
55 | all methods are run on the `repoview` object with the filtered `changelog` | |
45 | property. For this purpose the simple `repoview` class must be mixed with |
|
56 | property. For this purpose the simple `repoview` class must be mixed with | |
46 | the actual class of the repository. This ensures that the resulting |
|
57 | the actual class of the repository. This ensures that the resulting | |
47 | `repoview` object have the very same methods than the repo object. This |
|
58 | `repoview` object have the very same methods than the repo object. This | |
48 | leads to the property below. |
|
59 | leads to the property below. | |
49 |
|
60 | |||
50 | repoview.method() --> repo.__class__.method(repoview) |
|
61 | repoview.method() --> repo.__class__.method(repoview) | |
51 |
|
62 | |||
52 | The inheritance has to be done dynamically because `repo` can be of any |
|
63 | The inheritance has to be done dynamically because `repo` can be of any | |
53 | subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`. |
|
64 | subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`. | |
54 | """ |
|
65 | """ | |
55 |
|
66 | |||
56 | def __init__(self, repo, filtername): |
|
67 | def __init__(self, repo, filtername): | |
57 | object.__setattr__(self, '_unfilteredrepo', repo) |
|
68 | object.__setattr__(self, '_unfilteredrepo', repo) | |
58 | object.__setattr__(self, 'filtername', filtername) |
|
69 | object.__setattr__(self, 'filtername', filtername) | |
59 |
|
70 | |||
60 | # not a cacheproperty on purpose we shall implement a proper cache later |
|
71 | # not a cacheproperty on purpose we shall implement a proper cache later | |
61 | @property |
|
72 | @property | |
62 | def changelog(self): |
|
73 | def changelog(self): | |
63 | """return a filtered version of the changeset |
|
74 | """return a filtered version of the changeset | |
64 |
|
75 | |||
65 | this changelog must not be used for writing""" |
|
76 | this changelog must not be used for writing""" | |
66 | # some cache may be implemented later |
|
77 | # some cache may be implemented later | |
67 | cl = copy.copy(self._unfilteredrepo.changelog) |
|
78 | cl = copy.copy(self._unfilteredrepo.changelog) | |
68 | cl.filteredrevs = filteredrevs(self._unfilteredrepo, self.filtername) |
|
79 | cl.filteredrevs = filteredrevs(self._unfilteredrepo, self.filtername) | |
69 | return cl |
|
80 | return cl | |
70 |
|
81 | |||
71 | def unfiltered(self): |
|
82 | def unfiltered(self): | |
72 | """Return an unfiltered version of a repo""" |
|
83 | """Return an unfiltered version of a repo""" | |
73 | return self._unfilteredrepo |
|
84 | return self._unfilteredrepo | |
74 |
|
85 | |||
75 | def filtered(self, name): |
|
86 | def filtered(self, name): | |
76 | """Return a filtered version of a repository""" |
|
87 | """Return a filtered version of a repository""" | |
77 | if name == self.filtername: |
|
88 | if name == self.filtername: | |
78 | return self |
|
89 | return self | |
79 | return self.unfiltered().filtered(name) |
|
90 | return self.unfiltered().filtered(name) | |
80 |
|
91 | |||
81 | # everything access are forwarded to the proxied repo |
|
92 | # everything access are forwarded to the proxied repo | |
82 | def __getattr__(self, attr): |
|
93 | def __getattr__(self, attr): | |
83 | return getattr(self._unfilteredrepo, attr) |
|
94 | return getattr(self._unfilteredrepo, attr) | |
84 |
|
95 | |||
85 | def __setattr__(self, attr, value): |
|
96 | def __setattr__(self, attr, value): | |
86 | return setattr(self._unfilteredrepo, attr, value) |
|
97 | return setattr(self._unfilteredrepo, attr, value) | |
87 |
|
98 | |||
88 | def __delattr__(self, attr): |
|
99 | def __delattr__(self, attr): | |
89 | return delattr(self._unfilteredrepo, attr) |
|
100 | return delattr(self._unfilteredrepo, attr) | |
90 |
|
101 | |||
91 | # The `requirement` attribut is initialiazed during __init__. But |
|
102 | # The `requirement` attribut is initialiazed during __init__. But | |
92 | # __getattr__ won't be called as it also exists on the class. We need |
|
103 | # __getattr__ won't be called as it also exists on the class. We need | |
93 | # explicit forwarding to main repo here |
|
104 | # explicit forwarding to main repo here | |
94 | @property |
|
105 | @property | |
95 | def requirements(self): |
|
106 | def requirements(self): | |
96 | return self._unfilteredrepo.requirements |
|
107 | return self._unfilteredrepo.requirements | |
97 |
|
108 |
General Comments 0
You need to be logged in to leave comments.
Login now