5 # This software may be used and distributed according to the terms of the |
5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. |
6 # GNU General Public License version 2 or any later version. |
7 |
7 |
8 from __future__ import absolute_import |
8 from __future__ import absolute_import |
9 |
9 |
10 import struct |
|
11 |
|
12 from .thirdparty.zope import ( |
10 from .thirdparty.zope import ( |
13 interface as zi, |
11 interface as zi, |
14 ) |
12 ) |
15 from . import ( |
13 from . import ( |
16 error, |
|
17 mdiff, |
|
18 repository, |
14 repository, |
19 revlog, |
15 revlog, |
20 ) |
16 ) |
21 |
|
22 def _censoredtext(text): |
|
23 m, offs = revlog.parsemeta(text) |
|
24 return m and "censored" in m |
|
25 |
17 |
26 @zi.implementer(repository.ifilestorage) |
18 @zi.implementer(repository.ifilestorage) |
27 class filelog(revlog.revlog): |
19 class filelog(revlog.revlog): |
28 def __init__(self, opener, path): |
20 def __init__(self, opener, path): |
29 super(filelog, self).__init__(opener, |
21 super(filelog, self).__init__(opener, |
30 "/".join(("data", path + ".i"))) |
22 "/".join(("data", path + ".i")), |
|
23 censorable=True) |
31 # full name of the user visible file, relative to the repository root |
24 # full name of the user visible file, relative to the repository root |
32 self.filename = path |
25 self.filename = path |
33 |
26 |
34 def read(self, node): |
27 def read(self, node): |
35 t = self.revision(node) |
28 t = self.revision(node) |
88 if self.renamed(node): |
81 if self.renamed(node): |
89 t2 = self.read(node) |
82 t2 = self.read(node) |
90 return t2 != text |
83 return t2 != text |
91 |
84 |
92 return True |
85 return True |
93 |
|
94 def checkhash(self, text, node, p1=None, p2=None, rev=None): |
|
95 try: |
|
96 super(filelog, self).checkhash(text, node, p1=p1, p2=p2, rev=rev) |
|
97 except error.RevlogError: |
|
98 if _censoredtext(text): |
|
99 raise error.CensoredNodeError(self.indexfile, node, text) |
|
100 raise |
|
101 |
|
102 def iscensored(self, rev): |
|
103 """Check if a file revision is censored.""" |
|
104 return self.flags(rev) & revlog.REVIDX_ISCENSORED |
|
105 |
|
106 def _peek_iscensored(self, baserev, delta, flush): |
|
107 """Quickly check if a delta produces a censored revision.""" |
|
108 # Fragile heuristic: unless new file meta keys are added alphabetically |
|
109 # preceding "censored", all censored revisions are prefixed by |
|
110 # "\1\ncensored:". A delta producing such a censored revision must be a |
|
111 # full-replacement delta, so we inspect the first and only patch in the |
|
112 # delta for this prefix. |
|
113 hlen = struct.calcsize(">lll") |
|
114 if len(delta) <= hlen: |
|
115 return False |
|
116 |
|
117 oldlen = self.rawsize(baserev) |
|
118 newlen = len(delta) - hlen |
|
119 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen): |
|
120 return False |
|
121 |
|
122 add = "\1\ncensored:" |
|
123 addlen = len(add) |
|
124 return newlen >= addlen and delta[hlen:hlen + addlen] == add |
|