##// END OF EJS Templates
censor: fix [hg update] away from a revision with censored files...
Arseniy Alekseyev -
r50066:77b5a190 stable
parent child Browse files
Show More
@@ -1,294 +1,294 b''
1 # filelog.py - file history class for mercurial
1 # filelog.py - file history class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 #
4 #
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 from .i18n import _
10 from .i18n import _
11 from .node import nullrev
11 from .node import nullrev
12 from . import (
12 from . import (
13 error,
13 error,
14 revlog,
14 revlog,
15 )
15 )
16 from .interfaces import (
16 from .interfaces import (
17 repository,
17 repository,
18 util as interfaceutil,
18 util as interfaceutil,
19 )
19 )
20 from .utils import storageutil
20 from .utils import storageutil
21 from .revlogutils import (
21 from .revlogutils import (
22 constants as revlog_constants,
22 constants as revlog_constants,
23 rewrite,
23 rewrite,
24 )
24 )
25
25
26
26
27 @interfaceutil.implementer(repository.ifilestorage)
27 @interfaceutil.implementer(repository.ifilestorage)
28 class filelog(object):
28 class filelog(object):
29 def __init__(self, opener, path):
29 def __init__(self, opener, path):
30 self._revlog = revlog.revlog(
30 self._revlog = revlog.revlog(
31 opener,
31 opener,
32 # XXX should use the unencoded path
32 # XXX should use the unencoded path
33 target=(revlog_constants.KIND_FILELOG, path),
33 target=(revlog_constants.KIND_FILELOG, path),
34 radix=b'/'.join((b'data', path)),
34 radix=b'/'.join((b'data', path)),
35 censorable=True,
35 censorable=True,
36 )
36 )
37 # Full name of the user visible file, relative to the repository root.
37 # Full name of the user visible file, relative to the repository root.
38 # Used by LFS.
38 # Used by LFS.
39 self._revlog.filename = path
39 self._revlog.filename = path
40 self.nullid = self._revlog.nullid
40 self.nullid = self._revlog.nullid
41 opts = opener.options
41 opts = opener.options
42 self._fix_issue6528 = opts.get(b'issue6528.fix-incoming', True)
42 self._fix_issue6528 = opts.get(b'issue6528.fix-incoming', True)
43
43
44 def __len__(self):
44 def __len__(self):
45 return len(self._revlog)
45 return len(self._revlog)
46
46
47 def __iter__(self):
47 def __iter__(self):
48 return self._revlog.__iter__()
48 return self._revlog.__iter__()
49
49
50 def hasnode(self, node):
50 def hasnode(self, node):
51 if node in (self.nullid, nullrev):
51 if node in (self.nullid, nullrev):
52 return False
52 return False
53
53
54 try:
54 try:
55 self._revlog.rev(node)
55 self._revlog.rev(node)
56 return True
56 return True
57 except (TypeError, ValueError, IndexError, error.LookupError):
57 except (TypeError, ValueError, IndexError, error.LookupError):
58 return False
58 return False
59
59
60 def revs(self, start=0, stop=None):
60 def revs(self, start=0, stop=None):
61 return self._revlog.revs(start=start, stop=stop)
61 return self._revlog.revs(start=start, stop=stop)
62
62
63 def parents(self, node):
63 def parents(self, node):
64 return self._revlog.parents(node)
64 return self._revlog.parents(node)
65
65
66 def parentrevs(self, rev):
66 def parentrevs(self, rev):
67 return self._revlog.parentrevs(rev)
67 return self._revlog.parentrevs(rev)
68
68
69 def rev(self, node):
69 def rev(self, node):
70 return self._revlog.rev(node)
70 return self._revlog.rev(node)
71
71
72 def node(self, rev):
72 def node(self, rev):
73 return self._revlog.node(rev)
73 return self._revlog.node(rev)
74
74
75 def lookup(self, node):
75 def lookup(self, node):
76 return storageutil.fileidlookup(
76 return storageutil.fileidlookup(
77 self._revlog, node, self._revlog.display_id
77 self._revlog, node, self._revlog.display_id
78 )
78 )
79
79
80 def linkrev(self, rev):
80 def linkrev(self, rev):
81 return self._revlog.linkrev(rev)
81 return self._revlog.linkrev(rev)
82
82
83 def commonancestorsheads(self, node1, node2):
83 def commonancestorsheads(self, node1, node2):
84 return self._revlog.commonancestorsheads(node1, node2)
84 return self._revlog.commonancestorsheads(node1, node2)
85
85
86 # Used by dagop.blockdescendants().
86 # Used by dagop.blockdescendants().
87 def descendants(self, revs):
87 def descendants(self, revs):
88 return self._revlog.descendants(revs)
88 return self._revlog.descendants(revs)
89
89
90 def heads(self, start=None, stop=None):
90 def heads(self, start=None, stop=None):
91 return self._revlog.heads(start, stop)
91 return self._revlog.heads(start, stop)
92
92
93 # Used by hgweb, children extension.
93 # Used by hgweb, children extension.
94 def children(self, node):
94 def children(self, node):
95 return self._revlog.children(node)
95 return self._revlog.children(node)
96
96
97 def iscensored(self, rev):
97 def iscensored(self, rev):
98 return self._revlog.iscensored(rev)
98 return self._revlog.iscensored(rev)
99
99
100 def revision(self, node, _df=None):
100 def revision(self, node, _df=None):
101 return self._revlog.revision(node, _df=_df)
101 return self._revlog.revision(node, _df=_df)
102
102
103 def rawdata(self, node, _df=None):
103 def rawdata(self, node, _df=None):
104 return self._revlog.rawdata(node, _df=_df)
104 return self._revlog.rawdata(node, _df=_df)
105
105
106 def emitrevisions(
106 def emitrevisions(
107 self,
107 self,
108 nodes,
108 nodes,
109 nodesorder=None,
109 nodesorder=None,
110 revisiondata=False,
110 revisiondata=False,
111 assumehaveparentrevisions=False,
111 assumehaveparentrevisions=False,
112 deltamode=repository.CG_DELTAMODE_STD,
112 deltamode=repository.CG_DELTAMODE_STD,
113 sidedata_helpers=None,
113 sidedata_helpers=None,
114 ):
114 ):
115 return self._revlog.emitrevisions(
115 return self._revlog.emitrevisions(
116 nodes,
116 nodes,
117 nodesorder=nodesorder,
117 nodesorder=nodesorder,
118 revisiondata=revisiondata,
118 revisiondata=revisiondata,
119 assumehaveparentrevisions=assumehaveparentrevisions,
119 assumehaveparentrevisions=assumehaveparentrevisions,
120 deltamode=deltamode,
120 deltamode=deltamode,
121 sidedata_helpers=sidedata_helpers,
121 sidedata_helpers=sidedata_helpers,
122 )
122 )
123
123
124 def addrevision(
124 def addrevision(
125 self,
125 self,
126 revisiondata,
126 revisiondata,
127 transaction,
127 transaction,
128 linkrev,
128 linkrev,
129 p1,
129 p1,
130 p2,
130 p2,
131 node=None,
131 node=None,
132 flags=revlog.REVIDX_DEFAULT_FLAGS,
132 flags=revlog.REVIDX_DEFAULT_FLAGS,
133 cachedelta=None,
133 cachedelta=None,
134 ):
134 ):
135 return self._revlog.addrevision(
135 return self._revlog.addrevision(
136 revisiondata,
136 revisiondata,
137 transaction,
137 transaction,
138 linkrev,
138 linkrev,
139 p1,
139 p1,
140 p2,
140 p2,
141 node=node,
141 node=node,
142 flags=flags,
142 flags=flags,
143 cachedelta=cachedelta,
143 cachedelta=cachedelta,
144 )
144 )
145
145
146 def addgroup(
146 def addgroup(
147 self,
147 self,
148 deltas,
148 deltas,
149 linkmapper,
149 linkmapper,
150 transaction,
150 transaction,
151 addrevisioncb=None,
151 addrevisioncb=None,
152 duplicaterevisioncb=None,
152 duplicaterevisioncb=None,
153 maybemissingparents=False,
153 maybemissingparents=False,
154 ):
154 ):
155 if maybemissingparents:
155 if maybemissingparents:
156 raise error.Abort(
156 raise error.Abort(
157 _(
157 _(
158 b'revlog storage does not support missing '
158 b'revlog storage does not support missing '
159 b'parents write mode'
159 b'parents write mode'
160 )
160 )
161 )
161 )
162
162
163 with self._revlog._writing(transaction):
163 with self._revlog._writing(transaction):
164
164
165 if self._fix_issue6528:
165 if self._fix_issue6528:
166 deltas = rewrite.filter_delta_issue6528(self._revlog, deltas)
166 deltas = rewrite.filter_delta_issue6528(self._revlog, deltas)
167
167
168 return self._revlog.addgroup(
168 return self._revlog.addgroup(
169 deltas,
169 deltas,
170 linkmapper,
170 linkmapper,
171 transaction,
171 transaction,
172 addrevisioncb=addrevisioncb,
172 addrevisioncb=addrevisioncb,
173 duplicaterevisioncb=duplicaterevisioncb,
173 duplicaterevisioncb=duplicaterevisioncb,
174 )
174 )
175
175
176 def getstrippoint(self, minlink):
176 def getstrippoint(self, minlink):
177 return self._revlog.getstrippoint(minlink)
177 return self._revlog.getstrippoint(minlink)
178
178
179 def strip(self, minlink, transaction):
179 def strip(self, minlink, transaction):
180 return self._revlog.strip(minlink, transaction)
180 return self._revlog.strip(minlink, transaction)
181
181
182 def censorrevision(self, tr, node, tombstone=b''):
182 def censorrevision(self, tr, node, tombstone=b''):
183 return self._revlog.censorrevision(tr, node, tombstone=tombstone)
183 return self._revlog.censorrevision(tr, node, tombstone=tombstone)
184
184
185 def files(self):
185 def files(self):
186 return self._revlog.files()
186 return self._revlog.files()
187
187
188 def read(self, node):
188 def read(self, node):
189 return storageutil.filtermetadata(self.revision(node))
189 return storageutil.filtermetadata(self.revision(node))
190
190
191 def add(self, text, meta, transaction, link, p1=None, p2=None):
191 def add(self, text, meta, transaction, link, p1=None, p2=None):
192 if meta or text.startswith(b'\1\n'):
192 if meta or text.startswith(b'\1\n'):
193 text = storageutil.packmeta(meta, text)
193 text = storageutil.packmeta(meta, text)
194 rev = self.addrevision(text, transaction, link, p1, p2)
194 rev = self.addrevision(text, transaction, link, p1, p2)
195 return self.node(rev)
195 return self.node(rev)
196
196
197 def renamed(self, node):
197 def renamed(self, node):
198 return storageutil.filerevisioncopied(self, node)
198 return storageutil.filerevisioncopied(self, node)
199
199
200 def size(self, rev):
200 def size(self, rev):
201 """return the size of a given revision"""
201 """return the size of a given revision"""
202
202
203 # for revisions with renames, we have to go the slow way
203 # for revisions with renames, we have to go the slow way
204 node = self.node(rev)
204 node = self.node(rev)
205 if self.iscensored(rev):
206 return 0
205 if self.renamed(node):
207 if self.renamed(node):
206 return len(self.read(node))
208 return len(self.read(node))
207 if self.iscensored(rev):
208 return 0
209
209
210 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
210 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
211 return self._revlog.size(rev)
211 return self._revlog.size(rev)
212
212
213 def cmp(self, node, text):
213 def cmp(self, node, text):
214 """compare text with a given file revision
214 """compare text with a given file revision
215
215
216 returns True if text is different than what is stored.
216 returns True if text is different than what is stored.
217 """
217 """
218 return not storageutil.filedataequivalent(self, node, text)
218 return not storageutil.filedataequivalent(self, node, text)
219
219
220 def verifyintegrity(self, state):
220 def verifyintegrity(self, state):
221 return self._revlog.verifyintegrity(state)
221 return self._revlog.verifyintegrity(state)
222
222
223 def storageinfo(
223 def storageinfo(
224 self,
224 self,
225 exclusivefiles=False,
225 exclusivefiles=False,
226 sharedfiles=False,
226 sharedfiles=False,
227 revisionscount=False,
227 revisionscount=False,
228 trackedsize=False,
228 trackedsize=False,
229 storedsize=False,
229 storedsize=False,
230 ):
230 ):
231 return self._revlog.storageinfo(
231 return self._revlog.storageinfo(
232 exclusivefiles=exclusivefiles,
232 exclusivefiles=exclusivefiles,
233 sharedfiles=sharedfiles,
233 sharedfiles=sharedfiles,
234 revisionscount=revisionscount,
234 revisionscount=revisionscount,
235 trackedsize=trackedsize,
235 trackedsize=trackedsize,
236 storedsize=storedsize,
236 storedsize=storedsize,
237 )
237 )
238
238
239 # Used by repo upgrade.
239 # Used by repo upgrade.
240 def clone(self, tr, destrevlog, **kwargs):
240 def clone(self, tr, destrevlog, **kwargs):
241 if not isinstance(destrevlog, filelog):
241 if not isinstance(destrevlog, filelog):
242 raise error.ProgrammingError(b'expected filelog to clone()')
242 raise error.ProgrammingError(b'expected filelog to clone()')
243
243
244 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
244 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
245
245
246
246
247 class narrowfilelog(filelog):
247 class narrowfilelog(filelog):
248 """Filelog variation to be used with narrow stores."""
248 """Filelog variation to be used with narrow stores."""
249
249
250 def __init__(self, opener, path, narrowmatch):
250 def __init__(self, opener, path, narrowmatch):
251 super(narrowfilelog, self).__init__(opener, path)
251 super(narrowfilelog, self).__init__(opener, path)
252 self._narrowmatch = narrowmatch
252 self._narrowmatch = narrowmatch
253
253
254 def renamed(self, node):
254 def renamed(self, node):
255 res = super(narrowfilelog, self).renamed(node)
255 res = super(narrowfilelog, self).renamed(node)
256
256
257 # Renames that come from outside the narrowspec are problematic
257 # Renames that come from outside the narrowspec are problematic
258 # because we may lack the base text for the rename. This can result
258 # because we may lack the base text for the rename. This can result
259 # in code attempting to walk the ancestry or compute a diff
259 # in code attempting to walk the ancestry or compute a diff
260 # encountering a missing revision. We address this by silently
260 # encountering a missing revision. We address this by silently
261 # removing rename metadata if the source file is outside the
261 # removing rename metadata if the source file is outside the
262 # narrow spec.
262 # narrow spec.
263 #
263 #
264 # A better solution would be to see if the base revision is available,
264 # A better solution would be to see if the base revision is available,
265 # rather than assuming it isn't.
265 # rather than assuming it isn't.
266 #
266 #
267 # An even better solution would be to teach all consumers of rename
267 # An even better solution would be to teach all consumers of rename
268 # metadata that the base revision may not be available.
268 # metadata that the base revision may not be available.
269 #
269 #
270 # TODO consider better ways of doing this.
270 # TODO consider better ways of doing this.
271 if res and not self._narrowmatch(res[0]):
271 if res and not self._narrowmatch(res[0]):
272 return None
272 return None
273
273
274 return res
274 return res
275
275
276 def size(self, rev):
276 def size(self, rev):
277 # Because we have a custom renamed() that may lie, we need to call
277 # Because we have a custom renamed() that may lie, we need to call
278 # the base renamed() to report accurate results.
278 # the base renamed() to report accurate results.
279 node = self.node(rev)
279 node = self.node(rev)
280 if super(narrowfilelog, self).renamed(node):
280 if super(narrowfilelog, self).renamed(node):
281 return len(self.read(node))
281 return len(self.read(node))
282 else:
282 else:
283 return super(narrowfilelog, self).size(rev)
283 return super(narrowfilelog, self).size(rev)
284
284
285 def cmp(self, node, text):
285 def cmp(self, node, text):
286 # We don't call `super` because narrow parents can be buggy in case of a
286 # We don't call `super` because narrow parents can be buggy in case of a
287 # ambiguous dirstate. Always take the slow path until there is a better
287 # ambiguous dirstate. Always take the slow path until there is a better
288 # fix, see issue6150.
288 # fix, see issue6150.
289
289
290 # Censored files compare against the empty file.
290 # Censored files compare against the empty file.
291 if self.iscensored(self.rev(node)):
291 if self.iscensored(self.rev(node)):
292 return text != b''
292 return text != b''
293
293
294 return self.read(node) != text
294 return self.read(node) != text
@@ -1,23 +1,22 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [censor]
2 > [censor]
3 > policy=ignore
3 > policy=ignore
4 > EOF
4 > EOF
5
5
6 $ mkdir r
6 $ mkdir r
7 $ cd r
7 $ cd r
8 $ hg init
8 $ hg init
9 $ echo secret > target
9 $ echo secret > target
10 $ hg commit -Am "secret"
10 $ hg commit -Am "secret"
11 adding target
11 adding target
12 $ touch bystander
12 $ touch bystander
13 $ hg commit -Am "innocent"
13 $ hg commit -Am "innocent"
14 adding bystander
14 adding bystander
15 $ echo erased-secret > target
15 $ echo erased-secret > target
16 $ hg commit -m "erased secret"
16 $ hg commit -m "erased secret"
17 $ hg censor target --config extensions.censor= -r ".^^"
17 $ hg censor target --config extensions.censor= -r ".^^"
18 $ hg update ".^"
18 $ hg update ".^"
19 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ cat target
20 $ cat target
21 $ hg update tip
21 $ hg update tip
22 abort: file censored data/target:b1c12cf98dc8 (known-bad-output !)
22 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 [255]
General Comments 0
You need to be logged in to leave comments. Login now