##// END OF EJS Templates
manifest: introduce new exception to signal unavailability of fastdelta()...
Augie Fackler -
r45154:948fac24 default
parent child Browse files
Show More
@@ -1,1969 +1,1972 b''
1 # repository.py - Interfaces and base classes for repositories and peers.
1 # repository.py - Interfaces and base classes for repositories and peers.
2 #
2 #
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2017 Gregory Szorc <gregory.szorc@gmail.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 .. import error
11 from .. import error
12 from . import util as interfaceutil
12 from . import util as interfaceutil
13
13
14 # When narrowing is finalized and no longer subject to format changes,
14 # When narrowing is finalized and no longer subject to format changes,
15 # we should move this to just "narrow" or similar.
15 # we should move this to just "narrow" or similar.
16 NARROW_REQUIREMENT = b'narrowhg-experimental'
16 NARROW_REQUIREMENT = b'narrowhg-experimental'
17
17
18 # Local repository feature string.
18 # Local repository feature string.
19
19
20 # Revlogs are being used for file storage.
20 # Revlogs are being used for file storage.
21 REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
21 REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage'
22 # The storage part of the repository is shared from an external source.
22 # The storage part of the repository is shared from an external source.
23 REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
23 REPO_FEATURE_SHARED_STORAGE = b'sharedstore'
24 # LFS supported for backing file storage.
24 # LFS supported for backing file storage.
25 REPO_FEATURE_LFS = b'lfs'
25 REPO_FEATURE_LFS = b'lfs'
26 # Repository supports being stream cloned.
26 # Repository supports being stream cloned.
27 REPO_FEATURE_STREAM_CLONE = b'streamclone'
27 REPO_FEATURE_STREAM_CLONE = b'streamclone'
28 # Files storage may lack data for all ancestors.
28 # Files storage may lack data for all ancestors.
29 REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage'
29 REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage'
30
30
31 REVISION_FLAG_CENSORED = 1 << 15
31 REVISION_FLAG_CENSORED = 1 << 15
32 REVISION_FLAG_ELLIPSIS = 1 << 14
32 REVISION_FLAG_ELLIPSIS = 1 << 14
33 REVISION_FLAG_EXTSTORED = 1 << 13
33 REVISION_FLAG_EXTSTORED = 1 << 13
34 REVISION_FLAG_SIDEDATA = 1 << 12
34 REVISION_FLAG_SIDEDATA = 1 << 12
35
35
36 REVISION_FLAGS_KNOWN = (
36 REVISION_FLAGS_KNOWN = (
37 REVISION_FLAG_CENSORED
37 REVISION_FLAG_CENSORED
38 | REVISION_FLAG_ELLIPSIS
38 | REVISION_FLAG_ELLIPSIS
39 | REVISION_FLAG_EXTSTORED
39 | REVISION_FLAG_EXTSTORED
40 | REVISION_FLAG_SIDEDATA
40 | REVISION_FLAG_SIDEDATA
41 )
41 )
42
42
43 CG_DELTAMODE_STD = b'default'
43 CG_DELTAMODE_STD = b'default'
44 CG_DELTAMODE_PREV = b'previous'
44 CG_DELTAMODE_PREV = b'previous'
45 CG_DELTAMODE_FULL = b'fulltext'
45 CG_DELTAMODE_FULL = b'fulltext'
46 CG_DELTAMODE_P1 = b'p1'
46 CG_DELTAMODE_P1 = b'p1'
47
47
48
48
49 class ipeerconnection(interfaceutil.Interface):
49 class ipeerconnection(interfaceutil.Interface):
50 """Represents a "connection" to a repository.
50 """Represents a "connection" to a repository.
51
51
52 This is the base interface for representing a connection to a repository.
52 This is the base interface for representing a connection to a repository.
53 It holds basic properties and methods applicable to all peer types.
53 It holds basic properties and methods applicable to all peer types.
54
54
55 This is not a complete interface definition and should not be used
55 This is not a complete interface definition and should not be used
56 outside of this module.
56 outside of this module.
57 """
57 """
58
58
59 ui = interfaceutil.Attribute("""ui.ui instance""")
59 ui = interfaceutil.Attribute("""ui.ui instance""")
60
60
61 def url():
61 def url():
62 """Returns a URL string representing this peer.
62 """Returns a URL string representing this peer.
63
63
64 Currently, implementations expose the raw URL used to construct the
64 Currently, implementations expose the raw URL used to construct the
65 instance. It may contain credentials as part of the URL. The
65 instance. It may contain credentials as part of the URL. The
66 expectations of the value aren't well-defined and this could lead to
66 expectations of the value aren't well-defined and this could lead to
67 data leakage.
67 data leakage.
68
68
69 TODO audit/clean consumers and more clearly define the contents of this
69 TODO audit/clean consumers and more clearly define the contents of this
70 value.
70 value.
71 """
71 """
72
72
73 def local():
73 def local():
74 """Returns a local repository instance.
74 """Returns a local repository instance.
75
75
76 If the peer represents a local repository, returns an object that
76 If the peer represents a local repository, returns an object that
77 can be used to interface with it. Otherwise returns ``None``.
77 can be used to interface with it. Otherwise returns ``None``.
78 """
78 """
79
79
80 def peer():
80 def peer():
81 """Returns an object conforming to this interface.
81 """Returns an object conforming to this interface.
82
82
83 Most implementations will ``return self``.
83 Most implementations will ``return self``.
84 """
84 """
85
85
86 def canpush():
86 def canpush():
87 """Returns a boolean indicating if this peer can be pushed to."""
87 """Returns a boolean indicating if this peer can be pushed to."""
88
88
89 def close():
89 def close():
90 """Close the connection to this peer.
90 """Close the connection to this peer.
91
91
92 This is called when the peer will no longer be used. Resources
92 This is called when the peer will no longer be used. Resources
93 associated with the peer should be cleaned up.
93 associated with the peer should be cleaned up.
94 """
94 """
95
95
96
96
97 class ipeercapabilities(interfaceutil.Interface):
97 class ipeercapabilities(interfaceutil.Interface):
98 """Peer sub-interface related to capabilities."""
98 """Peer sub-interface related to capabilities."""
99
99
100 def capable(name):
100 def capable(name):
101 """Determine support for a named capability.
101 """Determine support for a named capability.
102
102
103 Returns ``False`` if capability not supported.
103 Returns ``False`` if capability not supported.
104
104
105 Returns ``True`` if boolean capability is supported. Returns a string
105 Returns ``True`` if boolean capability is supported. Returns a string
106 if capability support is non-boolean.
106 if capability support is non-boolean.
107
107
108 Capability strings may or may not map to wire protocol capabilities.
108 Capability strings may or may not map to wire protocol capabilities.
109 """
109 """
110
110
111 def requirecap(name, purpose):
111 def requirecap(name, purpose):
112 """Require a capability to be present.
112 """Require a capability to be present.
113
113
114 Raises a ``CapabilityError`` if the capability isn't present.
114 Raises a ``CapabilityError`` if the capability isn't present.
115 """
115 """
116
116
117
117
118 class ipeercommands(interfaceutil.Interface):
118 class ipeercommands(interfaceutil.Interface):
119 """Client-side interface for communicating over the wire protocol.
119 """Client-side interface for communicating over the wire protocol.
120
120
121 This interface is used as a gateway to the Mercurial wire protocol.
121 This interface is used as a gateway to the Mercurial wire protocol.
122 methods commonly call wire protocol commands of the same name.
122 methods commonly call wire protocol commands of the same name.
123 """
123 """
124
124
125 def branchmap():
125 def branchmap():
126 """Obtain heads in named branches.
126 """Obtain heads in named branches.
127
127
128 Returns a dict mapping branch name to an iterable of nodes that are
128 Returns a dict mapping branch name to an iterable of nodes that are
129 heads on that branch.
129 heads on that branch.
130 """
130 """
131
131
132 def capabilities():
132 def capabilities():
133 """Obtain capabilities of the peer.
133 """Obtain capabilities of the peer.
134
134
135 Returns a set of string capabilities.
135 Returns a set of string capabilities.
136 """
136 """
137
137
138 def clonebundles():
138 def clonebundles():
139 """Obtains the clone bundles manifest for the repo.
139 """Obtains the clone bundles manifest for the repo.
140
140
141 Returns the manifest as unparsed bytes.
141 Returns the manifest as unparsed bytes.
142 """
142 """
143
143
144 def debugwireargs(one, two, three=None, four=None, five=None):
144 def debugwireargs(one, two, three=None, four=None, five=None):
145 """Used to facilitate debugging of arguments passed over the wire."""
145 """Used to facilitate debugging of arguments passed over the wire."""
146
146
147 def getbundle(source, **kwargs):
147 def getbundle(source, **kwargs):
148 """Obtain remote repository data as a bundle.
148 """Obtain remote repository data as a bundle.
149
149
150 This command is how the bulk of repository data is transferred from
150 This command is how the bulk of repository data is transferred from
151 the peer to the local repository
151 the peer to the local repository
152
152
153 Returns a generator of bundle data.
153 Returns a generator of bundle data.
154 """
154 """
155
155
156 def heads():
156 def heads():
157 """Determine all known head revisions in the peer.
157 """Determine all known head revisions in the peer.
158
158
159 Returns an iterable of binary nodes.
159 Returns an iterable of binary nodes.
160 """
160 """
161
161
162 def known(nodes):
162 def known(nodes):
163 """Determine whether multiple nodes are known.
163 """Determine whether multiple nodes are known.
164
164
165 Accepts an iterable of nodes whose presence to check for.
165 Accepts an iterable of nodes whose presence to check for.
166
166
167 Returns an iterable of booleans indicating of the corresponding node
167 Returns an iterable of booleans indicating of the corresponding node
168 at that index is known to the peer.
168 at that index is known to the peer.
169 """
169 """
170
170
171 def listkeys(namespace):
171 def listkeys(namespace):
172 """Obtain all keys in a pushkey namespace.
172 """Obtain all keys in a pushkey namespace.
173
173
174 Returns an iterable of key names.
174 Returns an iterable of key names.
175 """
175 """
176
176
177 def lookup(key):
177 def lookup(key):
178 """Resolve a value to a known revision.
178 """Resolve a value to a known revision.
179
179
180 Returns a binary node of the resolved revision on success.
180 Returns a binary node of the resolved revision on success.
181 """
181 """
182
182
183 def pushkey(namespace, key, old, new):
183 def pushkey(namespace, key, old, new):
184 """Set a value using the ``pushkey`` protocol.
184 """Set a value using the ``pushkey`` protocol.
185
185
186 Arguments correspond to the pushkey namespace and key to operate on and
186 Arguments correspond to the pushkey namespace and key to operate on and
187 the old and new values for that key.
187 the old and new values for that key.
188
188
189 Returns a string with the peer result. The value inside varies by the
189 Returns a string with the peer result. The value inside varies by the
190 namespace.
190 namespace.
191 """
191 """
192
192
193 def stream_out():
193 def stream_out():
194 """Obtain streaming clone data.
194 """Obtain streaming clone data.
195
195
196 Successful result should be a generator of data chunks.
196 Successful result should be a generator of data chunks.
197 """
197 """
198
198
199 def unbundle(bundle, heads, url):
199 def unbundle(bundle, heads, url):
200 """Transfer repository data to the peer.
200 """Transfer repository data to the peer.
201
201
202 This is how the bulk of data during a push is transferred.
202 This is how the bulk of data during a push is transferred.
203
203
204 Returns the integer number of heads added to the peer.
204 Returns the integer number of heads added to the peer.
205 """
205 """
206
206
207
207
208 class ipeerlegacycommands(interfaceutil.Interface):
208 class ipeerlegacycommands(interfaceutil.Interface):
209 """Interface for implementing support for legacy wire protocol commands.
209 """Interface for implementing support for legacy wire protocol commands.
210
210
211 Wire protocol commands transition to legacy status when they are no longer
211 Wire protocol commands transition to legacy status when they are no longer
212 used by modern clients. To facilitate identifying which commands are
212 used by modern clients. To facilitate identifying which commands are
213 legacy, the interfaces are split.
213 legacy, the interfaces are split.
214 """
214 """
215
215
216 def between(pairs):
216 def between(pairs):
217 """Obtain nodes between pairs of nodes.
217 """Obtain nodes between pairs of nodes.
218
218
219 ``pairs`` is an iterable of node pairs.
219 ``pairs`` is an iterable of node pairs.
220
220
221 Returns an iterable of iterables of nodes corresponding to each
221 Returns an iterable of iterables of nodes corresponding to each
222 requested pair.
222 requested pair.
223 """
223 """
224
224
225 def branches(nodes):
225 def branches(nodes):
226 """Obtain ancestor changesets of specific nodes back to a branch point.
226 """Obtain ancestor changesets of specific nodes back to a branch point.
227
227
228 For each requested node, the peer finds the first ancestor node that is
228 For each requested node, the peer finds the first ancestor node that is
229 a DAG root or is a merge.
229 a DAG root or is a merge.
230
230
231 Returns an iterable of iterables with the resolved values for each node.
231 Returns an iterable of iterables with the resolved values for each node.
232 """
232 """
233
233
234 def changegroup(nodes, source):
234 def changegroup(nodes, source):
235 """Obtain a changegroup with data for descendants of specified nodes."""
235 """Obtain a changegroup with data for descendants of specified nodes."""
236
236
237 def changegroupsubset(bases, heads, source):
237 def changegroupsubset(bases, heads, source):
238 pass
238 pass
239
239
240
240
241 class ipeercommandexecutor(interfaceutil.Interface):
241 class ipeercommandexecutor(interfaceutil.Interface):
242 """Represents a mechanism to execute remote commands.
242 """Represents a mechanism to execute remote commands.
243
243
244 This is the primary interface for requesting that wire protocol commands
244 This is the primary interface for requesting that wire protocol commands
245 be executed. Instances of this interface are active in a context manager
245 be executed. Instances of this interface are active in a context manager
246 and have a well-defined lifetime. When the context manager exits, all
246 and have a well-defined lifetime. When the context manager exits, all
247 outstanding requests are waited on.
247 outstanding requests are waited on.
248 """
248 """
249
249
250 def callcommand(name, args):
250 def callcommand(name, args):
251 """Request that a named command be executed.
251 """Request that a named command be executed.
252
252
253 Receives the command name and a dictionary of command arguments.
253 Receives the command name and a dictionary of command arguments.
254
254
255 Returns a ``concurrent.futures.Future`` that will resolve to the
255 Returns a ``concurrent.futures.Future`` that will resolve to the
256 result of that command request. That exact value is left up to
256 result of that command request. That exact value is left up to
257 the implementation and possibly varies by command.
257 the implementation and possibly varies by command.
258
258
259 Not all commands can coexist with other commands in an executor
259 Not all commands can coexist with other commands in an executor
260 instance: it depends on the underlying wire protocol transport being
260 instance: it depends on the underlying wire protocol transport being
261 used and the command itself.
261 used and the command itself.
262
262
263 Implementations MAY call ``sendcommands()`` automatically if the
263 Implementations MAY call ``sendcommands()`` automatically if the
264 requested command can not coexist with other commands in this executor.
264 requested command can not coexist with other commands in this executor.
265
265
266 Implementations MAY call ``sendcommands()`` automatically when the
266 Implementations MAY call ``sendcommands()`` automatically when the
267 future's ``result()`` is called. So, consumers using multiple
267 future's ``result()`` is called. So, consumers using multiple
268 commands with an executor MUST ensure that ``result()`` is not called
268 commands with an executor MUST ensure that ``result()`` is not called
269 until all command requests have been issued.
269 until all command requests have been issued.
270 """
270 """
271
271
272 def sendcommands():
272 def sendcommands():
273 """Trigger submission of queued command requests.
273 """Trigger submission of queued command requests.
274
274
275 Not all transports submit commands as soon as they are requested to
275 Not all transports submit commands as soon as they are requested to
276 run. When called, this method forces queued command requests to be
276 run. When called, this method forces queued command requests to be
277 issued. It will no-op if all commands have already been sent.
277 issued. It will no-op if all commands have already been sent.
278
278
279 When called, no more new commands may be issued with this executor.
279 When called, no more new commands may be issued with this executor.
280 """
280 """
281
281
282 def close():
282 def close():
283 """Signal that this command request is finished.
283 """Signal that this command request is finished.
284
284
285 When called, no more new commands may be issued. All outstanding
285 When called, no more new commands may be issued. All outstanding
286 commands that have previously been issued are waited on before
286 commands that have previously been issued are waited on before
287 returning. This not only includes waiting for the futures to resolve,
287 returning. This not only includes waiting for the futures to resolve,
288 but also waiting for all response data to arrive. In other words,
288 but also waiting for all response data to arrive. In other words,
289 calling this waits for all on-wire state for issued command requests
289 calling this waits for all on-wire state for issued command requests
290 to finish.
290 to finish.
291
291
292 When used as a context manager, this method is called when exiting the
292 When used as a context manager, this method is called when exiting the
293 context manager.
293 context manager.
294
294
295 This method may call ``sendcommands()`` if there are buffered commands.
295 This method may call ``sendcommands()`` if there are buffered commands.
296 """
296 """
297
297
298
298
299 class ipeerrequests(interfaceutil.Interface):
299 class ipeerrequests(interfaceutil.Interface):
300 """Interface for executing commands on a peer."""
300 """Interface for executing commands on a peer."""
301
301
302 limitedarguments = interfaceutil.Attribute(
302 limitedarguments = interfaceutil.Attribute(
303 """True if the peer cannot receive large argument value for commands."""
303 """True if the peer cannot receive large argument value for commands."""
304 )
304 )
305
305
306 def commandexecutor():
306 def commandexecutor():
307 """A context manager that resolves to an ipeercommandexecutor.
307 """A context manager that resolves to an ipeercommandexecutor.
308
308
309 The object this resolves to can be used to issue command requests
309 The object this resolves to can be used to issue command requests
310 to the peer.
310 to the peer.
311
311
312 Callers should call its ``callcommand`` method to issue command
312 Callers should call its ``callcommand`` method to issue command
313 requests.
313 requests.
314
314
315 A new executor should be obtained for each distinct set of commands
315 A new executor should be obtained for each distinct set of commands
316 (possibly just a single command) that the consumer wants to execute
316 (possibly just a single command) that the consumer wants to execute
317 as part of a single operation or round trip. This is because some
317 as part of a single operation or round trip. This is because some
318 peers are half-duplex and/or don't support persistent connections.
318 peers are half-duplex and/or don't support persistent connections.
319 e.g. in the case of HTTP peers, commands sent to an executor represent
319 e.g. in the case of HTTP peers, commands sent to an executor represent
320 a single HTTP request. While some peers may support multiple command
320 a single HTTP request. While some peers may support multiple command
321 sends over the wire per executor, consumers need to code to the least
321 sends over the wire per executor, consumers need to code to the least
322 capable peer. So it should be assumed that command executors buffer
322 capable peer. So it should be assumed that command executors buffer
323 called commands until they are told to send them and that each
323 called commands until they are told to send them and that each
324 command executor could result in a new connection or wire-level request
324 command executor could result in a new connection or wire-level request
325 being issued.
325 being issued.
326 """
326 """
327
327
328
328
329 class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests):
329 class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests):
330 """Unified interface for peer repositories.
330 """Unified interface for peer repositories.
331
331
332 All peer instances must conform to this interface.
332 All peer instances must conform to this interface.
333 """
333 """
334
334
335
335
336 class ipeerv2(ipeerconnection, ipeercapabilities, ipeerrequests):
336 class ipeerv2(ipeerconnection, ipeercapabilities, ipeerrequests):
337 """Unified peer interface for wire protocol version 2 peers."""
337 """Unified peer interface for wire protocol version 2 peers."""
338
338
339 apidescriptor = interfaceutil.Attribute(
339 apidescriptor = interfaceutil.Attribute(
340 """Data structure holding description of server API."""
340 """Data structure holding description of server API."""
341 )
341 )
342
342
343
343
344 @interfaceutil.implementer(ipeerbase)
344 @interfaceutil.implementer(ipeerbase)
345 class peer(object):
345 class peer(object):
346 """Base class for peer repositories."""
346 """Base class for peer repositories."""
347
347
348 limitedarguments = False
348 limitedarguments = False
349
349
350 def capable(self, name):
350 def capable(self, name):
351 caps = self.capabilities()
351 caps = self.capabilities()
352 if name in caps:
352 if name in caps:
353 return True
353 return True
354
354
355 name = b'%s=' % name
355 name = b'%s=' % name
356 for cap in caps:
356 for cap in caps:
357 if cap.startswith(name):
357 if cap.startswith(name):
358 return cap[len(name) :]
358 return cap[len(name) :]
359
359
360 return False
360 return False
361
361
362 def requirecap(self, name, purpose):
362 def requirecap(self, name, purpose):
363 if self.capable(name):
363 if self.capable(name):
364 return
364 return
365
365
366 raise error.CapabilityError(
366 raise error.CapabilityError(
367 _(
367 _(
368 b'cannot %s; remote repository does not support the '
368 b'cannot %s; remote repository does not support the '
369 b'\'%s\' capability'
369 b'\'%s\' capability'
370 )
370 )
371 % (purpose, name)
371 % (purpose, name)
372 )
372 )
373
373
374
374
375 class iverifyproblem(interfaceutil.Interface):
375 class iverifyproblem(interfaceutil.Interface):
376 """Represents a problem with the integrity of the repository.
376 """Represents a problem with the integrity of the repository.
377
377
378 Instances of this interface are emitted to describe an integrity issue
378 Instances of this interface are emitted to describe an integrity issue
379 with a repository (e.g. corrupt storage, missing data, etc).
379 with a repository (e.g. corrupt storage, missing data, etc).
380
380
381 Instances are essentially messages associated with severity.
381 Instances are essentially messages associated with severity.
382 """
382 """
383
383
384 warning = interfaceutil.Attribute(
384 warning = interfaceutil.Attribute(
385 """Message indicating a non-fatal problem."""
385 """Message indicating a non-fatal problem."""
386 )
386 )
387
387
388 error = interfaceutil.Attribute("""Message indicating a fatal problem.""")
388 error = interfaceutil.Attribute("""Message indicating a fatal problem.""")
389
389
390 node = interfaceutil.Attribute(
390 node = interfaceutil.Attribute(
391 """Revision encountering the problem.
391 """Revision encountering the problem.
392
392
393 ``None`` means the problem doesn't apply to a single revision.
393 ``None`` means the problem doesn't apply to a single revision.
394 """
394 """
395 )
395 )
396
396
397
397
398 class irevisiondelta(interfaceutil.Interface):
398 class irevisiondelta(interfaceutil.Interface):
399 """Represents a delta between one revision and another.
399 """Represents a delta between one revision and another.
400
400
401 Instances convey enough information to allow a revision to be exchanged
401 Instances convey enough information to allow a revision to be exchanged
402 with another repository.
402 with another repository.
403
403
404 Instances represent the fulltext revision data or a delta against
404 Instances represent the fulltext revision data or a delta against
405 another revision. Therefore the ``revision`` and ``delta`` attributes
405 another revision. Therefore the ``revision`` and ``delta`` attributes
406 are mutually exclusive.
406 are mutually exclusive.
407
407
408 Typically used for changegroup generation.
408 Typically used for changegroup generation.
409 """
409 """
410
410
411 node = interfaceutil.Attribute("""20 byte node of this revision.""")
411 node = interfaceutil.Attribute("""20 byte node of this revision.""")
412
412
413 p1node = interfaceutil.Attribute(
413 p1node = interfaceutil.Attribute(
414 """20 byte node of 1st parent of this revision."""
414 """20 byte node of 1st parent of this revision."""
415 )
415 )
416
416
417 p2node = interfaceutil.Attribute(
417 p2node = interfaceutil.Attribute(
418 """20 byte node of 2nd parent of this revision."""
418 """20 byte node of 2nd parent of this revision."""
419 )
419 )
420
420
421 linknode = interfaceutil.Attribute(
421 linknode = interfaceutil.Attribute(
422 """20 byte node of the changelog revision this node is linked to."""
422 """20 byte node of the changelog revision this node is linked to."""
423 )
423 )
424
424
425 flags = interfaceutil.Attribute(
425 flags = interfaceutil.Attribute(
426 """2 bytes of integer flags that apply to this revision.
426 """2 bytes of integer flags that apply to this revision.
427
427
428 This is a bitwise composition of the ``REVISION_FLAG_*`` constants.
428 This is a bitwise composition of the ``REVISION_FLAG_*`` constants.
429 """
429 """
430 )
430 )
431
431
432 basenode = interfaceutil.Attribute(
432 basenode = interfaceutil.Attribute(
433 """20 byte node of the revision this data is a delta against.
433 """20 byte node of the revision this data is a delta against.
434
434
435 ``nullid`` indicates that the revision is a full revision and not
435 ``nullid`` indicates that the revision is a full revision and not
436 a delta.
436 a delta.
437 """
437 """
438 )
438 )
439
439
440 baserevisionsize = interfaceutil.Attribute(
440 baserevisionsize = interfaceutil.Attribute(
441 """Size of base revision this delta is against.
441 """Size of base revision this delta is against.
442
442
443 May be ``None`` if ``basenode`` is ``nullid``.
443 May be ``None`` if ``basenode`` is ``nullid``.
444 """
444 """
445 )
445 )
446
446
447 revision = interfaceutil.Attribute(
447 revision = interfaceutil.Attribute(
448 """Raw fulltext of revision data for this node."""
448 """Raw fulltext of revision data for this node."""
449 )
449 )
450
450
451 delta = interfaceutil.Attribute(
451 delta = interfaceutil.Attribute(
452 """Delta between ``basenode`` and ``node``.
452 """Delta between ``basenode`` and ``node``.
453
453
454 Stored in the bdiff delta format.
454 Stored in the bdiff delta format.
455 """
455 """
456 )
456 )
457
457
458
458
459 class ifilerevisionssequence(interfaceutil.Interface):
459 class ifilerevisionssequence(interfaceutil.Interface):
460 """Contains index data for all revisions of a file.
460 """Contains index data for all revisions of a file.
461
461
462 Types implementing this behave like lists of tuples. The index
462 Types implementing this behave like lists of tuples. The index
463 in the list corresponds to the revision number. The values contain
463 in the list corresponds to the revision number. The values contain
464 index metadata.
464 index metadata.
465
465
466 The *null* revision (revision number -1) is always the last item
466 The *null* revision (revision number -1) is always the last item
467 in the index.
467 in the index.
468 """
468 """
469
469
470 def __len__():
470 def __len__():
471 """The total number of revisions."""
471 """The total number of revisions."""
472
472
473 def __getitem__(rev):
473 def __getitem__(rev):
474 """Returns the object having a specific revision number.
474 """Returns the object having a specific revision number.
475
475
476 Returns an 8-tuple with the following fields:
476 Returns an 8-tuple with the following fields:
477
477
478 offset+flags
478 offset+flags
479 Contains the offset and flags for the revision. 64-bit unsigned
479 Contains the offset and flags for the revision. 64-bit unsigned
480 integer where first 6 bytes are the offset and the next 2 bytes
480 integer where first 6 bytes are the offset and the next 2 bytes
481 are flags. The offset can be 0 if it is not used by the store.
481 are flags. The offset can be 0 if it is not used by the store.
482 compressed size
482 compressed size
483 Size of the revision data in the store. It can be 0 if it isn't
483 Size of the revision data in the store. It can be 0 if it isn't
484 needed by the store.
484 needed by the store.
485 uncompressed size
485 uncompressed size
486 Fulltext size. It can be 0 if it isn't needed by the store.
486 Fulltext size. It can be 0 if it isn't needed by the store.
487 base revision
487 base revision
488 Revision number of revision the delta for storage is encoded
488 Revision number of revision the delta for storage is encoded
489 against. -1 indicates not encoded against a base revision.
489 against. -1 indicates not encoded against a base revision.
490 link revision
490 link revision
491 Revision number of changelog revision this entry is related to.
491 Revision number of changelog revision this entry is related to.
492 p1 revision
492 p1 revision
493 Revision number of 1st parent. -1 if no 1st parent.
493 Revision number of 1st parent. -1 if no 1st parent.
494 p2 revision
494 p2 revision
495 Revision number of 2nd parent. -1 if no 1st parent.
495 Revision number of 2nd parent. -1 if no 1st parent.
496 node
496 node
497 Binary node value for this revision number.
497 Binary node value for this revision number.
498
498
499 Negative values should index off the end of the sequence. ``-1``
499 Negative values should index off the end of the sequence. ``-1``
500 should return the null revision. ``-2`` should return the most
500 should return the null revision. ``-2`` should return the most
501 recent revision.
501 recent revision.
502 """
502 """
503
503
504 def __contains__(rev):
504 def __contains__(rev):
505 """Whether a revision number exists."""
505 """Whether a revision number exists."""
506
506
507 def insert(self, i, entry):
507 def insert(self, i, entry):
508 """Add an item to the index at specific revision."""
508 """Add an item to the index at specific revision."""
509
509
510
510
511 class ifileindex(interfaceutil.Interface):
511 class ifileindex(interfaceutil.Interface):
512 """Storage interface for index data of a single file.
512 """Storage interface for index data of a single file.
513
513
514 File storage data is divided into index metadata and data storage.
514 File storage data is divided into index metadata and data storage.
515 This interface defines the index portion of the interface.
515 This interface defines the index portion of the interface.
516
516
517 The index logically consists of:
517 The index logically consists of:
518
518
519 * A mapping between revision numbers and nodes.
519 * A mapping between revision numbers and nodes.
520 * DAG data (storing and querying the relationship between nodes).
520 * DAG data (storing and querying the relationship between nodes).
521 * Metadata to facilitate storage.
521 * Metadata to facilitate storage.
522 """
522 """
523
523
524 def __len__():
524 def __len__():
525 """Obtain the number of revisions stored for this file."""
525 """Obtain the number of revisions stored for this file."""
526
526
527 def __iter__():
527 def __iter__():
528 """Iterate over revision numbers for this file."""
528 """Iterate over revision numbers for this file."""
529
529
530 def hasnode(node):
530 def hasnode(node):
531 """Returns a bool indicating if a node is known to this store.
531 """Returns a bool indicating if a node is known to this store.
532
532
533 Implementations must only return True for full, binary node values:
533 Implementations must only return True for full, binary node values:
534 hex nodes, revision numbers, and partial node matches must be
534 hex nodes, revision numbers, and partial node matches must be
535 rejected.
535 rejected.
536
536
537 The null node is never present.
537 The null node is never present.
538 """
538 """
539
539
540 def revs(start=0, stop=None):
540 def revs(start=0, stop=None):
541 """Iterate over revision numbers for this file, with control."""
541 """Iterate over revision numbers for this file, with control."""
542
542
543 def parents(node):
543 def parents(node):
544 """Returns a 2-tuple of parent nodes for a revision.
544 """Returns a 2-tuple of parent nodes for a revision.
545
545
546 Values will be ``nullid`` if the parent is empty.
546 Values will be ``nullid`` if the parent is empty.
547 """
547 """
548
548
549 def parentrevs(rev):
549 def parentrevs(rev):
550 """Like parents() but operates on revision numbers."""
550 """Like parents() but operates on revision numbers."""
551
551
552 def rev(node):
552 def rev(node):
553 """Obtain the revision number given a node.
553 """Obtain the revision number given a node.
554
554
555 Raises ``error.LookupError`` if the node is not known.
555 Raises ``error.LookupError`` if the node is not known.
556 """
556 """
557
557
558 def node(rev):
558 def node(rev):
559 """Obtain the node value given a revision number.
559 """Obtain the node value given a revision number.
560
560
561 Raises ``IndexError`` if the node is not known.
561 Raises ``IndexError`` if the node is not known.
562 """
562 """
563
563
564 def lookup(node):
564 def lookup(node):
565 """Attempt to resolve a value to a node.
565 """Attempt to resolve a value to a node.
566
566
567 Value can be a binary node, hex node, revision number, or a string
567 Value can be a binary node, hex node, revision number, or a string
568 that can be converted to an integer.
568 that can be converted to an integer.
569
569
570 Raises ``error.LookupError`` if a node could not be resolved.
570 Raises ``error.LookupError`` if a node could not be resolved.
571 """
571 """
572
572
573 def linkrev(rev):
573 def linkrev(rev):
574 """Obtain the changeset revision number a revision is linked to."""
574 """Obtain the changeset revision number a revision is linked to."""
575
575
576 def iscensored(rev):
576 def iscensored(rev):
577 """Return whether a revision's content has been censored."""
577 """Return whether a revision's content has been censored."""
578
578
579 def commonancestorsheads(node1, node2):
579 def commonancestorsheads(node1, node2):
580 """Obtain an iterable of nodes containing heads of common ancestors.
580 """Obtain an iterable of nodes containing heads of common ancestors.
581
581
582 See ``ancestor.commonancestorsheads()``.
582 See ``ancestor.commonancestorsheads()``.
583 """
583 """
584
584
585 def descendants(revs):
585 def descendants(revs):
586 """Obtain descendant revision numbers for a set of revision numbers.
586 """Obtain descendant revision numbers for a set of revision numbers.
587
587
588 If ``nullrev`` is in the set, this is equivalent to ``revs()``.
588 If ``nullrev`` is in the set, this is equivalent to ``revs()``.
589 """
589 """
590
590
591 def heads(start=None, stop=None):
591 def heads(start=None, stop=None):
592 """Obtain a list of nodes that are DAG heads, with control.
592 """Obtain a list of nodes that are DAG heads, with control.
593
593
594 The set of revisions examined can be limited by specifying
594 The set of revisions examined can be limited by specifying
595 ``start`` and ``stop``. ``start`` is a node. ``stop`` is an
595 ``start`` and ``stop``. ``start`` is a node. ``stop`` is an
596 iterable of nodes. DAG traversal starts at earlier revision
596 iterable of nodes. DAG traversal starts at earlier revision
597 ``start`` and iterates forward until any node in ``stop`` is
597 ``start`` and iterates forward until any node in ``stop`` is
598 encountered.
598 encountered.
599 """
599 """
600
600
601 def children(node):
601 def children(node):
602 """Obtain nodes that are children of a node.
602 """Obtain nodes that are children of a node.
603
603
604 Returns a list of nodes.
604 Returns a list of nodes.
605 """
605 """
606
606
607
607
608 class ifiledata(interfaceutil.Interface):
608 class ifiledata(interfaceutil.Interface):
609 """Storage interface for data storage of a specific file.
609 """Storage interface for data storage of a specific file.
610
610
611 This complements ``ifileindex`` and provides an interface for accessing
611 This complements ``ifileindex`` and provides an interface for accessing
612 data for a tracked file.
612 data for a tracked file.
613 """
613 """
614
614
615 def size(rev):
615 def size(rev):
616 """Obtain the fulltext size of file data.
616 """Obtain the fulltext size of file data.
617
617
618 Any metadata is excluded from size measurements.
618 Any metadata is excluded from size measurements.
619 """
619 """
620
620
621 def revision(node, raw=False):
621 def revision(node, raw=False):
622 """"Obtain fulltext data for a node.
622 """"Obtain fulltext data for a node.
623
623
624 By default, any storage transformations are applied before the data
624 By default, any storage transformations are applied before the data
625 is returned. If ``raw`` is True, non-raw storage transformations
625 is returned. If ``raw`` is True, non-raw storage transformations
626 are not applied.
626 are not applied.
627
627
628 The fulltext data may contain a header containing metadata. Most
628 The fulltext data may contain a header containing metadata. Most
629 consumers should use ``read()`` to obtain the actual file data.
629 consumers should use ``read()`` to obtain the actual file data.
630 """
630 """
631
631
632 def rawdata(node):
632 def rawdata(node):
633 """Obtain raw data for a node.
633 """Obtain raw data for a node.
634 """
634 """
635
635
636 def read(node):
636 def read(node):
637 """Resolve file fulltext data.
637 """Resolve file fulltext data.
638
638
639 This is similar to ``revision()`` except any metadata in the data
639 This is similar to ``revision()`` except any metadata in the data
640 headers is stripped.
640 headers is stripped.
641 """
641 """
642
642
643 def renamed(node):
643 def renamed(node):
644 """Obtain copy metadata for a node.
644 """Obtain copy metadata for a node.
645
645
646 Returns ``False`` if no copy metadata is stored or a 2-tuple of
646 Returns ``False`` if no copy metadata is stored or a 2-tuple of
647 (path, node) from which this revision was copied.
647 (path, node) from which this revision was copied.
648 """
648 """
649
649
650 def cmp(node, fulltext):
650 def cmp(node, fulltext):
651 """Compare fulltext to another revision.
651 """Compare fulltext to another revision.
652
652
653 Returns True if the fulltext is different from what is stored.
653 Returns True if the fulltext is different from what is stored.
654
654
655 This takes copy metadata into account.
655 This takes copy metadata into account.
656
656
657 TODO better document the copy metadata and censoring logic.
657 TODO better document the copy metadata and censoring logic.
658 """
658 """
659
659
660 def emitrevisions(
660 def emitrevisions(
661 nodes,
661 nodes,
662 nodesorder=None,
662 nodesorder=None,
663 revisiondata=False,
663 revisiondata=False,
664 assumehaveparentrevisions=False,
664 assumehaveparentrevisions=False,
665 deltamode=CG_DELTAMODE_STD,
665 deltamode=CG_DELTAMODE_STD,
666 ):
666 ):
667 """Produce ``irevisiondelta`` for revisions.
667 """Produce ``irevisiondelta`` for revisions.
668
668
669 Given an iterable of nodes, emits objects conforming to the
669 Given an iterable of nodes, emits objects conforming to the
670 ``irevisiondelta`` interface that describe revisions in storage.
670 ``irevisiondelta`` interface that describe revisions in storage.
671
671
672 This method is a generator.
672 This method is a generator.
673
673
674 The input nodes may be unordered. Implementations must ensure that a
674 The input nodes may be unordered. Implementations must ensure that a
675 node's parents are emitted before the node itself. Transitively, this
675 node's parents are emitted before the node itself. Transitively, this
676 means that a node may only be emitted once all its ancestors in
676 means that a node may only be emitted once all its ancestors in
677 ``nodes`` have also been emitted.
677 ``nodes`` have also been emitted.
678
678
679 By default, emits "index" data (the ``node``, ``p1node``, and
679 By default, emits "index" data (the ``node``, ``p1node``, and
680 ``p2node`` attributes). If ``revisiondata`` is set, revision data
680 ``p2node`` attributes). If ``revisiondata`` is set, revision data
681 will also be present on the emitted objects.
681 will also be present on the emitted objects.
682
682
683 With default argument values, implementations can choose to emit
683 With default argument values, implementations can choose to emit
684 either fulltext revision data or a delta. When emitting deltas,
684 either fulltext revision data or a delta. When emitting deltas,
685 implementations must consider whether the delta's base revision
685 implementations must consider whether the delta's base revision
686 fulltext is available to the receiver.
686 fulltext is available to the receiver.
687
687
688 The base revision fulltext is guaranteed to be available if any of
688 The base revision fulltext is guaranteed to be available if any of
689 the following are met:
689 the following are met:
690
690
691 * Its fulltext revision was emitted by this method call.
691 * Its fulltext revision was emitted by this method call.
692 * A delta for that revision was emitted by this method call.
692 * A delta for that revision was emitted by this method call.
693 * ``assumehaveparentrevisions`` is True and the base revision is a
693 * ``assumehaveparentrevisions`` is True and the base revision is a
694 parent of the node.
694 parent of the node.
695
695
696 ``nodesorder`` can be used to control the order that revisions are
696 ``nodesorder`` can be used to control the order that revisions are
697 emitted. By default, revisions can be reordered as long as they are
697 emitted. By default, revisions can be reordered as long as they are
698 in DAG topological order (see above). If the value is ``nodes``,
698 in DAG topological order (see above). If the value is ``nodes``,
699 the iteration order from ``nodes`` should be used. If the value is
699 the iteration order from ``nodes`` should be used. If the value is
700 ``storage``, then the native order from the backing storage layer
700 ``storage``, then the native order from the backing storage layer
701 is used. (Not all storage layers will have strong ordering and behavior
701 is used. (Not all storage layers will have strong ordering and behavior
702 of this mode is storage-dependent.) ``nodes`` ordering can force
702 of this mode is storage-dependent.) ``nodes`` ordering can force
703 revisions to be emitted before their ancestors, so consumers should
703 revisions to be emitted before their ancestors, so consumers should
704 use it with care.
704 use it with care.
705
705
706 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
706 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
707 be set and it is the caller's responsibility to resolve it, if needed.
707 be set and it is the caller's responsibility to resolve it, if needed.
708
708
709 If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested,
709 If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested,
710 all revision data should be emitted as deltas against the revision
710 all revision data should be emitted as deltas against the revision
711 emitted just prior. The initial revision should be a delta against its
711 emitted just prior. The initial revision should be a delta against its
712 1st parent.
712 1st parent.
713 """
713 """
714
714
715
715
716 class ifilemutation(interfaceutil.Interface):
716 class ifilemutation(interfaceutil.Interface):
717 """Storage interface for mutation events of a tracked file."""
717 """Storage interface for mutation events of a tracked file."""
718
718
719 def add(filedata, meta, transaction, linkrev, p1, p2):
719 def add(filedata, meta, transaction, linkrev, p1, p2):
720 """Add a new revision to the store.
720 """Add a new revision to the store.
721
721
722 Takes file data, dictionary of metadata, a transaction, linkrev,
722 Takes file data, dictionary of metadata, a transaction, linkrev,
723 and parent nodes.
723 and parent nodes.
724
724
725 Returns the node that was added.
725 Returns the node that was added.
726
726
727 May no-op if a revision matching the supplied data is already stored.
727 May no-op if a revision matching the supplied data is already stored.
728 """
728 """
729
729
730 def addrevision(
730 def addrevision(
731 revisiondata,
731 revisiondata,
732 transaction,
732 transaction,
733 linkrev,
733 linkrev,
734 p1,
734 p1,
735 p2,
735 p2,
736 node=None,
736 node=None,
737 flags=0,
737 flags=0,
738 cachedelta=None,
738 cachedelta=None,
739 ):
739 ):
740 """Add a new revision to the store.
740 """Add a new revision to the store.
741
741
742 This is similar to ``add()`` except it operates at a lower level.
742 This is similar to ``add()`` except it operates at a lower level.
743
743
744 The data passed in already contains a metadata header, if any.
744 The data passed in already contains a metadata header, if any.
745
745
746 ``node`` and ``flags`` can be used to define the expected node and
746 ``node`` and ``flags`` can be used to define the expected node and
747 the flags to use with storage. ``flags`` is a bitwise value composed
747 the flags to use with storage. ``flags`` is a bitwise value composed
748 of the various ``REVISION_FLAG_*`` constants.
748 of the various ``REVISION_FLAG_*`` constants.
749
749
750 ``add()`` is usually called when adding files from e.g. the working
750 ``add()`` is usually called when adding files from e.g. the working
751 directory. ``addrevision()`` is often called by ``add()`` and for
751 directory. ``addrevision()`` is often called by ``add()`` and for
752 scenarios where revision data has already been computed, such as when
752 scenarios where revision data has already been computed, such as when
753 applying raw data from a peer repo.
753 applying raw data from a peer repo.
754 """
754 """
755
755
756 def addgroup(
756 def addgroup(
757 deltas,
757 deltas,
758 linkmapper,
758 linkmapper,
759 transaction,
759 transaction,
760 addrevisioncb=None,
760 addrevisioncb=None,
761 maybemissingparents=False,
761 maybemissingparents=False,
762 ):
762 ):
763 """Process a series of deltas for storage.
763 """Process a series of deltas for storage.
764
764
765 ``deltas`` is an iterable of 7-tuples of
765 ``deltas`` is an iterable of 7-tuples of
766 (node, p1, p2, linknode, deltabase, delta, flags) defining revisions
766 (node, p1, p2, linknode, deltabase, delta, flags) defining revisions
767 to add.
767 to add.
768
768
769 The ``delta`` field contains ``mpatch`` data to apply to a base
769 The ``delta`` field contains ``mpatch`` data to apply to a base
770 revision, identified by ``deltabase``. The base node can be
770 revision, identified by ``deltabase``. The base node can be
771 ``nullid``, in which case the header from the delta can be ignored
771 ``nullid``, in which case the header from the delta can be ignored
772 and the delta used as the fulltext.
772 and the delta used as the fulltext.
773
773
774 ``addrevisioncb`` should be called for each node as it is committed.
774 ``addrevisioncb`` should be called for each node as it is committed.
775
775
776 ``maybemissingparents`` is a bool indicating whether the incoming
776 ``maybemissingparents`` is a bool indicating whether the incoming
777 data may reference parents/ancestor revisions that aren't present.
777 data may reference parents/ancestor revisions that aren't present.
778 This flag is set when receiving data into a "shallow" store that
778 This flag is set when receiving data into a "shallow" store that
779 doesn't hold all history.
779 doesn't hold all history.
780
780
781 Returns a list of nodes that were processed. A node will be in the list
781 Returns a list of nodes that were processed. A node will be in the list
782 even if it existed in the store previously.
782 even if it existed in the store previously.
783 """
783 """
784
784
785 def censorrevision(tr, node, tombstone=b''):
785 def censorrevision(tr, node, tombstone=b''):
786 """Remove the content of a single revision.
786 """Remove the content of a single revision.
787
787
788 The specified ``node`` will have its content purged from storage.
788 The specified ``node`` will have its content purged from storage.
789 Future attempts to access the revision data for this node will
789 Future attempts to access the revision data for this node will
790 result in failure.
790 result in failure.
791
791
792 A ``tombstone`` message can optionally be stored. This message may be
792 A ``tombstone`` message can optionally be stored. This message may be
793 displayed to users when they attempt to access the missing revision
793 displayed to users when they attempt to access the missing revision
794 data.
794 data.
795
795
796 Storage backends may have stored deltas against the previous content
796 Storage backends may have stored deltas against the previous content
797 in this revision. As part of censoring a revision, these storage
797 in this revision. As part of censoring a revision, these storage
798 backends are expected to rewrite any internally stored deltas such
798 backends are expected to rewrite any internally stored deltas such
799 that they no longer reference the deleted content.
799 that they no longer reference the deleted content.
800 """
800 """
801
801
802 def getstrippoint(minlink):
802 def getstrippoint(minlink):
803 """Find the minimum revision that must be stripped to strip a linkrev.
803 """Find the minimum revision that must be stripped to strip a linkrev.
804
804
805 Returns a 2-tuple containing the minimum revision number and a set
805 Returns a 2-tuple containing the minimum revision number and a set
806 of all revisions numbers that would be broken by this strip.
806 of all revisions numbers that would be broken by this strip.
807
807
808 TODO this is highly revlog centric and should be abstracted into
808 TODO this is highly revlog centric and should be abstracted into
809 a higher-level deletion API. ``repair.strip()`` relies on this.
809 a higher-level deletion API. ``repair.strip()`` relies on this.
810 """
810 """
811
811
812 def strip(minlink, transaction):
812 def strip(minlink, transaction):
813 """Remove storage of items starting at a linkrev.
813 """Remove storage of items starting at a linkrev.
814
814
815 This uses ``getstrippoint()`` to determine the first node to remove.
815 This uses ``getstrippoint()`` to determine the first node to remove.
816 Then it effectively truncates storage for all revisions after that.
816 Then it effectively truncates storage for all revisions after that.
817
817
818 TODO this is highly revlog centric and should be abstracted into a
818 TODO this is highly revlog centric and should be abstracted into a
819 higher-level deletion API.
819 higher-level deletion API.
820 """
820 """
821
821
822
822
823 class ifilestorage(ifileindex, ifiledata, ifilemutation):
823 class ifilestorage(ifileindex, ifiledata, ifilemutation):
824 """Complete storage interface for a single tracked file."""
824 """Complete storage interface for a single tracked file."""
825
825
826 def files():
826 def files():
827 """Obtain paths that are backing storage for this file.
827 """Obtain paths that are backing storage for this file.
828
828
829 TODO this is used heavily by verify code and there should probably
829 TODO this is used heavily by verify code and there should probably
830 be a better API for that.
830 be a better API for that.
831 """
831 """
832
832
833 def storageinfo(
833 def storageinfo(
834 exclusivefiles=False,
834 exclusivefiles=False,
835 sharedfiles=False,
835 sharedfiles=False,
836 revisionscount=False,
836 revisionscount=False,
837 trackedsize=False,
837 trackedsize=False,
838 storedsize=False,
838 storedsize=False,
839 ):
839 ):
840 """Obtain information about storage for this file's data.
840 """Obtain information about storage for this file's data.
841
841
842 Returns a dict describing storage for this tracked path. The keys
842 Returns a dict describing storage for this tracked path. The keys
843 in the dict map to arguments of the same. The arguments are bools
843 in the dict map to arguments of the same. The arguments are bools
844 indicating whether to calculate and obtain that data.
844 indicating whether to calculate and obtain that data.
845
845
846 exclusivefiles
846 exclusivefiles
847 Iterable of (vfs, path) describing files that are exclusively
847 Iterable of (vfs, path) describing files that are exclusively
848 used to back storage for this tracked path.
848 used to back storage for this tracked path.
849
849
850 sharedfiles
850 sharedfiles
851 Iterable of (vfs, path) describing files that are used to back
851 Iterable of (vfs, path) describing files that are used to back
852 storage for this tracked path. Those files may also provide storage
852 storage for this tracked path. Those files may also provide storage
853 for other stored entities.
853 for other stored entities.
854
854
855 revisionscount
855 revisionscount
856 Number of revisions available for retrieval.
856 Number of revisions available for retrieval.
857
857
858 trackedsize
858 trackedsize
859 Total size in bytes of all tracked revisions. This is a sum of the
859 Total size in bytes of all tracked revisions. This is a sum of the
860 length of the fulltext of all revisions.
860 length of the fulltext of all revisions.
861
861
862 storedsize
862 storedsize
863 Total size in bytes used to store data for all tracked revisions.
863 Total size in bytes used to store data for all tracked revisions.
864 This is commonly less than ``trackedsize`` due to internal usage
864 This is commonly less than ``trackedsize`` due to internal usage
865 of deltas rather than fulltext revisions.
865 of deltas rather than fulltext revisions.
866
866
867 Not all storage backends may support all queries are have a reasonable
867 Not all storage backends may support all queries are have a reasonable
868 value to use. In that case, the value should be set to ``None`` and
868 value to use. In that case, the value should be set to ``None`` and
869 callers are expected to handle this special value.
869 callers are expected to handle this special value.
870 """
870 """
871
871
872 def verifyintegrity(state):
872 def verifyintegrity(state):
873 """Verifies the integrity of file storage.
873 """Verifies the integrity of file storage.
874
874
875 ``state`` is a dict holding state of the verifier process. It can be
875 ``state`` is a dict holding state of the verifier process. It can be
876 used to communicate data between invocations of multiple storage
876 used to communicate data between invocations of multiple storage
877 primitives.
877 primitives.
878
878
879 If individual revisions cannot have their revision content resolved,
879 If individual revisions cannot have their revision content resolved,
880 the method is expected to set the ``skipread`` key to a set of nodes
880 the method is expected to set the ``skipread`` key to a set of nodes
881 that encountered problems. If set, the method can also add the node(s)
881 that encountered problems. If set, the method can also add the node(s)
882 to ``safe_renamed`` in order to indicate nodes that may perform the
882 to ``safe_renamed`` in order to indicate nodes that may perform the
883 rename checks with currently accessible data.
883 rename checks with currently accessible data.
884
884
885 The method yields objects conforming to the ``iverifyproblem``
885 The method yields objects conforming to the ``iverifyproblem``
886 interface.
886 interface.
887 """
887 """
888
888
889
889
890 class idirs(interfaceutil.Interface):
890 class idirs(interfaceutil.Interface):
891 """Interface representing a collection of directories from paths.
891 """Interface representing a collection of directories from paths.
892
892
893 This interface is essentially a derived data structure representing
893 This interface is essentially a derived data structure representing
894 directories from a collection of paths.
894 directories from a collection of paths.
895 """
895 """
896
896
897 def addpath(path):
897 def addpath(path):
898 """Add a path to the collection.
898 """Add a path to the collection.
899
899
900 All directories in the path will be added to the collection.
900 All directories in the path will be added to the collection.
901 """
901 """
902
902
903 def delpath(path):
903 def delpath(path):
904 """Remove a path from the collection.
904 """Remove a path from the collection.
905
905
906 If the removal was the last path in a particular directory, the
906 If the removal was the last path in a particular directory, the
907 directory is removed from the collection.
907 directory is removed from the collection.
908 """
908 """
909
909
910 def __iter__():
910 def __iter__():
911 """Iterate over the directories in this collection of paths."""
911 """Iterate over the directories in this collection of paths."""
912
912
913 def __contains__(path):
913 def __contains__(path):
914 """Whether a specific directory is in this collection."""
914 """Whether a specific directory is in this collection."""
915
915
916
916
917 class imanifestdict(interfaceutil.Interface):
917 class imanifestdict(interfaceutil.Interface):
918 """Interface representing a manifest data structure.
918 """Interface representing a manifest data structure.
919
919
920 A manifest is effectively a dict mapping paths to entries. Each entry
920 A manifest is effectively a dict mapping paths to entries. Each entry
921 consists of a binary node and extra flags affecting that entry.
921 consists of a binary node and extra flags affecting that entry.
922 """
922 """
923
923
924 def __getitem__(path):
924 def __getitem__(path):
925 """Returns the binary node value for a path in the manifest.
925 """Returns the binary node value for a path in the manifest.
926
926
927 Raises ``KeyError`` if the path does not exist in the manifest.
927 Raises ``KeyError`` if the path does not exist in the manifest.
928
928
929 Equivalent to ``self.find(path)[0]``.
929 Equivalent to ``self.find(path)[0]``.
930 """
930 """
931
931
932 def find(path):
932 def find(path):
933 """Returns the entry for a path in the manifest.
933 """Returns the entry for a path in the manifest.
934
934
935 Returns a 2-tuple of (node, flags).
935 Returns a 2-tuple of (node, flags).
936
936
937 Raises ``KeyError`` if the path does not exist in the manifest.
937 Raises ``KeyError`` if the path does not exist in the manifest.
938 """
938 """
939
939
940 def __len__():
940 def __len__():
941 """Return the number of entries in the manifest."""
941 """Return the number of entries in the manifest."""
942
942
943 def __nonzero__():
943 def __nonzero__():
944 """Returns True if the manifest has entries, False otherwise."""
944 """Returns True if the manifest has entries, False otherwise."""
945
945
946 __bool__ = __nonzero__
946 __bool__ = __nonzero__
947
947
948 def __setitem__(path, node):
948 def __setitem__(path, node):
949 """Define the node value for a path in the manifest.
949 """Define the node value for a path in the manifest.
950
950
951 If the path is already in the manifest, its flags will be copied to
951 If the path is already in the manifest, its flags will be copied to
952 the new entry.
952 the new entry.
953 """
953 """
954
954
955 def __contains__(path):
955 def __contains__(path):
956 """Whether a path exists in the manifest."""
956 """Whether a path exists in the manifest."""
957
957
958 def __delitem__(path):
958 def __delitem__(path):
959 """Remove a path from the manifest.
959 """Remove a path from the manifest.
960
960
961 Raises ``KeyError`` if the path is not in the manifest.
961 Raises ``KeyError`` if the path is not in the manifest.
962 """
962 """
963
963
964 def __iter__():
964 def __iter__():
965 """Iterate over paths in the manifest."""
965 """Iterate over paths in the manifest."""
966
966
967 def iterkeys():
967 def iterkeys():
968 """Iterate over paths in the manifest."""
968 """Iterate over paths in the manifest."""
969
969
970 def keys():
970 def keys():
971 """Obtain a list of paths in the manifest."""
971 """Obtain a list of paths in the manifest."""
972
972
973 def filesnotin(other, match=None):
973 def filesnotin(other, match=None):
974 """Obtain the set of paths in this manifest but not in another.
974 """Obtain the set of paths in this manifest but not in another.
975
975
976 ``match`` is an optional matcher function to be applied to both
976 ``match`` is an optional matcher function to be applied to both
977 manifests.
977 manifests.
978
978
979 Returns a set of paths.
979 Returns a set of paths.
980 """
980 """
981
981
982 def dirs():
982 def dirs():
983 """Returns an object implementing the ``idirs`` interface."""
983 """Returns an object implementing the ``idirs`` interface."""
984
984
985 def hasdir(dir):
985 def hasdir(dir):
986 """Returns a bool indicating if a directory is in this manifest."""
986 """Returns a bool indicating if a directory is in this manifest."""
987
987
988 def walk(match):
988 def walk(match):
989 """Generator of paths in manifest satisfying a matcher.
989 """Generator of paths in manifest satisfying a matcher.
990
990
991 If the matcher has explicit files listed and they don't exist in
991 If the matcher has explicit files listed and they don't exist in
992 the manifest, ``match.bad()`` is called for each missing file.
992 the manifest, ``match.bad()`` is called for each missing file.
993 """
993 """
994
994
995 def diff(other, match=None, clean=False):
995 def diff(other, match=None, clean=False):
996 """Find differences between this manifest and another.
996 """Find differences between this manifest and another.
997
997
998 This manifest is compared to ``other``.
998 This manifest is compared to ``other``.
999
999
1000 If ``match`` is provided, the two manifests are filtered against this
1000 If ``match`` is provided, the two manifests are filtered against this
1001 matcher and only entries satisfying the matcher are compared.
1001 matcher and only entries satisfying the matcher are compared.
1002
1002
1003 If ``clean`` is True, unchanged files are included in the returned
1003 If ``clean`` is True, unchanged files are included in the returned
1004 object.
1004 object.
1005
1005
1006 Returns a dict with paths as keys and values of 2-tuples of 2-tuples of
1006 Returns a dict with paths as keys and values of 2-tuples of 2-tuples of
1007 the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)``
1007 the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)``
1008 represents the node and flags for this manifest and ``(node2, flag2)``
1008 represents the node and flags for this manifest and ``(node2, flag2)``
1009 are the same for the other manifest.
1009 are the same for the other manifest.
1010 """
1010 """
1011
1011
1012 def setflag(path, flag):
1012 def setflag(path, flag):
1013 """Set the flag value for a given path.
1013 """Set the flag value for a given path.
1014
1014
1015 Raises ``KeyError`` if the path is not already in the manifest.
1015 Raises ``KeyError`` if the path is not already in the manifest.
1016 """
1016 """
1017
1017
1018 def get(path, default=None):
1018 def get(path, default=None):
1019 """Obtain the node value for a path or a default value if missing."""
1019 """Obtain the node value for a path or a default value if missing."""
1020
1020
1021 def flags(path):
1021 def flags(path):
1022 """Return the flags value for a path (default: empty bytestring)."""
1022 """Return the flags value for a path (default: empty bytestring)."""
1023
1023
1024 def copy():
1024 def copy():
1025 """Return a copy of this manifest."""
1025 """Return a copy of this manifest."""
1026
1026
1027 def items():
1027 def items():
1028 """Returns an iterable of (path, node) for items in this manifest."""
1028 """Returns an iterable of (path, node) for items in this manifest."""
1029
1029
1030 def iteritems():
1030 def iteritems():
1031 """Identical to items()."""
1031 """Identical to items()."""
1032
1032
1033 def iterentries():
1033 def iterentries():
1034 """Returns an iterable of (path, node, flags) for this manifest.
1034 """Returns an iterable of (path, node, flags) for this manifest.
1035
1035
1036 Similar to ``iteritems()`` except items are a 3-tuple and include
1036 Similar to ``iteritems()`` except items are a 3-tuple and include
1037 flags.
1037 flags.
1038 """
1038 """
1039
1039
1040 def text():
1040 def text():
1041 """Obtain the raw data representation for this manifest.
1041 """Obtain the raw data representation for this manifest.
1042
1042
1043 Result is used to create a manifest revision.
1043 Result is used to create a manifest revision.
1044 """
1044 """
1045
1045
1046 def fastdelta(base, changes):
1046 def fastdelta(base, changes):
1047 """Obtain a delta between this manifest and another given changes.
1047 """Obtain a delta between this manifest and another given changes.
1048
1048
1049 ``base`` in the raw data representation for another manifest.
1049 ``base`` in the raw data representation for another manifest.
1050
1050
1051 ``changes`` is an iterable of ``(path, to_delete)``.
1051 ``changes`` is an iterable of ``(path, to_delete)``.
1052
1052
1053 Returns a 2-tuple containing ``bytearray(self.text())`` and the
1053 Returns a 2-tuple containing ``bytearray(self.text())`` and the
1054 delta between ``base`` and this manifest.
1054 delta between ``base`` and this manifest.
1055
1056 If this manifest implementation can't support ``fastdelta()``,
1057 raise ``mercurial.manifest.FastdeltaUnavailable``.
1055 """
1058 """
1056
1059
1057
1060
1058 class imanifestrevisionbase(interfaceutil.Interface):
1061 class imanifestrevisionbase(interfaceutil.Interface):
1059 """Base interface representing a single revision of a manifest.
1062 """Base interface representing a single revision of a manifest.
1060
1063
1061 Should not be used as a primary interface: should always be inherited
1064 Should not be used as a primary interface: should always be inherited
1062 as part of a larger interface.
1065 as part of a larger interface.
1063 """
1066 """
1064
1067
1065 def copy():
1068 def copy():
1066 """Obtain a copy of this manifest instance.
1069 """Obtain a copy of this manifest instance.
1067
1070
1068 Returns an object conforming to the ``imanifestrevisionwritable``
1071 Returns an object conforming to the ``imanifestrevisionwritable``
1069 interface. The instance will be associated with the same
1072 interface. The instance will be associated with the same
1070 ``imanifestlog`` collection as this instance.
1073 ``imanifestlog`` collection as this instance.
1071 """
1074 """
1072
1075
1073 def read():
1076 def read():
1074 """Obtain the parsed manifest data structure.
1077 """Obtain the parsed manifest data structure.
1075
1078
1076 The returned object conforms to the ``imanifestdict`` interface.
1079 The returned object conforms to the ``imanifestdict`` interface.
1077 """
1080 """
1078
1081
1079
1082
1080 class imanifestrevisionstored(imanifestrevisionbase):
1083 class imanifestrevisionstored(imanifestrevisionbase):
1081 """Interface representing a manifest revision committed to storage."""
1084 """Interface representing a manifest revision committed to storage."""
1082
1085
1083 def node():
1086 def node():
1084 """The binary node for this manifest."""
1087 """The binary node for this manifest."""
1085
1088
1086 parents = interfaceutil.Attribute(
1089 parents = interfaceutil.Attribute(
1087 """List of binary nodes that are parents for this manifest revision."""
1090 """List of binary nodes that are parents for this manifest revision."""
1088 )
1091 )
1089
1092
1090 def readdelta(shallow=False):
1093 def readdelta(shallow=False):
1091 """Obtain the manifest data structure representing changes from parent.
1094 """Obtain the manifest data structure representing changes from parent.
1092
1095
1093 This manifest is compared to its 1st parent. A new manifest representing
1096 This manifest is compared to its 1st parent. A new manifest representing
1094 those differences is constructed.
1097 those differences is constructed.
1095
1098
1096 The returned object conforms to the ``imanifestdict`` interface.
1099 The returned object conforms to the ``imanifestdict`` interface.
1097 """
1100 """
1098
1101
1099 def readfast(shallow=False):
1102 def readfast(shallow=False):
1100 """Calls either ``read()`` or ``readdelta()``.
1103 """Calls either ``read()`` or ``readdelta()``.
1101
1104
1102 The faster of the two options is called.
1105 The faster of the two options is called.
1103 """
1106 """
1104
1107
1105 def find(key):
1108 def find(key):
1106 """Calls self.read().find(key)``.
1109 """Calls self.read().find(key)``.
1107
1110
1108 Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``.
1111 Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``.
1109 """
1112 """
1110
1113
1111
1114
1112 class imanifestrevisionwritable(imanifestrevisionbase):
1115 class imanifestrevisionwritable(imanifestrevisionbase):
1113 """Interface representing a manifest revision that can be committed."""
1116 """Interface representing a manifest revision that can be committed."""
1114
1117
1115 def write(transaction, linkrev, p1node, p2node, added, removed, match=None):
1118 def write(transaction, linkrev, p1node, p2node, added, removed, match=None):
1116 """Add this revision to storage.
1119 """Add this revision to storage.
1117
1120
1118 Takes a transaction object, the changeset revision number it will
1121 Takes a transaction object, the changeset revision number it will
1119 be associated with, its parent nodes, and lists of added and
1122 be associated with, its parent nodes, and lists of added and
1120 removed paths.
1123 removed paths.
1121
1124
1122 If match is provided, storage can choose not to inspect or write out
1125 If match is provided, storage can choose not to inspect or write out
1123 items that do not match. Storage is still required to be able to provide
1126 items that do not match. Storage is still required to be able to provide
1124 the full manifest in the future for any directories written (these
1127 the full manifest in the future for any directories written (these
1125 manifests should not be "narrowed on disk").
1128 manifests should not be "narrowed on disk").
1126
1129
1127 Returns the binary node of the created revision.
1130 Returns the binary node of the created revision.
1128 """
1131 """
1129
1132
1130
1133
1131 class imanifeststorage(interfaceutil.Interface):
1134 class imanifeststorage(interfaceutil.Interface):
1132 """Storage interface for manifest data."""
1135 """Storage interface for manifest data."""
1133
1136
1134 tree = interfaceutil.Attribute(
1137 tree = interfaceutil.Attribute(
1135 """The path to the directory this manifest tracks.
1138 """The path to the directory this manifest tracks.
1136
1139
1137 The empty bytestring represents the root manifest.
1140 The empty bytestring represents the root manifest.
1138 """
1141 """
1139 )
1142 )
1140
1143
1141 index = interfaceutil.Attribute(
1144 index = interfaceutil.Attribute(
1142 """An ``ifilerevisionssequence`` instance."""
1145 """An ``ifilerevisionssequence`` instance."""
1143 )
1146 )
1144
1147
1145 indexfile = interfaceutil.Attribute(
1148 indexfile = interfaceutil.Attribute(
1146 """Path of revlog index file.
1149 """Path of revlog index file.
1147
1150
1148 TODO this is revlog specific and should not be exposed.
1151 TODO this is revlog specific and should not be exposed.
1149 """
1152 """
1150 )
1153 )
1151
1154
1152 opener = interfaceutil.Attribute(
1155 opener = interfaceutil.Attribute(
1153 """VFS opener to use to access underlying files used for storage.
1156 """VFS opener to use to access underlying files used for storage.
1154
1157
1155 TODO this is revlog specific and should not be exposed.
1158 TODO this is revlog specific and should not be exposed.
1156 """
1159 """
1157 )
1160 )
1158
1161
1159 version = interfaceutil.Attribute(
1162 version = interfaceutil.Attribute(
1160 """Revlog version number.
1163 """Revlog version number.
1161
1164
1162 TODO this is revlog specific and should not be exposed.
1165 TODO this is revlog specific and should not be exposed.
1163 """
1166 """
1164 )
1167 )
1165
1168
1166 _generaldelta = interfaceutil.Attribute(
1169 _generaldelta = interfaceutil.Attribute(
1167 """Whether generaldelta storage is being used.
1170 """Whether generaldelta storage is being used.
1168
1171
1169 TODO this is revlog specific and should not be exposed.
1172 TODO this is revlog specific and should not be exposed.
1170 """
1173 """
1171 )
1174 )
1172
1175
1173 fulltextcache = interfaceutil.Attribute(
1176 fulltextcache = interfaceutil.Attribute(
1174 """Dict with cache of fulltexts.
1177 """Dict with cache of fulltexts.
1175
1178
1176 TODO this doesn't feel appropriate for the storage interface.
1179 TODO this doesn't feel appropriate for the storage interface.
1177 """
1180 """
1178 )
1181 )
1179
1182
1180 def __len__():
1183 def __len__():
1181 """Obtain the number of revisions stored for this manifest."""
1184 """Obtain the number of revisions stored for this manifest."""
1182
1185
1183 def __iter__():
1186 def __iter__():
1184 """Iterate over revision numbers for this manifest."""
1187 """Iterate over revision numbers for this manifest."""
1185
1188
1186 def rev(node):
1189 def rev(node):
1187 """Obtain the revision number given a binary node.
1190 """Obtain the revision number given a binary node.
1188
1191
1189 Raises ``error.LookupError`` if the node is not known.
1192 Raises ``error.LookupError`` if the node is not known.
1190 """
1193 """
1191
1194
1192 def node(rev):
1195 def node(rev):
1193 """Obtain the node value given a revision number.
1196 """Obtain the node value given a revision number.
1194
1197
1195 Raises ``error.LookupError`` if the revision is not known.
1198 Raises ``error.LookupError`` if the revision is not known.
1196 """
1199 """
1197
1200
1198 def lookup(value):
1201 def lookup(value):
1199 """Attempt to resolve a value to a node.
1202 """Attempt to resolve a value to a node.
1200
1203
1201 Value can be a binary node, hex node, revision number, or a bytes
1204 Value can be a binary node, hex node, revision number, or a bytes
1202 that can be converted to an integer.
1205 that can be converted to an integer.
1203
1206
1204 Raises ``error.LookupError`` if a ndoe could not be resolved.
1207 Raises ``error.LookupError`` if a ndoe could not be resolved.
1205 """
1208 """
1206
1209
1207 def parents(node):
1210 def parents(node):
1208 """Returns a 2-tuple of parent nodes for a node.
1211 """Returns a 2-tuple of parent nodes for a node.
1209
1212
1210 Values will be ``nullid`` if the parent is empty.
1213 Values will be ``nullid`` if the parent is empty.
1211 """
1214 """
1212
1215
1213 def parentrevs(rev):
1216 def parentrevs(rev):
1214 """Like parents() but operates on revision numbers."""
1217 """Like parents() but operates on revision numbers."""
1215
1218
1216 def linkrev(rev):
1219 def linkrev(rev):
1217 """Obtain the changeset revision number a revision is linked to."""
1220 """Obtain the changeset revision number a revision is linked to."""
1218
1221
1219 def revision(node, _df=None, raw=False):
1222 def revision(node, _df=None, raw=False):
1220 """Obtain fulltext data for a node."""
1223 """Obtain fulltext data for a node."""
1221
1224
1222 def rawdata(node, _df=None):
1225 def rawdata(node, _df=None):
1223 """Obtain raw data for a node."""
1226 """Obtain raw data for a node."""
1224
1227
1225 def revdiff(rev1, rev2):
1228 def revdiff(rev1, rev2):
1226 """Obtain a delta between two revision numbers.
1229 """Obtain a delta between two revision numbers.
1227
1230
1228 The returned data is the result of ``bdiff.bdiff()`` on the raw
1231 The returned data is the result of ``bdiff.bdiff()`` on the raw
1229 revision data.
1232 revision data.
1230 """
1233 """
1231
1234
1232 def cmp(node, fulltext):
1235 def cmp(node, fulltext):
1233 """Compare fulltext to another revision.
1236 """Compare fulltext to another revision.
1234
1237
1235 Returns True if the fulltext is different from what is stored.
1238 Returns True if the fulltext is different from what is stored.
1236 """
1239 """
1237
1240
1238 def emitrevisions(
1241 def emitrevisions(
1239 nodes,
1242 nodes,
1240 nodesorder=None,
1243 nodesorder=None,
1241 revisiondata=False,
1244 revisiondata=False,
1242 assumehaveparentrevisions=False,
1245 assumehaveparentrevisions=False,
1243 ):
1246 ):
1244 """Produce ``irevisiondelta`` describing revisions.
1247 """Produce ``irevisiondelta`` describing revisions.
1245
1248
1246 See the documentation for ``ifiledata`` for more.
1249 See the documentation for ``ifiledata`` for more.
1247 """
1250 """
1248
1251
1249 def addgroup(deltas, linkmapper, transaction, addrevisioncb=None):
1252 def addgroup(deltas, linkmapper, transaction, addrevisioncb=None):
1250 """Process a series of deltas for storage.
1253 """Process a series of deltas for storage.
1251
1254
1252 See the documentation in ``ifilemutation`` for more.
1255 See the documentation in ``ifilemutation`` for more.
1253 """
1256 """
1254
1257
1255 def rawsize(rev):
1258 def rawsize(rev):
1256 """Obtain the size of tracked data.
1259 """Obtain the size of tracked data.
1257
1260
1258 Is equivalent to ``len(m.rawdata(node))``.
1261 Is equivalent to ``len(m.rawdata(node))``.
1259
1262
1260 TODO this method is only used by upgrade code and may be removed.
1263 TODO this method is only used by upgrade code and may be removed.
1261 """
1264 """
1262
1265
1263 def getstrippoint(minlink):
1266 def getstrippoint(minlink):
1264 """Find minimum revision that must be stripped to strip a linkrev.
1267 """Find minimum revision that must be stripped to strip a linkrev.
1265
1268
1266 See the documentation in ``ifilemutation`` for more.
1269 See the documentation in ``ifilemutation`` for more.
1267 """
1270 """
1268
1271
1269 def strip(minlink, transaction):
1272 def strip(minlink, transaction):
1270 """Remove storage of items starting at a linkrev.
1273 """Remove storage of items starting at a linkrev.
1271
1274
1272 See the documentation in ``ifilemutation`` for more.
1275 See the documentation in ``ifilemutation`` for more.
1273 """
1276 """
1274
1277
1275 def checksize():
1278 def checksize():
1276 """Obtain the expected sizes of backing files.
1279 """Obtain the expected sizes of backing files.
1277
1280
1278 TODO this is used by verify and it should not be part of the interface.
1281 TODO this is used by verify and it should not be part of the interface.
1279 """
1282 """
1280
1283
1281 def files():
1284 def files():
1282 """Obtain paths that are backing storage for this manifest.
1285 """Obtain paths that are backing storage for this manifest.
1283
1286
1284 TODO this is used by verify and there should probably be a better API
1287 TODO this is used by verify and there should probably be a better API
1285 for this functionality.
1288 for this functionality.
1286 """
1289 """
1287
1290
1288 def deltaparent(rev):
1291 def deltaparent(rev):
1289 """Obtain the revision that a revision is delta'd against.
1292 """Obtain the revision that a revision is delta'd against.
1290
1293
1291 TODO delta encoding is an implementation detail of storage and should
1294 TODO delta encoding is an implementation detail of storage and should
1292 not be exposed to the storage interface.
1295 not be exposed to the storage interface.
1293 """
1296 """
1294
1297
1295 def clone(tr, dest, **kwargs):
1298 def clone(tr, dest, **kwargs):
1296 """Clone this instance to another."""
1299 """Clone this instance to another."""
1297
1300
1298 def clearcaches(clear_persisted_data=False):
1301 def clearcaches(clear_persisted_data=False):
1299 """Clear any caches associated with this instance."""
1302 """Clear any caches associated with this instance."""
1300
1303
1301 def dirlog(d):
1304 def dirlog(d):
1302 """Obtain a manifest storage instance for a tree."""
1305 """Obtain a manifest storage instance for a tree."""
1303
1306
1304 def add(
1307 def add(
1305 m, transaction, link, p1, p2, added, removed, readtree=None, match=None
1308 m, transaction, link, p1, p2, added, removed, readtree=None, match=None
1306 ):
1309 ):
1307 """Add a revision to storage.
1310 """Add a revision to storage.
1308
1311
1309 ``m`` is an object conforming to ``imanifestdict``.
1312 ``m`` is an object conforming to ``imanifestdict``.
1310
1313
1311 ``link`` is the linkrev revision number.
1314 ``link`` is the linkrev revision number.
1312
1315
1313 ``p1`` and ``p2`` are the parent revision numbers.
1316 ``p1`` and ``p2`` are the parent revision numbers.
1314
1317
1315 ``added`` and ``removed`` are iterables of added and removed paths,
1318 ``added`` and ``removed`` are iterables of added and removed paths,
1316 respectively.
1319 respectively.
1317
1320
1318 ``readtree`` is a function that can be used to read the child tree(s)
1321 ``readtree`` is a function that can be used to read the child tree(s)
1319 when recursively writing the full tree structure when using
1322 when recursively writing the full tree structure when using
1320 treemanifets.
1323 treemanifets.
1321
1324
1322 ``match`` is a matcher that can be used to hint to storage that not all
1325 ``match`` is a matcher that can be used to hint to storage that not all
1323 paths must be inspected; this is an optimization and can be safely
1326 paths must be inspected; this is an optimization and can be safely
1324 ignored. Note that the storage must still be able to reproduce a full
1327 ignored. Note that the storage must still be able to reproduce a full
1325 manifest including files that did not match.
1328 manifest including files that did not match.
1326 """
1329 """
1327
1330
1328 def storageinfo(
1331 def storageinfo(
1329 exclusivefiles=False,
1332 exclusivefiles=False,
1330 sharedfiles=False,
1333 sharedfiles=False,
1331 revisionscount=False,
1334 revisionscount=False,
1332 trackedsize=False,
1335 trackedsize=False,
1333 storedsize=False,
1336 storedsize=False,
1334 ):
1337 ):
1335 """Obtain information about storage for this manifest's data.
1338 """Obtain information about storage for this manifest's data.
1336
1339
1337 See ``ifilestorage.storageinfo()`` for a description of this method.
1340 See ``ifilestorage.storageinfo()`` for a description of this method.
1338 This one behaves the same way, except for manifest data.
1341 This one behaves the same way, except for manifest data.
1339 """
1342 """
1340
1343
1341
1344
1342 class imanifestlog(interfaceutil.Interface):
1345 class imanifestlog(interfaceutil.Interface):
1343 """Interface representing a collection of manifest snapshots.
1346 """Interface representing a collection of manifest snapshots.
1344
1347
1345 Represents the root manifest in a repository.
1348 Represents the root manifest in a repository.
1346
1349
1347 Also serves as a means to access nested tree manifests and to cache
1350 Also serves as a means to access nested tree manifests and to cache
1348 tree manifests.
1351 tree manifests.
1349 """
1352 """
1350
1353
1351 def __getitem__(node):
1354 def __getitem__(node):
1352 """Obtain a manifest instance for a given binary node.
1355 """Obtain a manifest instance for a given binary node.
1353
1356
1354 Equivalent to calling ``self.get('', node)``.
1357 Equivalent to calling ``self.get('', node)``.
1355
1358
1356 The returned object conforms to the ``imanifestrevisionstored``
1359 The returned object conforms to the ``imanifestrevisionstored``
1357 interface.
1360 interface.
1358 """
1361 """
1359
1362
1360 def get(tree, node, verify=True):
1363 def get(tree, node, verify=True):
1361 """Retrieve the manifest instance for a given directory and binary node.
1364 """Retrieve the manifest instance for a given directory and binary node.
1362
1365
1363 ``node`` always refers to the node of the root manifest (which will be
1366 ``node`` always refers to the node of the root manifest (which will be
1364 the only manifest if flat manifests are being used).
1367 the only manifest if flat manifests are being used).
1365
1368
1366 If ``tree`` is the empty string, the root manifest is returned.
1369 If ``tree`` is the empty string, the root manifest is returned.
1367 Otherwise the manifest for the specified directory will be returned
1370 Otherwise the manifest for the specified directory will be returned
1368 (requires tree manifests).
1371 (requires tree manifests).
1369
1372
1370 If ``verify`` is True, ``LookupError`` is raised if the node is not
1373 If ``verify`` is True, ``LookupError`` is raised if the node is not
1371 known.
1374 known.
1372
1375
1373 The returned object conforms to the ``imanifestrevisionstored``
1376 The returned object conforms to the ``imanifestrevisionstored``
1374 interface.
1377 interface.
1375 """
1378 """
1376
1379
1377 def getstorage(tree):
1380 def getstorage(tree):
1378 """Retrieve an interface to storage for a particular tree.
1381 """Retrieve an interface to storage for a particular tree.
1379
1382
1380 If ``tree`` is the empty bytestring, storage for the root manifest will
1383 If ``tree`` is the empty bytestring, storage for the root manifest will
1381 be returned. Otherwise storage for a tree manifest is returned.
1384 be returned. Otherwise storage for a tree manifest is returned.
1382
1385
1383 TODO formalize interface for returned object.
1386 TODO formalize interface for returned object.
1384 """
1387 """
1385
1388
1386 def clearcaches():
1389 def clearcaches():
1387 """Clear caches associated with this collection."""
1390 """Clear caches associated with this collection."""
1388
1391
1389 def rev(node):
1392 def rev(node):
1390 """Obtain the revision number for a binary node.
1393 """Obtain the revision number for a binary node.
1391
1394
1392 Raises ``error.LookupError`` if the node is not known.
1395 Raises ``error.LookupError`` if the node is not known.
1393 """
1396 """
1394
1397
1395
1398
1396 class ilocalrepositoryfilestorage(interfaceutil.Interface):
1399 class ilocalrepositoryfilestorage(interfaceutil.Interface):
1397 """Local repository sub-interface providing access to tracked file storage.
1400 """Local repository sub-interface providing access to tracked file storage.
1398
1401
1399 This interface defines how a repository accesses storage for a single
1402 This interface defines how a repository accesses storage for a single
1400 tracked file path.
1403 tracked file path.
1401 """
1404 """
1402
1405
1403 def file(f):
1406 def file(f):
1404 """Obtain a filelog for a tracked path.
1407 """Obtain a filelog for a tracked path.
1405
1408
1406 The returned type conforms to the ``ifilestorage`` interface.
1409 The returned type conforms to the ``ifilestorage`` interface.
1407 """
1410 """
1408
1411
1409
1412
1410 class ilocalrepositorymain(interfaceutil.Interface):
1413 class ilocalrepositorymain(interfaceutil.Interface):
1411 """Main interface for local repositories.
1414 """Main interface for local repositories.
1412
1415
1413 This currently captures the reality of things - not how things should be.
1416 This currently captures the reality of things - not how things should be.
1414 """
1417 """
1415
1418
1416 supportedformats = interfaceutil.Attribute(
1419 supportedformats = interfaceutil.Attribute(
1417 """Set of requirements that apply to stream clone.
1420 """Set of requirements that apply to stream clone.
1418
1421
1419 This is actually a class attribute and is shared among all instances.
1422 This is actually a class attribute and is shared among all instances.
1420 """
1423 """
1421 )
1424 )
1422
1425
1423 supported = interfaceutil.Attribute(
1426 supported = interfaceutil.Attribute(
1424 """Set of requirements that this repo is capable of opening."""
1427 """Set of requirements that this repo is capable of opening."""
1425 )
1428 )
1426
1429
1427 requirements = interfaceutil.Attribute(
1430 requirements = interfaceutil.Attribute(
1428 """Set of requirements this repo uses."""
1431 """Set of requirements this repo uses."""
1429 )
1432 )
1430
1433
1431 features = interfaceutil.Attribute(
1434 features = interfaceutil.Attribute(
1432 """Set of "features" this repository supports.
1435 """Set of "features" this repository supports.
1433
1436
1434 A "feature" is a loosely-defined term. It can refer to a feature
1437 A "feature" is a loosely-defined term. It can refer to a feature
1435 in the classical sense or can describe an implementation detail
1438 in the classical sense or can describe an implementation detail
1436 of the repository. For example, a ``readonly`` feature may denote
1439 of the repository. For example, a ``readonly`` feature may denote
1437 the repository as read-only. Or a ``revlogfilestore`` feature may
1440 the repository as read-only. Or a ``revlogfilestore`` feature may
1438 denote that the repository is using revlogs for file storage.
1441 denote that the repository is using revlogs for file storage.
1439
1442
1440 The intent of features is to provide a machine-queryable mechanism
1443 The intent of features is to provide a machine-queryable mechanism
1441 for repo consumers to test for various repository characteristics.
1444 for repo consumers to test for various repository characteristics.
1442
1445
1443 Features are similar to ``requirements``. The main difference is that
1446 Features are similar to ``requirements``. The main difference is that
1444 requirements are stored on-disk and represent requirements to open the
1447 requirements are stored on-disk and represent requirements to open the
1445 repository. Features are more run-time capabilities of the repository
1448 repository. Features are more run-time capabilities of the repository
1446 and more granular capabilities (which may be derived from requirements).
1449 and more granular capabilities (which may be derived from requirements).
1447 """
1450 """
1448 )
1451 )
1449
1452
1450 filtername = interfaceutil.Attribute(
1453 filtername = interfaceutil.Attribute(
1451 """Name of the repoview that is active on this repo."""
1454 """Name of the repoview that is active on this repo."""
1452 )
1455 )
1453
1456
1454 wvfs = interfaceutil.Attribute(
1457 wvfs = interfaceutil.Attribute(
1455 """VFS used to access the working directory."""
1458 """VFS used to access the working directory."""
1456 )
1459 )
1457
1460
1458 vfs = interfaceutil.Attribute(
1461 vfs = interfaceutil.Attribute(
1459 """VFS rooted at the .hg directory.
1462 """VFS rooted at the .hg directory.
1460
1463
1461 Used to access repository data not in the store.
1464 Used to access repository data not in the store.
1462 """
1465 """
1463 )
1466 )
1464
1467
1465 svfs = interfaceutil.Attribute(
1468 svfs = interfaceutil.Attribute(
1466 """VFS rooted at the store.
1469 """VFS rooted at the store.
1467
1470
1468 Used to access repository data in the store. Typically .hg/store.
1471 Used to access repository data in the store. Typically .hg/store.
1469 But can point elsewhere if the store is shared.
1472 But can point elsewhere if the store is shared.
1470 """
1473 """
1471 )
1474 )
1472
1475
1473 root = interfaceutil.Attribute(
1476 root = interfaceutil.Attribute(
1474 """Path to the root of the working directory."""
1477 """Path to the root of the working directory."""
1475 )
1478 )
1476
1479
1477 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1480 path = interfaceutil.Attribute("""Path to the .hg directory.""")
1478
1481
1479 origroot = interfaceutil.Attribute(
1482 origroot = interfaceutil.Attribute(
1480 """The filesystem path that was used to construct the repo."""
1483 """The filesystem path that was used to construct the repo."""
1481 )
1484 )
1482
1485
1483 auditor = interfaceutil.Attribute(
1486 auditor = interfaceutil.Attribute(
1484 """A pathauditor for the working directory.
1487 """A pathauditor for the working directory.
1485
1488
1486 This checks if a path refers to a nested repository.
1489 This checks if a path refers to a nested repository.
1487
1490
1488 Operates on the filesystem.
1491 Operates on the filesystem.
1489 """
1492 """
1490 )
1493 )
1491
1494
1492 nofsauditor = interfaceutil.Attribute(
1495 nofsauditor = interfaceutil.Attribute(
1493 """A pathauditor for the working directory.
1496 """A pathauditor for the working directory.
1494
1497
1495 This is like ``auditor`` except it doesn't do filesystem checks.
1498 This is like ``auditor`` except it doesn't do filesystem checks.
1496 """
1499 """
1497 )
1500 )
1498
1501
1499 baseui = interfaceutil.Attribute(
1502 baseui = interfaceutil.Attribute(
1500 """Original ui instance passed into constructor."""
1503 """Original ui instance passed into constructor."""
1501 )
1504 )
1502
1505
1503 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1506 ui = interfaceutil.Attribute("""Main ui instance for this instance.""")
1504
1507
1505 sharedpath = interfaceutil.Attribute(
1508 sharedpath = interfaceutil.Attribute(
1506 """Path to the .hg directory of the repo this repo was shared from."""
1509 """Path to the .hg directory of the repo this repo was shared from."""
1507 )
1510 )
1508
1511
1509 store = interfaceutil.Attribute("""A store instance.""")
1512 store = interfaceutil.Attribute("""A store instance.""")
1510
1513
1511 spath = interfaceutil.Attribute("""Path to the store.""")
1514 spath = interfaceutil.Attribute("""Path to the store.""")
1512
1515
1513 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1516 sjoin = interfaceutil.Attribute("""Alias to self.store.join.""")
1514
1517
1515 cachevfs = interfaceutil.Attribute(
1518 cachevfs = interfaceutil.Attribute(
1516 """A VFS used to access the cache directory.
1519 """A VFS used to access the cache directory.
1517
1520
1518 Typically .hg/cache.
1521 Typically .hg/cache.
1519 """
1522 """
1520 )
1523 )
1521
1524
1522 wcachevfs = interfaceutil.Attribute(
1525 wcachevfs = interfaceutil.Attribute(
1523 """A VFS used to access the cache directory dedicated to working copy
1526 """A VFS used to access the cache directory dedicated to working copy
1524
1527
1525 Typically .hg/wcache.
1528 Typically .hg/wcache.
1526 """
1529 """
1527 )
1530 )
1528
1531
1529 filteredrevcache = interfaceutil.Attribute(
1532 filteredrevcache = interfaceutil.Attribute(
1530 """Holds sets of revisions to be filtered."""
1533 """Holds sets of revisions to be filtered."""
1531 )
1534 )
1532
1535
1533 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1536 names = interfaceutil.Attribute("""A ``namespaces`` instance.""")
1534
1537
1535 filecopiesmode = interfaceutil.Attribute(
1538 filecopiesmode = interfaceutil.Attribute(
1536 """The way files copies should be dealt with in this repo."""
1539 """The way files copies should be dealt with in this repo."""
1537 )
1540 )
1538
1541
1539 def close():
1542 def close():
1540 """Close the handle on this repository."""
1543 """Close the handle on this repository."""
1541
1544
1542 def peer():
1545 def peer():
1543 """Obtain an object conforming to the ``peer`` interface."""
1546 """Obtain an object conforming to the ``peer`` interface."""
1544
1547
1545 def unfiltered():
1548 def unfiltered():
1546 """Obtain an unfiltered/raw view of this repo."""
1549 """Obtain an unfiltered/raw view of this repo."""
1547
1550
1548 def filtered(name, visibilityexceptions=None):
1551 def filtered(name, visibilityexceptions=None):
1549 """Obtain a named view of this repository."""
1552 """Obtain a named view of this repository."""
1550
1553
1551 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1554 obsstore = interfaceutil.Attribute("""A store of obsolescence data.""")
1552
1555
1553 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1556 changelog = interfaceutil.Attribute("""A handle on the changelog revlog.""")
1554
1557
1555 manifestlog = interfaceutil.Attribute(
1558 manifestlog = interfaceutil.Attribute(
1556 """An instance conforming to the ``imanifestlog`` interface.
1559 """An instance conforming to the ``imanifestlog`` interface.
1557
1560
1558 Provides access to manifests for the repository.
1561 Provides access to manifests for the repository.
1559 """
1562 """
1560 )
1563 )
1561
1564
1562 dirstate = interfaceutil.Attribute("""Working directory state.""")
1565 dirstate = interfaceutil.Attribute("""Working directory state.""")
1563
1566
1564 narrowpats = interfaceutil.Attribute(
1567 narrowpats = interfaceutil.Attribute(
1565 """Matcher patterns for this repository's narrowspec."""
1568 """Matcher patterns for this repository's narrowspec."""
1566 )
1569 )
1567
1570
1568 def narrowmatch(match=None, includeexact=False):
1571 def narrowmatch(match=None, includeexact=False):
1569 """Obtain a matcher for the narrowspec."""
1572 """Obtain a matcher for the narrowspec."""
1570
1573
1571 def setnarrowpats(newincludes, newexcludes):
1574 def setnarrowpats(newincludes, newexcludes):
1572 """Define the narrowspec for this repository."""
1575 """Define the narrowspec for this repository."""
1573
1576
1574 def __getitem__(changeid):
1577 def __getitem__(changeid):
1575 """Try to resolve a changectx."""
1578 """Try to resolve a changectx."""
1576
1579
1577 def __contains__(changeid):
1580 def __contains__(changeid):
1578 """Whether a changeset exists."""
1581 """Whether a changeset exists."""
1579
1582
1580 def __nonzero__():
1583 def __nonzero__():
1581 """Always returns True."""
1584 """Always returns True."""
1582 return True
1585 return True
1583
1586
1584 __bool__ = __nonzero__
1587 __bool__ = __nonzero__
1585
1588
1586 def __len__():
1589 def __len__():
1587 """Returns the number of changesets in the repo."""
1590 """Returns the number of changesets in the repo."""
1588
1591
1589 def __iter__():
1592 def __iter__():
1590 """Iterate over revisions in the changelog."""
1593 """Iterate over revisions in the changelog."""
1591
1594
1592 def revs(expr, *args):
1595 def revs(expr, *args):
1593 """Evaluate a revset.
1596 """Evaluate a revset.
1594
1597
1595 Emits revisions.
1598 Emits revisions.
1596 """
1599 """
1597
1600
1598 def set(expr, *args):
1601 def set(expr, *args):
1599 """Evaluate a revset.
1602 """Evaluate a revset.
1600
1603
1601 Emits changectx instances.
1604 Emits changectx instances.
1602 """
1605 """
1603
1606
1604 def anyrevs(specs, user=False, localalias=None):
1607 def anyrevs(specs, user=False, localalias=None):
1605 """Find revisions matching one of the given revsets."""
1608 """Find revisions matching one of the given revsets."""
1606
1609
1607 def url():
1610 def url():
1608 """Returns a string representing the location of this repo."""
1611 """Returns a string representing the location of this repo."""
1609
1612
1610 def hook(name, throw=False, **args):
1613 def hook(name, throw=False, **args):
1611 """Call a hook."""
1614 """Call a hook."""
1612
1615
1613 def tags():
1616 def tags():
1614 """Return a mapping of tag to node."""
1617 """Return a mapping of tag to node."""
1615
1618
1616 def tagtype(tagname):
1619 def tagtype(tagname):
1617 """Return the type of a given tag."""
1620 """Return the type of a given tag."""
1618
1621
1619 def tagslist():
1622 def tagslist():
1620 """Return a list of tags ordered by revision."""
1623 """Return a list of tags ordered by revision."""
1621
1624
1622 def nodetags(node):
1625 def nodetags(node):
1623 """Return the tags associated with a node."""
1626 """Return the tags associated with a node."""
1624
1627
1625 def nodebookmarks(node):
1628 def nodebookmarks(node):
1626 """Return the list of bookmarks pointing to the specified node."""
1629 """Return the list of bookmarks pointing to the specified node."""
1627
1630
1628 def branchmap():
1631 def branchmap():
1629 """Return a mapping of branch to heads in that branch."""
1632 """Return a mapping of branch to heads in that branch."""
1630
1633
1631 def revbranchcache():
1634 def revbranchcache():
1632 pass
1635 pass
1633
1636
1634 def branchtip(branchtip, ignoremissing=False):
1637 def branchtip(branchtip, ignoremissing=False):
1635 """Return the tip node for a given branch."""
1638 """Return the tip node for a given branch."""
1636
1639
1637 def lookup(key):
1640 def lookup(key):
1638 """Resolve the node for a revision."""
1641 """Resolve the node for a revision."""
1639
1642
1640 def lookupbranch(key):
1643 def lookupbranch(key):
1641 """Look up the branch name of the given revision or branch name."""
1644 """Look up the branch name of the given revision or branch name."""
1642
1645
1643 def known(nodes):
1646 def known(nodes):
1644 """Determine whether a series of nodes is known.
1647 """Determine whether a series of nodes is known.
1645
1648
1646 Returns a list of bools.
1649 Returns a list of bools.
1647 """
1650 """
1648
1651
1649 def local():
1652 def local():
1650 """Whether the repository is local."""
1653 """Whether the repository is local."""
1651 return True
1654 return True
1652
1655
1653 def publishing():
1656 def publishing():
1654 """Whether the repository is a publishing repository."""
1657 """Whether the repository is a publishing repository."""
1655
1658
1656 def cancopy():
1659 def cancopy():
1657 pass
1660 pass
1658
1661
1659 def shared():
1662 def shared():
1660 """The type of shared repository or None."""
1663 """The type of shared repository or None."""
1661
1664
1662 def wjoin(f, *insidef):
1665 def wjoin(f, *insidef):
1663 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1666 """Calls self.vfs.reljoin(self.root, f, *insidef)"""
1664
1667
1665 def setparents(p1, p2):
1668 def setparents(p1, p2):
1666 """Set the parent nodes of the working directory."""
1669 """Set the parent nodes of the working directory."""
1667
1670
1668 def filectx(path, changeid=None, fileid=None):
1671 def filectx(path, changeid=None, fileid=None):
1669 """Obtain a filectx for the given file revision."""
1672 """Obtain a filectx for the given file revision."""
1670
1673
1671 def getcwd():
1674 def getcwd():
1672 """Obtain the current working directory from the dirstate."""
1675 """Obtain the current working directory from the dirstate."""
1673
1676
1674 def pathto(f, cwd=None):
1677 def pathto(f, cwd=None):
1675 """Obtain the relative path to a file."""
1678 """Obtain the relative path to a file."""
1676
1679
1677 def adddatafilter(name, fltr):
1680 def adddatafilter(name, fltr):
1678 pass
1681 pass
1679
1682
1680 def wread(filename):
1683 def wread(filename):
1681 """Read a file from wvfs, using data filters."""
1684 """Read a file from wvfs, using data filters."""
1682
1685
1683 def wwrite(filename, data, flags, backgroundclose=False, **kwargs):
1686 def wwrite(filename, data, flags, backgroundclose=False, **kwargs):
1684 """Write data to a file in the wvfs, using data filters."""
1687 """Write data to a file in the wvfs, using data filters."""
1685
1688
1686 def wwritedata(filename, data):
1689 def wwritedata(filename, data):
1687 """Resolve data for writing to the wvfs, using data filters."""
1690 """Resolve data for writing to the wvfs, using data filters."""
1688
1691
1689 def currenttransaction():
1692 def currenttransaction():
1690 """Obtain the current transaction instance or None."""
1693 """Obtain the current transaction instance or None."""
1691
1694
1692 def transaction(desc, report=None):
1695 def transaction(desc, report=None):
1693 """Open a new transaction to write to the repository."""
1696 """Open a new transaction to write to the repository."""
1694
1697
1695 def undofiles():
1698 def undofiles():
1696 """Returns a list of (vfs, path) for files to undo transactions."""
1699 """Returns a list of (vfs, path) for files to undo transactions."""
1697
1700
1698 def recover():
1701 def recover():
1699 """Roll back an interrupted transaction."""
1702 """Roll back an interrupted transaction."""
1700
1703
1701 def rollback(dryrun=False, force=False):
1704 def rollback(dryrun=False, force=False):
1702 """Undo the last transaction.
1705 """Undo the last transaction.
1703
1706
1704 DANGEROUS.
1707 DANGEROUS.
1705 """
1708 """
1706
1709
1707 def updatecaches(tr=None, full=False):
1710 def updatecaches(tr=None, full=False):
1708 """Warm repo caches."""
1711 """Warm repo caches."""
1709
1712
1710 def invalidatecaches():
1713 def invalidatecaches():
1711 """Invalidate cached data due to the repository mutating."""
1714 """Invalidate cached data due to the repository mutating."""
1712
1715
1713 def invalidatevolatilesets():
1716 def invalidatevolatilesets():
1714 pass
1717 pass
1715
1718
1716 def invalidatedirstate():
1719 def invalidatedirstate():
1717 """Invalidate the dirstate."""
1720 """Invalidate the dirstate."""
1718
1721
1719 def invalidate(clearfilecache=False):
1722 def invalidate(clearfilecache=False):
1720 pass
1723 pass
1721
1724
1722 def invalidateall():
1725 def invalidateall():
1723 pass
1726 pass
1724
1727
1725 def lock(wait=True):
1728 def lock(wait=True):
1726 """Lock the repository store and return a lock instance."""
1729 """Lock the repository store and return a lock instance."""
1727
1730
1728 def wlock(wait=True):
1731 def wlock(wait=True):
1729 """Lock the non-store parts of the repository."""
1732 """Lock the non-store parts of the repository."""
1730
1733
1731 def currentwlock():
1734 def currentwlock():
1732 """Return the wlock if it's held or None."""
1735 """Return the wlock if it's held or None."""
1733
1736
1734 def checkcommitpatterns(wctx, match, status, fail):
1737 def checkcommitpatterns(wctx, match, status, fail):
1735 pass
1738 pass
1736
1739
1737 def commit(
1740 def commit(
1738 text=b'',
1741 text=b'',
1739 user=None,
1742 user=None,
1740 date=None,
1743 date=None,
1741 match=None,
1744 match=None,
1742 force=False,
1745 force=False,
1743 editor=False,
1746 editor=False,
1744 extra=None,
1747 extra=None,
1745 ):
1748 ):
1746 """Add a new revision to the repository."""
1749 """Add a new revision to the repository."""
1747
1750
1748 def commitctx(ctx, error=False, origctx=None):
1751 def commitctx(ctx, error=False, origctx=None):
1749 """Commit a commitctx instance to the repository."""
1752 """Commit a commitctx instance to the repository."""
1750
1753
1751 def destroying():
1754 def destroying():
1752 """Inform the repository that nodes are about to be destroyed."""
1755 """Inform the repository that nodes are about to be destroyed."""
1753
1756
1754 def destroyed():
1757 def destroyed():
1755 """Inform the repository that nodes have been destroyed."""
1758 """Inform the repository that nodes have been destroyed."""
1756
1759
1757 def status(
1760 def status(
1758 node1=b'.',
1761 node1=b'.',
1759 node2=None,
1762 node2=None,
1760 match=None,
1763 match=None,
1761 ignored=False,
1764 ignored=False,
1762 clean=False,
1765 clean=False,
1763 unknown=False,
1766 unknown=False,
1764 listsubrepos=False,
1767 listsubrepos=False,
1765 ):
1768 ):
1766 """Convenience method to call repo[x].status()."""
1769 """Convenience method to call repo[x].status()."""
1767
1770
1768 def addpostdsstatus(ps):
1771 def addpostdsstatus(ps):
1769 pass
1772 pass
1770
1773
1771 def postdsstatus():
1774 def postdsstatus():
1772 pass
1775 pass
1773
1776
1774 def clearpostdsstatus():
1777 def clearpostdsstatus():
1775 pass
1778 pass
1776
1779
1777 def heads(start=None):
1780 def heads(start=None):
1778 """Obtain list of nodes that are DAG heads."""
1781 """Obtain list of nodes that are DAG heads."""
1779
1782
1780 def branchheads(branch=None, start=None, closed=False):
1783 def branchheads(branch=None, start=None, closed=False):
1781 pass
1784 pass
1782
1785
1783 def branches(nodes):
1786 def branches(nodes):
1784 pass
1787 pass
1785
1788
1786 def between(pairs):
1789 def between(pairs):
1787 pass
1790 pass
1788
1791
1789 def checkpush(pushop):
1792 def checkpush(pushop):
1790 pass
1793 pass
1791
1794
1792 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
1795 prepushoutgoinghooks = interfaceutil.Attribute("""util.hooks instance.""")
1793
1796
1794 def pushkey(namespace, key, old, new):
1797 def pushkey(namespace, key, old, new):
1795 pass
1798 pass
1796
1799
1797 def listkeys(namespace):
1800 def listkeys(namespace):
1798 pass
1801 pass
1799
1802
1800 def debugwireargs(one, two, three=None, four=None, five=None):
1803 def debugwireargs(one, two, three=None, four=None, five=None):
1801 pass
1804 pass
1802
1805
1803 def savecommitmessage(text):
1806 def savecommitmessage(text):
1804 pass
1807 pass
1805
1808
1806
1809
1807 class completelocalrepository(
1810 class completelocalrepository(
1808 ilocalrepositorymain, ilocalrepositoryfilestorage
1811 ilocalrepositorymain, ilocalrepositoryfilestorage
1809 ):
1812 ):
1810 """Complete interface for a local repository."""
1813 """Complete interface for a local repository."""
1811
1814
1812
1815
1813 class iwireprotocolcommandcacher(interfaceutil.Interface):
1816 class iwireprotocolcommandcacher(interfaceutil.Interface):
1814 """Represents a caching backend for wire protocol commands.
1817 """Represents a caching backend for wire protocol commands.
1815
1818
1816 Wire protocol version 2 supports transparent caching of many commands.
1819 Wire protocol version 2 supports transparent caching of many commands.
1817 To leverage this caching, servers can activate objects that cache
1820 To leverage this caching, servers can activate objects that cache
1818 command responses. Objects handle both cache writing and reading.
1821 command responses. Objects handle both cache writing and reading.
1819 This interface defines how that response caching mechanism works.
1822 This interface defines how that response caching mechanism works.
1820
1823
1821 Wire protocol version 2 commands emit a series of objects that are
1824 Wire protocol version 2 commands emit a series of objects that are
1822 serialized and sent to the client. The caching layer exists between
1825 serialized and sent to the client. The caching layer exists between
1823 the invocation of the command function and the sending of its output
1826 the invocation of the command function and the sending of its output
1824 objects to an output layer.
1827 objects to an output layer.
1825
1828
1826 Instances of this interface represent a binding to a cache that
1829 Instances of this interface represent a binding to a cache that
1827 can serve a response (in place of calling a command function) and/or
1830 can serve a response (in place of calling a command function) and/or
1828 write responses to a cache for subsequent use.
1831 write responses to a cache for subsequent use.
1829
1832
1830 When a command request arrives, the following happens with regards
1833 When a command request arrives, the following happens with regards
1831 to this interface:
1834 to this interface:
1832
1835
1833 1. The server determines whether the command request is cacheable.
1836 1. The server determines whether the command request is cacheable.
1834 2. If it is, an instance of this interface is spawned.
1837 2. If it is, an instance of this interface is spawned.
1835 3. The cacher is activated in a context manager (``__enter__`` is called).
1838 3. The cacher is activated in a context manager (``__enter__`` is called).
1836 4. A cache *key* for that request is derived. This will call the
1839 4. A cache *key* for that request is derived. This will call the
1837 instance's ``adjustcachekeystate()`` method so the derivation
1840 instance's ``adjustcachekeystate()`` method so the derivation
1838 can be influenced.
1841 can be influenced.
1839 5. The cacher is informed of the derived cache key via a call to
1842 5. The cacher is informed of the derived cache key via a call to
1840 ``setcachekey()``.
1843 ``setcachekey()``.
1841 6. The cacher's ``lookup()`` method is called to test for presence of
1844 6. The cacher's ``lookup()`` method is called to test for presence of
1842 the derived key in the cache.
1845 the derived key in the cache.
1843 7. If ``lookup()`` returns a hit, that cached result is used in place
1846 7. If ``lookup()`` returns a hit, that cached result is used in place
1844 of invoking the command function. ``__exit__`` is called and the instance
1847 of invoking the command function. ``__exit__`` is called and the instance
1845 is discarded.
1848 is discarded.
1846 8. The command function is invoked.
1849 8. The command function is invoked.
1847 9. ``onobject()`` is called for each object emitted by the command
1850 9. ``onobject()`` is called for each object emitted by the command
1848 function.
1851 function.
1849 10. After the final object is seen, ``onfinished()`` is called.
1852 10. After the final object is seen, ``onfinished()`` is called.
1850 11. ``__exit__`` is called to signal the end of use of the instance.
1853 11. ``__exit__`` is called to signal the end of use of the instance.
1851
1854
1852 Cache *key* derivation can be influenced by the instance.
1855 Cache *key* derivation can be influenced by the instance.
1853
1856
1854 Cache keys are initially derived by a deterministic representation of
1857 Cache keys are initially derived by a deterministic representation of
1855 the command request. This includes the command name, arguments, protocol
1858 the command request. This includes the command name, arguments, protocol
1856 version, etc. This initial key derivation is performed by CBOR-encoding a
1859 version, etc. This initial key derivation is performed by CBOR-encoding a
1857 data structure and feeding that output into a hasher.
1860 data structure and feeding that output into a hasher.
1858
1861
1859 Instances of this interface can influence this initial key derivation
1862 Instances of this interface can influence this initial key derivation
1860 via ``adjustcachekeystate()``.
1863 via ``adjustcachekeystate()``.
1861
1864
1862 The instance is informed of the derived cache key via a call to
1865 The instance is informed of the derived cache key via a call to
1863 ``setcachekey()``. The instance must store the key locally so it can
1866 ``setcachekey()``. The instance must store the key locally so it can
1864 be consulted on subsequent operations that may require it.
1867 be consulted on subsequent operations that may require it.
1865
1868
1866 When constructed, the instance has access to a callable that can be used
1869 When constructed, the instance has access to a callable that can be used
1867 for encoding response objects. This callable receives as its single
1870 for encoding response objects. This callable receives as its single
1868 argument an object emitted by a command function. It returns an iterable
1871 argument an object emitted by a command function. It returns an iterable
1869 of bytes chunks representing the encoded object. Unless the cacher is
1872 of bytes chunks representing the encoded object. Unless the cacher is
1870 caching native Python objects in memory or has a way of reconstructing
1873 caching native Python objects in memory or has a way of reconstructing
1871 the original Python objects, implementations typically call this function
1874 the original Python objects, implementations typically call this function
1872 to produce bytes from the output objects and then store those bytes in
1875 to produce bytes from the output objects and then store those bytes in
1873 the cache. When it comes time to re-emit those bytes, they are wrapped
1876 the cache. When it comes time to re-emit those bytes, they are wrapped
1874 in a ``wireprototypes.encodedresponse`` instance to tell the output
1877 in a ``wireprototypes.encodedresponse`` instance to tell the output
1875 layer that they are pre-encoded.
1878 layer that they are pre-encoded.
1876
1879
1877 When receiving the objects emitted by the command function, instances
1880 When receiving the objects emitted by the command function, instances
1878 can choose what to do with those objects. The simplest thing to do is
1881 can choose what to do with those objects. The simplest thing to do is
1879 re-emit the original objects. They will be forwarded to the output
1882 re-emit the original objects. They will be forwarded to the output
1880 layer and will be processed as if the cacher did not exist.
1883 layer and will be processed as if the cacher did not exist.
1881
1884
1882 Implementations could also choose to not emit objects - instead locally
1885 Implementations could also choose to not emit objects - instead locally
1883 buffering objects or their encoded representation. They could then emit
1886 buffering objects or their encoded representation. They could then emit
1884 a single "coalesced" object when ``onfinished()`` is called. In
1887 a single "coalesced" object when ``onfinished()`` is called. In
1885 this way, the implementation would function as a filtering layer of
1888 this way, the implementation would function as a filtering layer of
1886 sorts.
1889 sorts.
1887
1890
1888 When caching objects, typically the encoded form of the object will
1891 When caching objects, typically the encoded form of the object will
1889 be stored. Keep in mind that if the original object is forwarded to
1892 be stored. Keep in mind that if the original object is forwarded to
1890 the output layer, it will need to be encoded there as well. For large
1893 the output layer, it will need to be encoded there as well. For large
1891 output, this redundant encoding could add overhead. Implementations
1894 output, this redundant encoding could add overhead. Implementations
1892 could wrap the encoded object data in ``wireprototypes.encodedresponse``
1895 could wrap the encoded object data in ``wireprototypes.encodedresponse``
1893 instances to avoid this overhead.
1896 instances to avoid this overhead.
1894 """
1897 """
1895
1898
1896 def __enter__():
1899 def __enter__():
1897 """Marks the instance as active.
1900 """Marks the instance as active.
1898
1901
1899 Should return self.
1902 Should return self.
1900 """
1903 """
1901
1904
1902 def __exit__(exctype, excvalue, exctb):
1905 def __exit__(exctype, excvalue, exctb):
1903 """Called when cacher is no longer used.
1906 """Called when cacher is no longer used.
1904
1907
1905 This can be used by implementations to perform cleanup actions (e.g.
1908 This can be used by implementations to perform cleanup actions (e.g.
1906 disconnecting network sockets, aborting a partially cached response.
1909 disconnecting network sockets, aborting a partially cached response.
1907 """
1910 """
1908
1911
1909 def adjustcachekeystate(state):
1912 def adjustcachekeystate(state):
1910 """Influences cache key derivation by adjusting state to derive key.
1913 """Influences cache key derivation by adjusting state to derive key.
1911
1914
1912 A dict defining the state used to derive the cache key is passed.
1915 A dict defining the state used to derive the cache key is passed.
1913
1916
1914 Implementations can modify this dict to record additional state that
1917 Implementations can modify this dict to record additional state that
1915 is wanted to influence key derivation.
1918 is wanted to influence key derivation.
1916
1919
1917 Implementations are *highly* encouraged to not modify or delete
1920 Implementations are *highly* encouraged to not modify or delete
1918 existing keys.
1921 existing keys.
1919 """
1922 """
1920
1923
1921 def setcachekey(key):
1924 def setcachekey(key):
1922 """Record the derived cache key for this request.
1925 """Record the derived cache key for this request.
1923
1926
1924 Instances may mutate the key for internal usage, as desired. e.g.
1927 Instances may mutate the key for internal usage, as desired. e.g.
1925 instances may wish to prepend the repo name, introduce path
1928 instances may wish to prepend the repo name, introduce path
1926 components for filesystem or URL addressing, etc. Behavior is up to
1929 components for filesystem or URL addressing, etc. Behavior is up to
1927 the cache.
1930 the cache.
1928
1931
1929 Returns a bool indicating if the request is cacheable by this
1932 Returns a bool indicating if the request is cacheable by this
1930 instance.
1933 instance.
1931 """
1934 """
1932
1935
1933 def lookup():
1936 def lookup():
1934 """Attempt to resolve an entry in the cache.
1937 """Attempt to resolve an entry in the cache.
1935
1938
1936 The instance is instructed to look for the cache key that it was
1939 The instance is instructed to look for the cache key that it was
1937 informed about via the call to ``setcachekey()``.
1940 informed about via the call to ``setcachekey()``.
1938
1941
1939 If there's no cache hit or the cacher doesn't wish to use the cached
1942 If there's no cache hit or the cacher doesn't wish to use the cached
1940 entry, ``None`` should be returned.
1943 entry, ``None`` should be returned.
1941
1944
1942 Else, a dict defining the cached result should be returned. The
1945 Else, a dict defining the cached result should be returned. The
1943 dict may have the following keys:
1946 dict may have the following keys:
1944
1947
1945 objs
1948 objs
1946 An iterable of objects that should be sent to the client. That
1949 An iterable of objects that should be sent to the client. That
1947 iterable of objects is expected to be what the command function
1950 iterable of objects is expected to be what the command function
1948 would return if invoked or an equivalent representation thereof.
1951 would return if invoked or an equivalent representation thereof.
1949 """
1952 """
1950
1953
1951 def onobject(obj):
1954 def onobject(obj):
1952 """Called when a new object is emitted from the command function.
1955 """Called when a new object is emitted from the command function.
1953
1956
1954 Receives as its argument the object that was emitted from the
1957 Receives as its argument the object that was emitted from the
1955 command function.
1958 command function.
1956
1959
1957 This method returns an iterator of objects to forward to the output
1960 This method returns an iterator of objects to forward to the output
1958 layer. The easiest implementation is a generator that just
1961 layer. The easiest implementation is a generator that just
1959 ``yield obj``.
1962 ``yield obj``.
1960 """
1963 """
1961
1964
1962 def onfinished():
1965 def onfinished():
1963 """Called after all objects have been emitted from the command function.
1966 """Called after all objects have been emitted from the command function.
1964
1967
1965 Implementations should return an iterator of objects to forward to
1968 Implementations should return an iterator of objects to forward to
1966 the output layer.
1969 the output layer.
1967
1970
1968 This method can be a generator.
1971 This method can be a generator.
1969 """
1972 """
@@ -1,2256 +1,2266 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@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 import heapq
10 import heapq
11 import itertools
11 import itertools
12 import struct
12 import struct
13 import weakref
13 import weakref
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from .pycompat import getattr
22 from .pycompat import getattr
23 from . import (
23 from . import (
24 encoding,
24 encoding,
25 error,
25 error,
26 match as matchmod,
26 match as matchmod,
27 mdiff,
27 mdiff,
28 pathutil,
28 pathutil,
29 policy,
29 policy,
30 pycompat,
30 pycompat,
31 revlog,
31 revlog,
32 util,
32 util,
33 )
33 )
34 from .interfaces import (
34 from .interfaces import (
35 repository,
35 repository,
36 util as interfaceutil,
36 util as interfaceutil,
37 )
37 )
38
38
39 parsers = policy.importmod('parsers')
39 parsers = policy.importmod('parsers')
40 propertycache = util.propertycache
40 propertycache = util.propertycache
41
41
42 # Allow tests to more easily test the alternate path in manifestdict.fastdelta()
42 # Allow tests to more easily test the alternate path in manifestdict.fastdelta()
43 FASTDELTA_TEXTDIFF_THRESHOLD = 1000
43 FASTDELTA_TEXTDIFF_THRESHOLD = 1000
44
44
45
45
46 def _parse(data):
46 def _parse(data):
47 # This method does a little bit of excessive-looking
47 # This method does a little bit of excessive-looking
48 # precondition checking. This is so that the behavior of this
48 # precondition checking. This is so that the behavior of this
49 # class exactly matches its C counterpart to try and help
49 # class exactly matches its C counterpart to try and help
50 # prevent surprise breakage for anyone that develops against
50 # prevent surprise breakage for anyone that develops against
51 # the pure version.
51 # the pure version.
52 if data and data[-1:] != b'\n':
52 if data and data[-1:] != b'\n':
53 raise ValueError(b'Manifest did not end in a newline.')
53 raise ValueError(b'Manifest did not end in a newline.')
54 prev = None
54 prev = None
55 for l in data.splitlines():
55 for l in data.splitlines():
56 if prev is not None and prev > l:
56 if prev is not None and prev > l:
57 raise ValueError(b'Manifest lines not in sorted order.')
57 raise ValueError(b'Manifest lines not in sorted order.')
58 prev = l
58 prev = l
59 f, n = l.split(b'\0')
59 f, n = l.split(b'\0')
60 if len(n) > 40:
60 if len(n) > 40:
61 yield f, bin(n[:40]), n[40:]
61 yield f, bin(n[:40]), n[40:]
62 else:
62 else:
63 yield f, bin(n), b''
63 yield f, bin(n), b''
64
64
65
65
66 def _text(it):
66 def _text(it):
67 files = []
67 files = []
68 lines = []
68 lines = []
69 for f, n, fl in it:
69 for f, n, fl in it:
70 files.append(f)
70 files.append(f)
71 # if this is changed to support newlines in filenames,
71 # if this is changed to support newlines in filenames,
72 # be sure to check the templates/ dir again (especially *-raw.tmpl)
72 # be sure to check the templates/ dir again (especially *-raw.tmpl)
73 lines.append(b"%s\0%s%s\n" % (f, hex(n), fl))
73 lines.append(b"%s\0%s%s\n" % (f, hex(n), fl))
74
74
75 _checkforbidden(files)
75 _checkforbidden(files)
76 return b''.join(lines)
76 return b''.join(lines)
77
77
78
78
79 class lazymanifestiter(object):
79 class lazymanifestiter(object):
80 def __init__(self, lm):
80 def __init__(self, lm):
81 self.pos = 0
81 self.pos = 0
82 self.lm = lm
82 self.lm = lm
83
83
84 def __iter__(self):
84 def __iter__(self):
85 return self
85 return self
86
86
87 def next(self):
87 def next(self):
88 try:
88 try:
89 data, pos = self.lm._get(self.pos)
89 data, pos = self.lm._get(self.pos)
90 except IndexError:
90 except IndexError:
91 raise StopIteration
91 raise StopIteration
92 if pos == -1:
92 if pos == -1:
93 self.pos += 1
93 self.pos += 1
94 return data[0]
94 return data[0]
95 self.pos += 1
95 self.pos += 1
96 zeropos = data.find(b'\x00', pos)
96 zeropos = data.find(b'\x00', pos)
97 return data[pos:zeropos]
97 return data[pos:zeropos]
98
98
99 __next__ = next
99 __next__ = next
100
100
101
101
102 class lazymanifestiterentries(object):
102 class lazymanifestiterentries(object):
103 def __init__(self, lm):
103 def __init__(self, lm):
104 self.lm = lm
104 self.lm = lm
105 self.pos = 0
105 self.pos = 0
106
106
107 def __iter__(self):
107 def __iter__(self):
108 return self
108 return self
109
109
110 def next(self):
110 def next(self):
111 try:
111 try:
112 data, pos = self.lm._get(self.pos)
112 data, pos = self.lm._get(self.pos)
113 except IndexError:
113 except IndexError:
114 raise StopIteration
114 raise StopIteration
115 if pos == -1:
115 if pos == -1:
116 self.pos += 1
116 self.pos += 1
117 return data
117 return data
118 zeropos = data.find(b'\x00', pos)
118 zeropos = data.find(b'\x00', pos)
119 hashval = unhexlify(data, self.lm.extrainfo[self.pos], zeropos + 1, 40)
119 hashval = unhexlify(data, self.lm.extrainfo[self.pos], zeropos + 1, 40)
120 flags = self.lm._getflags(data, self.pos, zeropos)
120 flags = self.lm._getflags(data, self.pos, zeropos)
121 self.pos += 1
121 self.pos += 1
122 return (data[pos:zeropos], hashval, flags)
122 return (data[pos:zeropos], hashval, flags)
123
123
124 __next__ = next
124 __next__ = next
125
125
126
126
127 def unhexlify(data, extra, pos, length):
127 def unhexlify(data, extra, pos, length):
128 s = bin(data[pos : pos + length])
128 s = bin(data[pos : pos + length])
129 if extra:
129 if extra:
130 s += chr(extra & 0xFF)
130 s += chr(extra & 0xFF)
131 return s
131 return s
132
132
133
133
134 def _cmp(a, b):
134 def _cmp(a, b):
135 return (a > b) - (a < b)
135 return (a > b) - (a < b)
136
136
137
137
138 class _lazymanifest(object):
138 class _lazymanifest(object):
139 """A pure python manifest backed by a byte string. It is supplimented with
139 """A pure python manifest backed by a byte string. It is supplimented with
140 internal lists as it is modified, until it is compacted back to a pure byte
140 internal lists as it is modified, until it is compacted back to a pure byte
141 string.
141 string.
142
142
143 ``data`` is the initial manifest data.
143 ``data`` is the initial manifest data.
144
144
145 ``positions`` is a list of offsets, one per manifest entry. Positive
145 ``positions`` is a list of offsets, one per manifest entry. Positive
146 values are offsets into ``data``, negative values are offsets into the
146 values are offsets into ``data``, negative values are offsets into the
147 ``extradata`` list. When an entry is removed, its entry is dropped from
147 ``extradata`` list. When an entry is removed, its entry is dropped from
148 ``positions``. The values are encoded such that when walking the list and
148 ``positions``. The values are encoded such that when walking the list and
149 indexing into ``data`` or ``extradata`` as appropriate, the entries are
149 indexing into ``data`` or ``extradata`` as appropriate, the entries are
150 sorted by filename.
150 sorted by filename.
151
151
152 ``extradata`` is a list of (key, hash, flags) for entries that were added or
152 ``extradata`` is a list of (key, hash, flags) for entries that were added or
153 modified since the manifest was created or compacted.
153 modified since the manifest was created or compacted.
154 """
154 """
155
155
156 def __init__(
156 def __init__(
157 self,
157 self,
158 data,
158 data,
159 positions=None,
159 positions=None,
160 extrainfo=None,
160 extrainfo=None,
161 extradata=None,
161 extradata=None,
162 hasremovals=False,
162 hasremovals=False,
163 ):
163 ):
164 if positions is None:
164 if positions is None:
165 self.positions = self.findlines(data)
165 self.positions = self.findlines(data)
166 self.extrainfo = [0] * len(self.positions)
166 self.extrainfo = [0] * len(self.positions)
167 self.data = data
167 self.data = data
168 self.extradata = []
168 self.extradata = []
169 self.hasremovals = False
169 self.hasremovals = False
170 else:
170 else:
171 self.positions = positions[:]
171 self.positions = positions[:]
172 self.extrainfo = extrainfo[:]
172 self.extrainfo = extrainfo[:]
173 self.extradata = extradata[:]
173 self.extradata = extradata[:]
174 self.data = data
174 self.data = data
175 self.hasremovals = hasremovals
175 self.hasremovals = hasremovals
176
176
177 def findlines(self, data):
177 def findlines(self, data):
178 if not data:
178 if not data:
179 return []
179 return []
180 pos = data.find(b"\n")
180 pos = data.find(b"\n")
181 if pos == -1 or data[-1:] != b'\n':
181 if pos == -1 or data[-1:] != b'\n':
182 raise ValueError(b"Manifest did not end in a newline.")
182 raise ValueError(b"Manifest did not end in a newline.")
183 positions = [0]
183 positions = [0]
184 prev = data[: data.find(b'\x00')]
184 prev = data[: data.find(b'\x00')]
185 while pos < len(data) - 1 and pos != -1:
185 while pos < len(data) - 1 and pos != -1:
186 positions.append(pos + 1)
186 positions.append(pos + 1)
187 nexts = data[pos + 1 : data.find(b'\x00', pos + 1)]
187 nexts = data[pos + 1 : data.find(b'\x00', pos + 1)]
188 if nexts < prev:
188 if nexts < prev:
189 raise ValueError(b"Manifest lines not in sorted order.")
189 raise ValueError(b"Manifest lines not in sorted order.")
190 prev = nexts
190 prev = nexts
191 pos = data.find(b"\n", pos + 1)
191 pos = data.find(b"\n", pos + 1)
192 return positions
192 return positions
193
193
194 def _get(self, index):
194 def _get(self, index):
195 # get the position encoded in pos:
195 # get the position encoded in pos:
196 # positive number is an index in 'data'
196 # positive number is an index in 'data'
197 # negative number is in extrapieces
197 # negative number is in extrapieces
198 pos = self.positions[index]
198 pos = self.positions[index]
199 if pos >= 0:
199 if pos >= 0:
200 return self.data, pos
200 return self.data, pos
201 return self.extradata[-pos - 1], -1
201 return self.extradata[-pos - 1], -1
202
202
203 def _getkey(self, pos):
203 def _getkey(self, pos):
204 if pos >= 0:
204 if pos >= 0:
205 return self.data[pos : self.data.find(b'\x00', pos + 1)]
205 return self.data[pos : self.data.find(b'\x00', pos + 1)]
206 return self.extradata[-pos - 1][0]
206 return self.extradata[-pos - 1][0]
207
207
208 def bsearch(self, key):
208 def bsearch(self, key):
209 first = 0
209 first = 0
210 last = len(self.positions) - 1
210 last = len(self.positions) - 1
211
211
212 while first <= last:
212 while first <= last:
213 midpoint = (first + last) // 2
213 midpoint = (first + last) // 2
214 nextpos = self.positions[midpoint]
214 nextpos = self.positions[midpoint]
215 candidate = self._getkey(nextpos)
215 candidate = self._getkey(nextpos)
216 r = _cmp(key, candidate)
216 r = _cmp(key, candidate)
217 if r == 0:
217 if r == 0:
218 return midpoint
218 return midpoint
219 else:
219 else:
220 if r < 0:
220 if r < 0:
221 last = midpoint - 1
221 last = midpoint - 1
222 else:
222 else:
223 first = midpoint + 1
223 first = midpoint + 1
224 return -1
224 return -1
225
225
226 def bsearch2(self, key):
226 def bsearch2(self, key):
227 # same as the above, but will always return the position
227 # same as the above, but will always return the position
228 # done for performance reasons
228 # done for performance reasons
229 first = 0
229 first = 0
230 last = len(self.positions) - 1
230 last = len(self.positions) - 1
231
231
232 while first <= last:
232 while first <= last:
233 midpoint = (first + last) // 2
233 midpoint = (first + last) // 2
234 nextpos = self.positions[midpoint]
234 nextpos = self.positions[midpoint]
235 candidate = self._getkey(nextpos)
235 candidate = self._getkey(nextpos)
236 r = _cmp(key, candidate)
236 r = _cmp(key, candidate)
237 if r == 0:
237 if r == 0:
238 return (midpoint, True)
238 return (midpoint, True)
239 else:
239 else:
240 if r < 0:
240 if r < 0:
241 last = midpoint - 1
241 last = midpoint - 1
242 else:
242 else:
243 first = midpoint + 1
243 first = midpoint + 1
244 return (first, False)
244 return (first, False)
245
245
246 def __contains__(self, key):
246 def __contains__(self, key):
247 return self.bsearch(key) != -1
247 return self.bsearch(key) != -1
248
248
249 def _getflags(self, data, needle, pos):
249 def _getflags(self, data, needle, pos):
250 start = pos + 41
250 start = pos + 41
251 end = data.find(b"\n", start)
251 end = data.find(b"\n", start)
252 if end == -1:
252 if end == -1:
253 end = len(data) - 1
253 end = len(data) - 1
254 if start == end:
254 if start == end:
255 return b''
255 return b''
256 return self.data[start:end]
256 return self.data[start:end]
257
257
258 def __getitem__(self, key):
258 def __getitem__(self, key):
259 if not isinstance(key, bytes):
259 if not isinstance(key, bytes):
260 raise TypeError(b"getitem: manifest keys must be a bytes.")
260 raise TypeError(b"getitem: manifest keys must be a bytes.")
261 needle = self.bsearch(key)
261 needle = self.bsearch(key)
262 if needle == -1:
262 if needle == -1:
263 raise KeyError
263 raise KeyError
264 data, pos = self._get(needle)
264 data, pos = self._get(needle)
265 if pos == -1:
265 if pos == -1:
266 return (data[1], data[2])
266 return (data[1], data[2])
267 zeropos = data.find(b'\x00', pos)
267 zeropos = data.find(b'\x00', pos)
268 assert 0 <= needle <= len(self.positions)
268 assert 0 <= needle <= len(self.positions)
269 assert len(self.extrainfo) == len(self.positions)
269 assert len(self.extrainfo) == len(self.positions)
270 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
270 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
271 flags = self._getflags(data, needle, zeropos)
271 flags = self._getflags(data, needle, zeropos)
272 return (hashval, flags)
272 return (hashval, flags)
273
273
274 def __delitem__(self, key):
274 def __delitem__(self, key):
275 needle, found = self.bsearch2(key)
275 needle, found = self.bsearch2(key)
276 if not found:
276 if not found:
277 raise KeyError
277 raise KeyError
278 cur = self.positions[needle]
278 cur = self.positions[needle]
279 self.positions = self.positions[:needle] + self.positions[needle + 1 :]
279 self.positions = self.positions[:needle] + self.positions[needle + 1 :]
280 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1 :]
280 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1 :]
281 if cur >= 0:
281 if cur >= 0:
282 # This does NOT unsort the list as far as the search functions are
282 # This does NOT unsort the list as far as the search functions are
283 # concerned, as they only examine lines mapped by self.positions.
283 # concerned, as they only examine lines mapped by self.positions.
284 self.data = self.data[:cur] + b'\x00' + self.data[cur + 1 :]
284 self.data = self.data[:cur] + b'\x00' + self.data[cur + 1 :]
285 self.hasremovals = True
285 self.hasremovals = True
286
286
287 def __setitem__(self, key, value):
287 def __setitem__(self, key, value):
288 if not isinstance(key, bytes):
288 if not isinstance(key, bytes):
289 raise TypeError(b"setitem: manifest keys must be a byte string.")
289 raise TypeError(b"setitem: manifest keys must be a byte string.")
290 if not isinstance(value, tuple) or len(value) != 2:
290 if not isinstance(value, tuple) or len(value) != 2:
291 raise TypeError(
291 raise TypeError(
292 b"Manifest values must be a tuple of (node, flags)."
292 b"Manifest values must be a tuple of (node, flags)."
293 )
293 )
294 hashval = value[0]
294 hashval = value[0]
295 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
295 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
296 raise TypeError(b"node must be a 20-byte byte string")
296 raise TypeError(b"node must be a 20-byte byte string")
297 flags = value[1]
297 flags = value[1]
298 if len(hashval) == 22:
298 if len(hashval) == 22:
299 hashval = hashval[:-1]
299 hashval = hashval[:-1]
300 if not isinstance(flags, bytes) or len(flags) > 1:
300 if not isinstance(flags, bytes) or len(flags) > 1:
301 raise TypeError(b"flags must a 0 or 1 byte string, got %r", flags)
301 raise TypeError(b"flags must a 0 or 1 byte string, got %r", flags)
302 needle, found = self.bsearch2(key)
302 needle, found = self.bsearch2(key)
303 if found:
303 if found:
304 # put the item
304 # put the item
305 pos = self.positions[needle]
305 pos = self.positions[needle]
306 if pos < 0:
306 if pos < 0:
307 self.extradata[-pos - 1] = (key, hashval, value[1])
307 self.extradata[-pos - 1] = (key, hashval, value[1])
308 else:
308 else:
309 # just don't bother
309 # just don't bother
310 self.extradata.append((key, hashval, value[1]))
310 self.extradata.append((key, hashval, value[1]))
311 self.positions[needle] = -len(self.extradata)
311 self.positions[needle] = -len(self.extradata)
312 else:
312 else:
313 # not found, put it in with extra positions
313 # not found, put it in with extra positions
314 self.extradata.append((key, hashval, value[1]))
314 self.extradata.append((key, hashval, value[1]))
315 self.positions = (
315 self.positions = (
316 self.positions[:needle]
316 self.positions[:needle]
317 + [-len(self.extradata)]
317 + [-len(self.extradata)]
318 + self.positions[needle:]
318 + self.positions[needle:]
319 )
319 )
320 self.extrainfo = (
320 self.extrainfo = (
321 self.extrainfo[:needle] + [0] + self.extrainfo[needle:]
321 self.extrainfo[:needle] + [0] + self.extrainfo[needle:]
322 )
322 )
323
323
324 def copy(self):
324 def copy(self):
325 # XXX call _compact like in C?
325 # XXX call _compact like in C?
326 return _lazymanifest(
326 return _lazymanifest(
327 self.data,
327 self.data,
328 self.positions,
328 self.positions,
329 self.extrainfo,
329 self.extrainfo,
330 self.extradata,
330 self.extradata,
331 self.hasremovals,
331 self.hasremovals,
332 )
332 )
333
333
334 def _compact(self):
334 def _compact(self):
335 # hopefully not called TOO often
335 # hopefully not called TOO often
336 if len(self.extradata) == 0 and not self.hasremovals:
336 if len(self.extradata) == 0 and not self.hasremovals:
337 return
337 return
338 l = []
338 l = []
339 i = 0
339 i = 0
340 offset = 0
340 offset = 0
341 self.extrainfo = [0] * len(self.positions)
341 self.extrainfo = [0] * len(self.positions)
342 while i < len(self.positions):
342 while i < len(self.positions):
343 if self.positions[i] >= 0:
343 if self.positions[i] >= 0:
344 cur = self.positions[i]
344 cur = self.positions[i]
345 last_cut = cur
345 last_cut = cur
346
346
347 # Collect all contiguous entries in the buffer at the current
347 # Collect all contiguous entries in the buffer at the current
348 # offset, breaking out only for added/modified items held in
348 # offset, breaking out only for added/modified items held in
349 # extradata, or a deleted line prior to the next position.
349 # extradata, or a deleted line prior to the next position.
350 while True:
350 while True:
351 self.positions[i] = offset
351 self.positions[i] = offset
352 i += 1
352 i += 1
353 if i == len(self.positions) or self.positions[i] < 0:
353 if i == len(self.positions) or self.positions[i] < 0:
354 break
354 break
355
355
356 # A removed file has no positions[] entry, but does have an
356 # A removed file has no positions[] entry, but does have an
357 # overwritten first byte. Break out and find the end of the
357 # overwritten first byte. Break out and find the end of the
358 # current good entry/entries if there is a removed file
358 # current good entry/entries if there is a removed file
359 # before the next position.
359 # before the next position.
360 if (
360 if (
361 self.hasremovals
361 self.hasremovals
362 and self.data.find(b'\n\x00', cur, self.positions[i])
362 and self.data.find(b'\n\x00', cur, self.positions[i])
363 != -1
363 != -1
364 ):
364 ):
365 break
365 break
366
366
367 offset += self.positions[i] - cur
367 offset += self.positions[i] - cur
368 cur = self.positions[i]
368 cur = self.positions[i]
369 end_cut = self.data.find(b'\n', cur)
369 end_cut = self.data.find(b'\n', cur)
370 if end_cut != -1:
370 if end_cut != -1:
371 end_cut += 1
371 end_cut += 1
372 offset += end_cut - cur
372 offset += end_cut - cur
373 l.append(self.data[last_cut:end_cut])
373 l.append(self.data[last_cut:end_cut])
374 else:
374 else:
375 while i < len(self.positions) and self.positions[i] < 0:
375 while i < len(self.positions) and self.positions[i] < 0:
376 cur = self.positions[i]
376 cur = self.positions[i]
377 t = self.extradata[-cur - 1]
377 t = self.extradata[-cur - 1]
378 l.append(self._pack(t))
378 l.append(self._pack(t))
379 self.positions[i] = offset
379 self.positions[i] = offset
380 if len(t[1]) > 20:
380 if len(t[1]) > 20:
381 self.extrainfo[i] = ord(t[1][21])
381 self.extrainfo[i] = ord(t[1][21])
382 offset += len(l[-1])
382 offset += len(l[-1])
383 i += 1
383 i += 1
384 self.data = b''.join(l)
384 self.data = b''.join(l)
385 self.hasremovals = False
385 self.hasremovals = False
386 self.extradata = []
386 self.extradata = []
387
387
388 def _pack(self, d):
388 def _pack(self, d):
389 return d[0] + b'\x00' + hex(d[1][:20]) + d[2] + b'\n'
389 return d[0] + b'\x00' + hex(d[1][:20]) + d[2] + b'\n'
390
390
391 def text(self):
391 def text(self):
392 self._compact()
392 self._compact()
393 return self.data
393 return self.data
394
394
395 def diff(self, m2, clean=False):
395 def diff(self, m2, clean=False):
396 '''Finds changes between the current manifest and m2.'''
396 '''Finds changes between the current manifest and m2.'''
397 # XXX think whether efficiency matters here
397 # XXX think whether efficiency matters here
398 diff = {}
398 diff = {}
399
399
400 for fn, e1, flags in self.iterentries():
400 for fn, e1, flags in self.iterentries():
401 if fn not in m2:
401 if fn not in m2:
402 diff[fn] = (e1, flags), (None, b'')
402 diff[fn] = (e1, flags), (None, b'')
403 else:
403 else:
404 e2 = m2[fn]
404 e2 = m2[fn]
405 if (e1, flags) != e2:
405 if (e1, flags) != e2:
406 diff[fn] = (e1, flags), e2
406 diff[fn] = (e1, flags), e2
407 elif clean:
407 elif clean:
408 diff[fn] = None
408 diff[fn] = None
409
409
410 for fn, e2, flags in m2.iterentries():
410 for fn, e2, flags in m2.iterentries():
411 if fn not in self:
411 if fn not in self:
412 diff[fn] = (None, b''), (e2, flags)
412 diff[fn] = (None, b''), (e2, flags)
413
413
414 return diff
414 return diff
415
415
416 def iterentries(self):
416 def iterentries(self):
417 return lazymanifestiterentries(self)
417 return lazymanifestiterentries(self)
418
418
419 def iterkeys(self):
419 def iterkeys(self):
420 return lazymanifestiter(self)
420 return lazymanifestiter(self)
421
421
422 def __iter__(self):
422 def __iter__(self):
423 return lazymanifestiter(self)
423 return lazymanifestiter(self)
424
424
425 def __len__(self):
425 def __len__(self):
426 return len(self.positions)
426 return len(self.positions)
427
427
428 def filtercopy(self, filterfn):
428 def filtercopy(self, filterfn):
429 # XXX should be optimized
429 # XXX should be optimized
430 c = _lazymanifest(b'')
430 c = _lazymanifest(b'')
431 for f, n, fl in self.iterentries():
431 for f, n, fl in self.iterentries():
432 if filterfn(f):
432 if filterfn(f):
433 c[f] = n, fl
433 c[f] = n, fl
434 return c
434 return c
435
435
436
436
437 try:
437 try:
438 _lazymanifest = parsers.lazymanifest
438 _lazymanifest = parsers.lazymanifest
439 except AttributeError:
439 except AttributeError:
440 pass
440 pass
441
441
442
442
443 @interfaceutil.implementer(repository.imanifestdict)
443 @interfaceutil.implementer(repository.imanifestdict)
444 class manifestdict(object):
444 class manifestdict(object):
445 def __init__(self, data=b''):
445 def __init__(self, data=b''):
446 self._lm = _lazymanifest(data)
446 self._lm = _lazymanifest(data)
447
447
448 def __getitem__(self, key):
448 def __getitem__(self, key):
449 return self._lm[key][0]
449 return self._lm[key][0]
450
450
451 def find(self, key):
451 def find(self, key):
452 return self._lm[key]
452 return self._lm[key]
453
453
454 def __len__(self):
454 def __len__(self):
455 return len(self._lm)
455 return len(self._lm)
456
456
457 def __nonzero__(self):
457 def __nonzero__(self):
458 # nonzero is covered by the __len__ function, but implementing it here
458 # nonzero is covered by the __len__ function, but implementing it here
459 # makes it easier for extensions to override.
459 # makes it easier for extensions to override.
460 return len(self._lm) != 0
460 return len(self._lm) != 0
461
461
462 __bool__ = __nonzero__
462 __bool__ = __nonzero__
463
463
464 def __setitem__(self, key, node):
464 def __setitem__(self, key, node):
465 self._lm[key] = node, self.flags(key)
465 self._lm[key] = node, self.flags(key)
466
466
467 def __contains__(self, key):
467 def __contains__(self, key):
468 if key is None:
468 if key is None:
469 return False
469 return False
470 return key in self._lm
470 return key in self._lm
471
471
472 def __delitem__(self, key):
472 def __delitem__(self, key):
473 del self._lm[key]
473 del self._lm[key]
474
474
475 def __iter__(self):
475 def __iter__(self):
476 return self._lm.__iter__()
476 return self._lm.__iter__()
477
477
478 def iterkeys(self):
478 def iterkeys(self):
479 return self._lm.iterkeys()
479 return self._lm.iterkeys()
480
480
481 def keys(self):
481 def keys(self):
482 return list(self.iterkeys())
482 return list(self.iterkeys())
483
483
484 def filesnotin(self, m2, match=None):
484 def filesnotin(self, m2, match=None):
485 '''Set of files in this manifest that are not in the other'''
485 '''Set of files in this manifest that are not in the other'''
486 if match is not None:
486 if match is not None:
487 match = matchmod.badmatch(match, lambda path, msg: None)
487 match = matchmod.badmatch(match, lambda path, msg: None)
488 sm2 = set(m2.walk(match))
488 sm2 = set(m2.walk(match))
489 return {f for f in self.walk(match) if f not in sm2}
489 return {f for f in self.walk(match) if f not in sm2}
490 return {f for f in self if f not in m2}
490 return {f for f in self if f not in m2}
491
491
492 @propertycache
492 @propertycache
493 def _dirs(self):
493 def _dirs(self):
494 return pathutil.dirs(self)
494 return pathutil.dirs(self)
495
495
496 def dirs(self):
496 def dirs(self):
497 return self._dirs
497 return self._dirs
498
498
499 def hasdir(self, dir):
499 def hasdir(self, dir):
500 return dir in self._dirs
500 return dir in self._dirs
501
501
502 def _filesfastpath(self, match):
502 def _filesfastpath(self, match):
503 '''Checks whether we can correctly and quickly iterate over matcher
503 '''Checks whether we can correctly and quickly iterate over matcher
504 files instead of over manifest files.'''
504 files instead of over manifest files.'''
505 files = match.files()
505 files = match.files()
506 return len(files) < 100 and (
506 return len(files) < 100 and (
507 match.isexact()
507 match.isexact()
508 or (match.prefix() and all(fn in self for fn in files))
508 or (match.prefix() and all(fn in self for fn in files))
509 )
509 )
510
510
511 def walk(self, match):
511 def walk(self, match):
512 '''Generates matching file names.
512 '''Generates matching file names.
513
513
514 Equivalent to manifest.matches(match).iterkeys(), but without creating
514 Equivalent to manifest.matches(match).iterkeys(), but without creating
515 an entirely new manifest.
515 an entirely new manifest.
516
516
517 It also reports nonexistent files by marking them bad with match.bad().
517 It also reports nonexistent files by marking them bad with match.bad().
518 '''
518 '''
519 if match.always():
519 if match.always():
520 for f in iter(self):
520 for f in iter(self):
521 yield f
521 yield f
522 return
522 return
523
523
524 fset = set(match.files())
524 fset = set(match.files())
525
525
526 # avoid the entire walk if we're only looking for specific files
526 # avoid the entire walk if we're only looking for specific files
527 if self._filesfastpath(match):
527 if self._filesfastpath(match):
528 for fn in sorted(fset):
528 for fn in sorted(fset):
529 if fn in self:
529 if fn in self:
530 yield fn
530 yield fn
531 return
531 return
532
532
533 for fn in self:
533 for fn in self:
534 if fn in fset:
534 if fn in fset:
535 # specified pattern is the exact name
535 # specified pattern is the exact name
536 fset.remove(fn)
536 fset.remove(fn)
537 if match(fn):
537 if match(fn):
538 yield fn
538 yield fn
539
539
540 # for dirstate.walk, files=[''] means "walk the whole tree".
540 # for dirstate.walk, files=[''] means "walk the whole tree".
541 # follow that here, too
541 # follow that here, too
542 fset.discard(b'')
542 fset.discard(b'')
543
543
544 for fn in sorted(fset):
544 for fn in sorted(fset):
545 if not self.hasdir(fn):
545 if not self.hasdir(fn):
546 match.bad(fn, None)
546 match.bad(fn, None)
547
547
548 def _matches(self, match):
548 def _matches(self, match):
549 '''generate a new manifest filtered by the match argument'''
549 '''generate a new manifest filtered by the match argument'''
550 if match.always():
550 if match.always():
551 return self.copy()
551 return self.copy()
552
552
553 if self._filesfastpath(match):
553 if self._filesfastpath(match):
554 m = manifestdict()
554 m = manifestdict()
555 lm = self._lm
555 lm = self._lm
556 for fn in match.files():
556 for fn in match.files():
557 if fn in lm:
557 if fn in lm:
558 m._lm[fn] = lm[fn]
558 m._lm[fn] = lm[fn]
559 return m
559 return m
560
560
561 m = manifestdict()
561 m = manifestdict()
562 m._lm = self._lm.filtercopy(match)
562 m._lm = self._lm.filtercopy(match)
563 return m
563 return m
564
564
565 def diff(self, m2, match=None, clean=False):
565 def diff(self, m2, match=None, clean=False):
566 '''Finds changes between the current manifest and m2.
566 '''Finds changes between the current manifest and m2.
567
567
568 Args:
568 Args:
569 m2: the manifest to which this manifest should be compared.
569 m2: the manifest to which this manifest should be compared.
570 clean: if true, include files unchanged between these manifests
570 clean: if true, include files unchanged between these manifests
571 with a None value in the returned dictionary.
571 with a None value in the returned dictionary.
572
572
573 The result is returned as a dict with filename as key and
573 The result is returned as a dict with filename as key and
574 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
574 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
575 nodeid in the current/other manifest and fl1/fl2 is the flag
575 nodeid in the current/other manifest and fl1/fl2 is the flag
576 in the current/other manifest. Where the file does not exist,
576 in the current/other manifest. Where the file does not exist,
577 the nodeid will be None and the flags will be the empty
577 the nodeid will be None and the flags will be the empty
578 string.
578 string.
579 '''
579 '''
580 if match:
580 if match:
581 m1 = self._matches(match)
581 m1 = self._matches(match)
582 m2 = m2._matches(match)
582 m2 = m2._matches(match)
583 return m1.diff(m2, clean=clean)
583 return m1.diff(m2, clean=clean)
584 return self._lm.diff(m2._lm, clean)
584 return self._lm.diff(m2._lm, clean)
585
585
586 def setflag(self, key, flag):
586 def setflag(self, key, flag):
587 self._lm[key] = self[key], flag
587 self._lm[key] = self[key], flag
588
588
589 def get(self, key, default=None):
589 def get(self, key, default=None):
590 try:
590 try:
591 return self._lm[key][0]
591 return self._lm[key][0]
592 except KeyError:
592 except KeyError:
593 return default
593 return default
594
594
595 def flags(self, key):
595 def flags(self, key):
596 try:
596 try:
597 return self._lm[key][1]
597 return self._lm[key][1]
598 except KeyError:
598 except KeyError:
599 return b''
599 return b''
600
600
601 def copy(self):
601 def copy(self):
602 c = manifestdict()
602 c = manifestdict()
603 c._lm = self._lm.copy()
603 c._lm = self._lm.copy()
604 return c
604 return c
605
605
606 def items(self):
606 def items(self):
607 return (x[:2] for x in self._lm.iterentries())
607 return (x[:2] for x in self._lm.iterentries())
608
608
609 def iteritems(self):
609 def iteritems(self):
610 return (x[:2] for x in self._lm.iterentries())
610 return (x[:2] for x in self._lm.iterentries())
611
611
612 def iterentries(self):
612 def iterentries(self):
613 return self._lm.iterentries()
613 return self._lm.iterentries()
614
614
615 def text(self):
615 def text(self):
616 # most likely uses native version
616 # most likely uses native version
617 return self._lm.text()
617 return self._lm.text()
618
618
619 def fastdelta(self, base, changes):
619 def fastdelta(self, base, changes):
620 """Given a base manifest text as a bytearray and a list of changes
620 """Given a base manifest text as a bytearray and a list of changes
621 relative to that text, compute a delta that can be used by revlog.
621 relative to that text, compute a delta that can be used by revlog.
622 """
622 """
623 delta = []
623 delta = []
624 dstart = None
624 dstart = None
625 dend = None
625 dend = None
626 dline = [b""]
626 dline = [b""]
627 start = 0
627 start = 0
628 # zero copy representation of base as a buffer
628 # zero copy representation of base as a buffer
629 addbuf = util.buffer(base)
629 addbuf = util.buffer(base)
630
630
631 changes = list(changes)
631 changes = list(changes)
632 if len(changes) < FASTDELTA_TEXTDIFF_THRESHOLD:
632 if len(changes) < FASTDELTA_TEXTDIFF_THRESHOLD:
633 # start with a readonly loop that finds the offset of
633 # start with a readonly loop that finds the offset of
634 # each line and creates the deltas
634 # each line and creates the deltas
635 for f, todelete in changes:
635 for f, todelete in changes:
636 # bs will either be the index of the item or the insert point
636 # bs will either be the index of the item or the insert point
637 start, end = _msearch(addbuf, f, start)
637 start, end = _msearch(addbuf, f, start)
638 if not todelete:
638 if not todelete:
639 h, fl = self._lm[f]
639 h, fl = self._lm[f]
640 l = b"%s\0%s%s\n" % (f, hex(h), fl)
640 l = b"%s\0%s%s\n" % (f, hex(h), fl)
641 else:
641 else:
642 if start == end:
642 if start == end:
643 # item we want to delete was not found, error out
643 # item we want to delete was not found, error out
644 raise AssertionError(
644 raise AssertionError(
645 _(b"failed to remove %s from manifest") % f
645 _(b"failed to remove %s from manifest") % f
646 )
646 )
647 l = b""
647 l = b""
648 if dstart is not None and dstart <= start and dend >= start:
648 if dstart is not None and dstart <= start and dend >= start:
649 if dend < end:
649 if dend < end:
650 dend = end
650 dend = end
651 if l:
651 if l:
652 dline.append(l)
652 dline.append(l)
653 else:
653 else:
654 if dstart is not None:
654 if dstart is not None:
655 delta.append([dstart, dend, b"".join(dline)])
655 delta.append([dstart, dend, b"".join(dline)])
656 dstart = start
656 dstart = start
657 dend = end
657 dend = end
658 dline = [l]
658 dline = [l]
659
659
660 if dstart is not None:
660 if dstart is not None:
661 delta.append([dstart, dend, b"".join(dline)])
661 delta.append([dstart, dend, b"".join(dline)])
662 # apply the delta to the base, and get a delta for addrevision
662 # apply the delta to the base, and get a delta for addrevision
663 deltatext, arraytext = _addlistdelta(base, delta)
663 deltatext, arraytext = _addlistdelta(base, delta)
664 else:
664 else:
665 # For large changes, it's much cheaper to just build the text and
665 # For large changes, it's much cheaper to just build the text and
666 # diff it.
666 # diff it.
667 arraytext = bytearray(self.text())
667 arraytext = bytearray(self.text())
668 deltatext = mdiff.textdiff(
668 deltatext = mdiff.textdiff(
669 util.buffer(base), util.buffer(arraytext)
669 util.buffer(base), util.buffer(arraytext)
670 )
670 )
671
671
672 return arraytext, deltatext
672 return arraytext, deltatext
673
673
674
674
675 def _msearch(m, s, lo=0, hi=None):
675 def _msearch(m, s, lo=0, hi=None):
676 '''return a tuple (start, end) that says where to find s within m.
676 '''return a tuple (start, end) that says where to find s within m.
677
677
678 If the string is found m[start:end] are the line containing
678 If the string is found m[start:end] are the line containing
679 that string. If start == end the string was not found and
679 that string. If start == end the string was not found and
680 they indicate the proper sorted insertion point.
680 they indicate the proper sorted insertion point.
681
681
682 m should be a buffer, a memoryview or a byte string.
682 m should be a buffer, a memoryview or a byte string.
683 s is a byte string'''
683 s is a byte string'''
684
684
685 def advance(i, c):
685 def advance(i, c):
686 while i < lenm and m[i : i + 1] != c:
686 while i < lenm and m[i : i + 1] != c:
687 i += 1
687 i += 1
688 return i
688 return i
689
689
690 if not s:
690 if not s:
691 return (lo, lo)
691 return (lo, lo)
692 lenm = len(m)
692 lenm = len(m)
693 if not hi:
693 if not hi:
694 hi = lenm
694 hi = lenm
695 while lo < hi:
695 while lo < hi:
696 mid = (lo + hi) // 2
696 mid = (lo + hi) // 2
697 start = mid
697 start = mid
698 while start > 0 and m[start - 1 : start] != b'\n':
698 while start > 0 and m[start - 1 : start] != b'\n':
699 start -= 1
699 start -= 1
700 end = advance(start, b'\0')
700 end = advance(start, b'\0')
701 if bytes(m[start:end]) < s:
701 if bytes(m[start:end]) < s:
702 # we know that after the null there are 40 bytes of sha1
702 # we know that after the null there are 40 bytes of sha1
703 # this translates to the bisect lo = mid + 1
703 # this translates to the bisect lo = mid + 1
704 lo = advance(end + 40, b'\n') + 1
704 lo = advance(end + 40, b'\n') + 1
705 else:
705 else:
706 # this translates to the bisect hi = mid
706 # this translates to the bisect hi = mid
707 hi = start
707 hi = start
708 end = advance(lo, b'\0')
708 end = advance(lo, b'\0')
709 found = m[lo:end]
709 found = m[lo:end]
710 if s == found:
710 if s == found:
711 # we know that after the null there are 40 bytes of sha1
711 # we know that after the null there are 40 bytes of sha1
712 end = advance(end + 40, b'\n')
712 end = advance(end + 40, b'\n')
713 return (lo, end + 1)
713 return (lo, end + 1)
714 else:
714 else:
715 return (lo, lo)
715 return (lo, lo)
716
716
717
717
718 def _checkforbidden(l):
718 def _checkforbidden(l):
719 """Check filenames for illegal characters."""
719 """Check filenames for illegal characters."""
720 for f in l:
720 for f in l:
721 if b'\n' in f or b'\r' in f:
721 if b'\n' in f or b'\r' in f:
722 raise error.StorageError(
722 raise error.StorageError(
723 _(b"'\\n' and '\\r' disallowed in filenames: %r")
723 _(b"'\\n' and '\\r' disallowed in filenames: %r")
724 % pycompat.bytestr(f)
724 % pycompat.bytestr(f)
725 )
725 )
726
726
727
727
728 # apply the changes collected during the bisect loop to our addlist
728 # apply the changes collected during the bisect loop to our addlist
729 # return a delta suitable for addrevision
729 # return a delta suitable for addrevision
730 def _addlistdelta(addlist, x):
730 def _addlistdelta(addlist, x):
731 # for large addlist arrays, building a new array is cheaper
731 # for large addlist arrays, building a new array is cheaper
732 # than repeatedly modifying the existing one
732 # than repeatedly modifying the existing one
733 currentposition = 0
733 currentposition = 0
734 newaddlist = bytearray()
734 newaddlist = bytearray()
735
735
736 for start, end, content in x:
736 for start, end, content in x:
737 newaddlist += addlist[currentposition:start]
737 newaddlist += addlist[currentposition:start]
738 if content:
738 if content:
739 newaddlist += bytearray(content)
739 newaddlist += bytearray(content)
740
740
741 currentposition = end
741 currentposition = end
742
742
743 newaddlist += addlist[currentposition:]
743 newaddlist += addlist[currentposition:]
744
744
745 deltatext = b"".join(
745 deltatext = b"".join(
746 struct.pack(b">lll", start, end, len(content)) + content
746 struct.pack(b">lll", start, end, len(content)) + content
747 for start, end, content in x
747 for start, end, content in x
748 )
748 )
749 return deltatext, newaddlist
749 return deltatext, newaddlist
750
750
751
751
752 def _splittopdir(f):
752 def _splittopdir(f):
753 if b'/' in f:
753 if b'/' in f:
754 dir, subpath = f.split(b'/', 1)
754 dir, subpath = f.split(b'/', 1)
755 return dir + b'/', subpath
755 return dir + b'/', subpath
756 else:
756 else:
757 return b'', f
757 return b'', f
758
758
759
759
760 _noop = lambda s: None
760 _noop = lambda s: None
761
761
762
762
763 class treemanifest(object):
763 class treemanifest(object):
764 def __init__(self, dir=b'', text=b''):
764 def __init__(self, dir=b'', text=b''):
765 self._dir = dir
765 self._dir = dir
766 self._node = nullid
766 self._node = nullid
767 self._loadfunc = _noop
767 self._loadfunc = _noop
768 self._copyfunc = _noop
768 self._copyfunc = _noop
769 self._dirty = False
769 self._dirty = False
770 self._dirs = {}
770 self._dirs = {}
771 self._lazydirs = {}
771 self._lazydirs = {}
772 # Using _lazymanifest here is a little slower than plain old dicts
772 # Using _lazymanifest here is a little slower than plain old dicts
773 self._files = {}
773 self._files = {}
774 self._flags = {}
774 self._flags = {}
775 if text:
775 if text:
776
776
777 def readsubtree(subdir, subm):
777 def readsubtree(subdir, subm):
778 raise AssertionError(
778 raise AssertionError(
779 b'treemanifest constructor only accepts flat manifests'
779 b'treemanifest constructor only accepts flat manifests'
780 )
780 )
781
781
782 self.parse(text, readsubtree)
782 self.parse(text, readsubtree)
783 self._dirty = True # Mark flat manifest dirty after parsing
783 self._dirty = True # Mark flat manifest dirty after parsing
784
784
785 def _subpath(self, path):
785 def _subpath(self, path):
786 return self._dir + path
786 return self._dir + path
787
787
788 def _loadalllazy(self):
788 def _loadalllazy(self):
789 selfdirs = self._dirs
789 selfdirs = self._dirs
790 for d, (path, node, readsubtree, docopy) in pycompat.iteritems(
790 for d, (path, node, readsubtree, docopy) in pycompat.iteritems(
791 self._lazydirs
791 self._lazydirs
792 ):
792 ):
793 if docopy:
793 if docopy:
794 selfdirs[d] = readsubtree(path, node).copy()
794 selfdirs[d] = readsubtree(path, node).copy()
795 else:
795 else:
796 selfdirs[d] = readsubtree(path, node)
796 selfdirs[d] = readsubtree(path, node)
797 self._lazydirs = {}
797 self._lazydirs = {}
798
798
799 def _loadlazy(self, d):
799 def _loadlazy(self, d):
800 v = self._lazydirs.get(d)
800 v = self._lazydirs.get(d)
801 if v:
801 if v:
802 path, node, readsubtree, docopy = v
802 path, node, readsubtree, docopy = v
803 if docopy:
803 if docopy:
804 self._dirs[d] = readsubtree(path, node).copy()
804 self._dirs[d] = readsubtree(path, node).copy()
805 else:
805 else:
806 self._dirs[d] = readsubtree(path, node)
806 self._dirs[d] = readsubtree(path, node)
807 del self._lazydirs[d]
807 del self._lazydirs[d]
808
808
809 def _loadchildrensetlazy(self, visit):
809 def _loadchildrensetlazy(self, visit):
810 if not visit:
810 if not visit:
811 return None
811 return None
812 if visit == b'all' or visit == b'this':
812 if visit == b'all' or visit == b'this':
813 self._loadalllazy()
813 self._loadalllazy()
814 return None
814 return None
815
815
816 loadlazy = self._loadlazy
816 loadlazy = self._loadlazy
817 for k in visit:
817 for k in visit:
818 loadlazy(k + b'/')
818 loadlazy(k + b'/')
819 return visit
819 return visit
820
820
821 def _loaddifflazy(self, t1, t2):
821 def _loaddifflazy(self, t1, t2):
822 """load items in t1 and t2 if they're needed for diffing.
822 """load items in t1 and t2 if they're needed for diffing.
823
823
824 The criteria currently is:
824 The criteria currently is:
825 - if it's not present in _lazydirs in either t1 or t2, load it in the
825 - if it's not present in _lazydirs in either t1 or t2, load it in the
826 other (it may already be loaded or it may not exist, doesn't matter)
826 other (it may already be loaded or it may not exist, doesn't matter)
827 - if it's present in _lazydirs in both, compare the nodeid; if it
827 - if it's present in _lazydirs in both, compare the nodeid; if it
828 differs, load it in both
828 differs, load it in both
829 """
829 """
830 toloadlazy = []
830 toloadlazy = []
831 for d, v1 in pycompat.iteritems(t1._lazydirs):
831 for d, v1 in pycompat.iteritems(t1._lazydirs):
832 v2 = t2._lazydirs.get(d)
832 v2 = t2._lazydirs.get(d)
833 if not v2 or v2[1] != v1[1]:
833 if not v2 or v2[1] != v1[1]:
834 toloadlazy.append(d)
834 toloadlazy.append(d)
835 for d, v1 in pycompat.iteritems(t2._lazydirs):
835 for d, v1 in pycompat.iteritems(t2._lazydirs):
836 if d not in t1._lazydirs:
836 if d not in t1._lazydirs:
837 toloadlazy.append(d)
837 toloadlazy.append(d)
838
838
839 for d in toloadlazy:
839 for d in toloadlazy:
840 t1._loadlazy(d)
840 t1._loadlazy(d)
841 t2._loadlazy(d)
841 t2._loadlazy(d)
842
842
843 def __len__(self):
843 def __len__(self):
844 self._load()
844 self._load()
845 size = len(self._files)
845 size = len(self._files)
846 self._loadalllazy()
846 self._loadalllazy()
847 for m in self._dirs.values():
847 for m in self._dirs.values():
848 size += m.__len__()
848 size += m.__len__()
849 return size
849 return size
850
850
851 def __nonzero__(self):
851 def __nonzero__(self):
852 # Faster than "__len() != 0" since it avoids loading sub-manifests
852 # Faster than "__len() != 0" since it avoids loading sub-manifests
853 return not self._isempty()
853 return not self._isempty()
854
854
855 __bool__ = __nonzero__
855 __bool__ = __nonzero__
856
856
857 def _isempty(self):
857 def _isempty(self):
858 self._load() # for consistency; already loaded by all callers
858 self._load() # for consistency; already loaded by all callers
859 # See if we can skip loading everything.
859 # See if we can skip loading everything.
860 if self._files or (
860 if self._files or (
861 self._dirs and any(not m._isempty() for m in self._dirs.values())
861 self._dirs and any(not m._isempty() for m in self._dirs.values())
862 ):
862 ):
863 return False
863 return False
864 self._loadalllazy()
864 self._loadalllazy()
865 return not self._dirs or all(m._isempty() for m in self._dirs.values())
865 return not self._dirs or all(m._isempty() for m in self._dirs.values())
866
866
867 @encoding.strmethod
867 @encoding.strmethod
868 def __repr__(self):
868 def __repr__(self):
869 return (
869 return (
870 b'<treemanifest dir=%s, node=%s, loaded=%r, dirty=%r at 0x%x>'
870 b'<treemanifest dir=%s, node=%s, loaded=%r, dirty=%r at 0x%x>'
871 % (
871 % (
872 self._dir,
872 self._dir,
873 hex(self._node),
873 hex(self._node),
874 bool(self._loadfunc is _noop),
874 bool(self._loadfunc is _noop),
875 self._dirty,
875 self._dirty,
876 id(self),
876 id(self),
877 )
877 )
878 )
878 )
879
879
880 def dir(self):
880 def dir(self):
881 '''The directory that this tree manifest represents, including a
881 '''The directory that this tree manifest represents, including a
882 trailing '/'. Empty string for the repo root directory.'''
882 trailing '/'. Empty string for the repo root directory.'''
883 return self._dir
883 return self._dir
884
884
885 def node(self):
885 def node(self):
886 '''This node of this instance. nullid for unsaved instances. Should
886 '''This node of this instance. nullid for unsaved instances. Should
887 be updated when the instance is read or written from a revlog.
887 be updated when the instance is read or written from a revlog.
888 '''
888 '''
889 assert not self._dirty
889 assert not self._dirty
890 return self._node
890 return self._node
891
891
892 def setnode(self, node):
892 def setnode(self, node):
893 self._node = node
893 self._node = node
894 self._dirty = False
894 self._dirty = False
895
895
896 def iterentries(self):
896 def iterentries(self):
897 self._load()
897 self._load()
898 self._loadalllazy()
898 self._loadalllazy()
899 for p, n in sorted(
899 for p, n in sorted(
900 itertools.chain(self._dirs.items(), self._files.items())
900 itertools.chain(self._dirs.items(), self._files.items())
901 ):
901 ):
902 if p in self._files:
902 if p in self._files:
903 yield self._subpath(p), n, self._flags.get(p, b'')
903 yield self._subpath(p), n, self._flags.get(p, b'')
904 else:
904 else:
905 for x in n.iterentries():
905 for x in n.iterentries():
906 yield x
906 yield x
907
907
908 def items(self):
908 def items(self):
909 self._load()
909 self._load()
910 self._loadalllazy()
910 self._loadalllazy()
911 for p, n in sorted(
911 for p, n in sorted(
912 itertools.chain(self._dirs.items(), self._files.items())
912 itertools.chain(self._dirs.items(), self._files.items())
913 ):
913 ):
914 if p in self._files:
914 if p in self._files:
915 yield self._subpath(p), n
915 yield self._subpath(p), n
916 else:
916 else:
917 for f, sn in pycompat.iteritems(n):
917 for f, sn in pycompat.iteritems(n):
918 yield f, sn
918 yield f, sn
919
919
920 iteritems = items
920 iteritems = items
921
921
922 def iterkeys(self):
922 def iterkeys(self):
923 self._load()
923 self._load()
924 self._loadalllazy()
924 self._loadalllazy()
925 for p in sorted(itertools.chain(self._dirs, self._files)):
925 for p in sorted(itertools.chain(self._dirs, self._files)):
926 if p in self._files:
926 if p in self._files:
927 yield self._subpath(p)
927 yield self._subpath(p)
928 else:
928 else:
929 for f in self._dirs[p]:
929 for f in self._dirs[p]:
930 yield f
930 yield f
931
931
932 def keys(self):
932 def keys(self):
933 return list(self.iterkeys())
933 return list(self.iterkeys())
934
934
935 def __iter__(self):
935 def __iter__(self):
936 return self.iterkeys()
936 return self.iterkeys()
937
937
938 def __contains__(self, f):
938 def __contains__(self, f):
939 if f is None:
939 if f is None:
940 return False
940 return False
941 self._load()
941 self._load()
942 dir, subpath = _splittopdir(f)
942 dir, subpath = _splittopdir(f)
943 if dir:
943 if dir:
944 self._loadlazy(dir)
944 self._loadlazy(dir)
945
945
946 if dir not in self._dirs:
946 if dir not in self._dirs:
947 return False
947 return False
948
948
949 return self._dirs[dir].__contains__(subpath)
949 return self._dirs[dir].__contains__(subpath)
950 else:
950 else:
951 return f in self._files
951 return f in self._files
952
952
953 def get(self, f, default=None):
953 def get(self, f, default=None):
954 self._load()
954 self._load()
955 dir, subpath = _splittopdir(f)
955 dir, subpath = _splittopdir(f)
956 if dir:
956 if dir:
957 self._loadlazy(dir)
957 self._loadlazy(dir)
958
958
959 if dir not in self._dirs:
959 if dir not in self._dirs:
960 return default
960 return default
961 return self._dirs[dir].get(subpath, default)
961 return self._dirs[dir].get(subpath, default)
962 else:
962 else:
963 return self._files.get(f, default)
963 return self._files.get(f, default)
964
964
965 def __getitem__(self, f):
965 def __getitem__(self, f):
966 self._load()
966 self._load()
967 dir, subpath = _splittopdir(f)
967 dir, subpath = _splittopdir(f)
968 if dir:
968 if dir:
969 self._loadlazy(dir)
969 self._loadlazy(dir)
970
970
971 return self._dirs[dir].__getitem__(subpath)
971 return self._dirs[dir].__getitem__(subpath)
972 else:
972 else:
973 return self._files[f]
973 return self._files[f]
974
974
975 def flags(self, f):
975 def flags(self, f):
976 self._load()
976 self._load()
977 dir, subpath = _splittopdir(f)
977 dir, subpath = _splittopdir(f)
978 if dir:
978 if dir:
979 self._loadlazy(dir)
979 self._loadlazy(dir)
980
980
981 if dir not in self._dirs:
981 if dir not in self._dirs:
982 return b''
982 return b''
983 return self._dirs[dir].flags(subpath)
983 return self._dirs[dir].flags(subpath)
984 else:
984 else:
985 if f in self._lazydirs or f in self._dirs:
985 if f in self._lazydirs or f in self._dirs:
986 return b''
986 return b''
987 return self._flags.get(f, b'')
987 return self._flags.get(f, b'')
988
988
989 def find(self, f):
989 def find(self, f):
990 self._load()
990 self._load()
991 dir, subpath = _splittopdir(f)
991 dir, subpath = _splittopdir(f)
992 if dir:
992 if dir:
993 self._loadlazy(dir)
993 self._loadlazy(dir)
994
994
995 return self._dirs[dir].find(subpath)
995 return self._dirs[dir].find(subpath)
996 else:
996 else:
997 return self._files[f], self._flags.get(f, b'')
997 return self._files[f], self._flags.get(f, b'')
998
998
999 def __delitem__(self, f):
999 def __delitem__(self, f):
1000 self._load()
1000 self._load()
1001 dir, subpath = _splittopdir(f)
1001 dir, subpath = _splittopdir(f)
1002 if dir:
1002 if dir:
1003 self._loadlazy(dir)
1003 self._loadlazy(dir)
1004
1004
1005 self._dirs[dir].__delitem__(subpath)
1005 self._dirs[dir].__delitem__(subpath)
1006 # If the directory is now empty, remove it
1006 # If the directory is now empty, remove it
1007 if self._dirs[dir]._isempty():
1007 if self._dirs[dir]._isempty():
1008 del self._dirs[dir]
1008 del self._dirs[dir]
1009 else:
1009 else:
1010 del self._files[f]
1010 del self._files[f]
1011 if f in self._flags:
1011 if f in self._flags:
1012 del self._flags[f]
1012 del self._flags[f]
1013 self._dirty = True
1013 self._dirty = True
1014
1014
1015 def __setitem__(self, f, n):
1015 def __setitem__(self, f, n):
1016 assert n is not None
1016 assert n is not None
1017 self._load()
1017 self._load()
1018 dir, subpath = _splittopdir(f)
1018 dir, subpath = _splittopdir(f)
1019 if dir:
1019 if dir:
1020 self._loadlazy(dir)
1020 self._loadlazy(dir)
1021 if dir not in self._dirs:
1021 if dir not in self._dirs:
1022 self._dirs[dir] = treemanifest(self._subpath(dir))
1022 self._dirs[dir] = treemanifest(self._subpath(dir))
1023 self._dirs[dir].__setitem__(subpath, n)
1023 self._dirs[dir].__setitem__(subpath, n)
1024 else:
1024 else:
1025 self._files[f] = n[:21] # to match manifestdict's behavior
1025 self._files[f] = n[:21] # to match manifestdict's behavior
1026 self._dirty = True
1026 self._dirty = True
1027
1027
1028 def _load(self):
1028 def _load(self):
1029 if self._loadfunc is not _noop:
1029 if self._loadfunc is not _noop:
1030 lf, self._loadfunc = self._loadfunc, _noop
1030 lf, self._loadfunc = self._loadfunc, _noop
1031 lf(self)
1031 lf(self)
1032 elif self._copyfunc is not _noop:
1032 elif self._copyfunc is not _noop:
1033 cf, self._copyfunc = self._copyfunc, _noop
1033 cf, self._copyfunc = self._copyfunc, _noop
1034 cf(self)
1034 cf(self)
1035
1035
1036 def setflag(self, f, flags):
1036 def setflag(self, f, flags):
1037 """Set the flags (symlink, executable) for path f."""
1037 """Set the flags (symlink, executable) for path f."""
1038 self._load()
1038 self._load()
1039 dir, subpath = _splittopdir(f)
1039 dir, subpath = _splittopdir(f)
1040 if dir:
1040 if dir:
1041 self._loadlazy(dir)
1041 self._loadlazy(dir)
1042 if dir not in self._dirs:
1042 if dir not in self._dirs:
1043 self._dirs[dir] = treemanifest(self._subpath(dir))
1043 self._dirs[dir] = treemanifest(self._subpath(dir))
1044 self._dirs[dir].setflag(subpath, flags)
1044 self._dirs[dir].setflag(subpath, flags)
1045 else:
1045 else:
1046 self._flags[f] = flags
1046 self._flags[f] = flags
1047 self._dirty = True
1047 self._dirty = True
1048
1048
1049 def copy(self):
1049 def copy(self):
1050 copy = treemanifest(self._dir)
1050 copy = treemanifest(self._dir)
1051 copy._node = self._node
1051 copy._node = self._node
1052 copy._dirty = self._dirty
1052 copy._dirty = self._dirty
1053 if self._copyfunc is _noop:
1053 if self._copyfunc is _noop:
1054
1054
1055 def _copyfunc(s):
1055 def _copyfunc(s):
1056 self._load()
1056 self._load()
1057 s._lazydirs = {
1057 s._lazydirs = {
1058 d: (p, n, r, True)
1058 d: (p, n, r, True)
1059 for d, (p, n, r, c) in pycompat.iteritems(self._lazydirs)
1059 for d, (p, n, r, c) in pycompat.iteritems(self._lazydirs)
1060 }
1060 }
1061 sdirs = s._dirs
1061 sdirs = s._dirs
1062 for d, v in pycompat.iteritems(self._dirs):
1062 for d, v in pycompat.iteritems(self._dirs):
1063 sdirs[d] = v.copy()
1063 sdirs[d] = v.copy()
1064 s._files = dict.copy(self._files)
1064 s._files = dict.copy(self._files)
1065 s._flags = dict.copy(self._flags)
1065 s._flags = dict.copy(self._flags)
1066
1066
1067 if self._loadfunc is _noop:
1067 if self._loadfunc is _noop:
1068 _copyfunc(copy)
1068 _copyfunc(copy)
1069 else:
1069 else:
1070 copy._copyfunc = _copyfunc
1070 copy._copyfunc = _copyfunc
1071 else:
1071 else:
1072 copy._copyfunc = self._copyfunc
1072 copy._copyfunc = self._copyfunc
1073 return copy
1073 return copy
1074
1074
1075 def filesnotin(self, m2, match=None):
1075 def filesnotin(self, m2, match=None):
1076 '''Set of files in this manifest that are not in the other'''
1076 '''Set of files in this manifest that are not in the other'''
1077 if match and not match.always():
1077 if match and not match.always():
1078 m1 = self._matches(match)
1078 m1 = self._matches(match)
1079 m2 = m2._matches(match)
1079 m2 = m2._matches(match)
1080 return m1.filesnotin(m2)
1080 return m1.filesnotin(m2)
1081
1081
1082 files = set()
1082 files = set()
1083
1083
1084 def _filesnotin(t1, t2):
1084 def _filesnotin(t1, t2):
1085 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1085 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1086 return
1086 return
1087 t1._load()
1087 t1._load()
1088 t2._load()
1088 t2._load()
1089 self._loaddifflazy(t1, t2)
1089 self._loaddifflazy(t1, t2)
1090 for d, m1 in pycompat.iteritems(t1._dirs):
1090 for d, m1 in pycompat.iteritems(t1._dirs):
1091 if d in t2._dirs:
1091 if d in t2._dirs:
1092 m2 = t2._dirs[d]
1092 m2 = t2._dirs[d]
1093 _filesnotin(m1, m2)
1093 _filesnotin(m1, m2)
1094 else:
1094 else:
1095 files.update(m1.iterkeys())
1095 files.update(m1.iterkeys())
1096
1096
1097 for fn in t1._files:
1097 for fn in t1._files:
1098 if fn not in t2._files:
1098 if fn not in t2._files:
1099 files.add(t1._subpath(fn))
1099 files.add(t1._subpath(fn))
1100
1100
1101 _filesnotin(self, m2)
1101 _filesnotin(self, m2)
1102 return files
1102 return files
1103
1103
1104 @propertycache
1104 @propertycache
1105 def _alldirs(self):
1105 def _alldirs(self):
1106 return pathutil.dirs(self)
1106 return pathutil.dirs(self)
1107
1107
1108 def dirs(self):
1108 def dirs(self):
1109 return self._alldirs
1109 return self._alldirs
1110
1110
1111 def hasdir(self, dir):
1111 def hasdir(self, dir):
1112 self._load()
1112 self._load()
1113 topdir, subdir = _splittopdir(dir)
1113 topdir, subdir = _splittopdir(dir)
1114 if topdir:
1114 if topdir:
1115 self._loadlazy(topdir)
1115 self._loadlazy(topdir)
1116 if topdir in self._dirs:
1116 if topdir in self._dirs:
1117 return self._dirs[topdir].hasdir(subdir)
1117 return self._dirs[topdir].hasdir(subdir)
1118 return False
1118 return False
1119 dirslash = dir + b'/'
1119 dirslash = dir + b'/'
1120 return dirslash in self._dirs or dirslash in self._lazydirs
1120 return dirslash in self._dirs or dirslash in self._lazydirs
1121
1121
1122 def walk(self, match):
1122 def walk(self, match):
1123 '''Generates matching file names.
1123 '''Generates matching file names.
1124
1124
1125 It also reports nonexistent files by marking them bad with match.bad().
1125 It also reports nonexistent files by marking them bad with match.bad().
1126 '''
1126 '''
1127 if match.always():
1127 if match.always():
1128 for f in iter(self):
1128 for f in iter(self):
1129 yield f
1129 yield f
1130 return
1130 return
1131
1131
1132 fset = set(match.files())
1132 fset = set(match.files())
1133
1133
1134 for fn in self._walk(match):
1134 for fn in self._walk(match):
1135 if fn in fset:
1135 if fn in fset:
1136 # specified pattern is the exact name
1136 # specified pattern is the exact name
1137 fset.remove(fn)
1137 fset.remove(fn)
1138 yield fn
1138 yield fn
1139
1139
1140 # for dirstate.walk, files=[''] means "walk the whole tree".
1140 # for dirstate.walk, files=[''] means "walk the whole tree".
1141 # follow that here, too
1141 # follow that here, too
1142 fset.discard(b'')
1142 fset.discard(b'')
1143
1143
1144 for fn in sorted(fset):
1144 for fn in sorted(fset):
1145 if not self.hasdir(fn):
1145 if not self.hasdir(fn):
1146 match.bad(fn, None)
1146 match.bad(fn, None)
1147
1147
1148 def _walk(self, match):
1148 def _walk(self, match):
1149 '''Recursively generates matching file names for walk().'''
1149 '''Recursively generates matching file names for walk().'''
1150 visit = match.visitchildrenset(self._dir[:-1])
1150 visit = match.visitchildrenset(self._dir[:-1])
1151 if not visit:
1151 if not visit:
1152 return
1152 return
1153
1153
1154 # yield this dir's files and walk its submanifests
1154 # yield this dir's files and walk its submanifests
1155 self._load()
1155 self._load()
1156 visit = self._loadchildrensetlazy(visit)
1156 visit = self._loadchildrensetlazy(visit)
1157 for p in sorted(list(self._dirs) + list(self._files)):
1157 for p in sorted(list(self._dirs) + list(self._files)):
1158 if p in self._files:
1158 if p in self._files:
1159 fullp = self._subpath(p)
1159 fullp = self._subpath(p)
1160 if match(fullp):
1160 if match(fullp):
1161 yield fullp
1161 yield fullp
1162 else:
1162 else:
1163 if not visit or p[:-1] in visit:
1163 if not visit or p[:-1] in visit:
1164 for f in self._dirs[p]._walk(match):
1164 for f in self._dirs[p]._walk(match):
1165 yield f
1165 yield f
1166
1166
1167 def _matches(self, match):
1167 def _matches(self, match):
1168 '''recursively generate a new manifest filtered by the match argument.
1168 '''recursively generate a new manifest filtered by the match argument.
1169 '''
1169 '''
1170 if match.always():
1170 if match.always():
1171 return self.copy()
1171 return self.copy()
1172 return self._matches_inner(match)
1172 return self._matches_inner(match)
1173
1173
1174 def _matches_inner(self, match):
1174 def _matches_inner(self, match):
1175 if match.always():
1175 if match.always():
1176 return self.copy()
1176 return self.copy()
1177
1177
1178 visit = match.visitchildrenset(self._dir[:-1])
1178 visit = match.visitchildrenset(self._dir[:-1])
1179 if visit == b'all':
1179 if visit == b'all':
1180 return self.copy()
1180 return self.copy()
1181 ret = treemanifest(self._dir)
1181 ret = treemanifest(self._dir)
1182 if not visit:
1182 if not visit:
1183 return ret
1183 return ret
1184
1184
1185 self._load()
1185 self._load()
1186 for fn in self._files:
1186 for fn in self._files:
1187 # While visitchildrenset *usually* lists only subdirs, this is
1187 # While visitchildrenset *usually* lists only subdirs, this is
1188 # actually up to the matcher and may have some files in the set().
1188 # actually up to the matcher and may have some files in the set().
1189 # If visit == 'this', we should obviously look at the files in this
1189 # If visit == 'this', we should obviously look at the files in this
1190 # directory; if visit is a set, and fn is in it, we should inspect
1190 # directory; if visit is a set, and fn is in it, we should inspect
1191 # fn (but no need to inspect things not in the set).
1191 # fn (but no need to inspect things not in the set).
1192 if visit != b'this' and fn not in visit:
1192 if visit != b'this' and fn not in visit:
1193 continue
1193 continue
1194 fullp = self._subpath(fn)
1194 fullp = self._subpath(fn)
1195 # visitchildrenset isn't perfect, we still need to call the regular
1195 # visitchildrenset isn't perfect, we still need to call the regular
1196 # matcher code to further filter results.
1196 # matcher code to further filter results.
1197 if not match(fullp):
1197 if not match(fullp):
1198 continue
1198 continue
1199 ret._files[fn] = self._files[fn]
1199 ret._files[fn] = self._files[fn]
1200 if fn in self._flags:
1200 if fn in self._flags:
1201 ret._flags[fn] = self._flags[fn]
1201 ret._flags[fn] = self._flags[fn]
1202
1202
1203 visit = self._loadchildrensetlazy(visit)
1203 visit = self._loadchildrensetlazy(visit)
1204 for dir, subm in pycompat.iteritems(self._dirs):
1204 for dir, subm in pycompat.iteritems(self._dirs):
1205 if visit and dir[:-1] not in visit:
1205 if visit and dir[:-1] not in visit:
1206 continue
1206 continue
1207 m = subm._matches_inner(match)
1207 m = subm._matches_inner(match)
1208 if not m._isempty():
1208 if not m._isempty():
1209 ret._dirs[dir] = m
1209 ret._dirs[dir] = m
1210
1210
1211 if not ret._isempty():
1211 if not ret._isempty():
1212 ret._dirty = True
1212 ret._dirty = True
1213 return ret
1213 return ret
1214
1214
1215 def fastdelta(self, base, changes):
1216 raise FastdeltaUnavailable()
1217
1215 def diff(self, m2, match=None, clean=False):
1218 def diff(self, m2, match=None, clean=False):
1216 '''Finds changes between the current manifest and m2.
1219 '''Finds changes between the current manifest and m2.
1217
1220
1218 Args:
1221 Args:
1219 m2: the manifest to which this manifest should be compared.
1222 m2: the manifest to which this manifest should be compared.
1220 clean: if true, include files unchanged between these manifests
1223 clean: if true, include files unchanged between these manifests
1221 with a None value in the returned dictionary.
1224 with a None value in the returned dictionary.
1222
1225
1223 The result is returned as a dict with filename as key and
1226 The result is returned as a dict with filename as key and
1224 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1227 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1225 nodeid in the current/other manifest and fl1/fl2 is the flag
1228 nodeid in the current/other manifest and fl1/fl2 is the flag
1226 in the current/other manifest. Where the file does not exist,
1229 in the current/other manifest. Where the file does not exist,
1227 the nodeid will be None and the flags will be the empty
1230 the nodeid will be None and the flags will be the empty
1228 string.
1231 string.
1229 '''
1232 '''
1230 if match and not match.always():
1233 if match and not match.always():
1231 m1 = self._matches(match)
1234 m1 = self._matches(match)
1232 m2 = m2._matches(match)
1235 m2 = m2._matches(match)
1233 return m1.diff(m2, clean=clean)
1236 return m1.diff(m2, clean=clean)
1234 result = {}
1237 result = {}
1235 emptytree = treemanifest()
1238 emptytree = treemanifest()
1236
1239
1237 def _iterativediff(t1, t2, stack):
1240 def _iterativediff(t1, t2, stack):
1238 """compares two tree manifests and append new tree-manifests which
1241 """compares two tree manifests and append new tree-manifests which
1239 needs to be compared to stack"""
1242 needs to be compared to stack"""
1240 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1243 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1241 return
1244 return
1242 t1._load()
1245 t1._load()
1243 t2._load()
1246 t2._load()
1244 self._loaddifflazy(t1, t2)
1247 self._loaddifflazy(t1, t2)
1245
1248
1246 for d, m1 in pycompat.iteritems(t1._dirs):
1249 for d, m1 in pycompat.iteritems(t1._dirs):
1247 m2 = t2._dirs.get(d, emptytree)
1250 m2 = t2._dirs.get(d, emptytree)
1248 stack.append((m1, m2))
1251 stack.append((m1, m2))
1249
1252
1250 for d, m2 in pycompat.iteritems(t2._dirs):
1253 for d, m2 in pycompat.iteritems(t2._dirs):
1251 if d not in t1._dirs:
1254 if d not in t1._dirs:
1252 stack.append((emptytree, m2))
1255 stack.append((emptytree, m2))
1253
1256
1254 for fn, n1 in pycompat.iteritems(t1._files):
1257 for fn, n1 in pycompat.iteritems(t1._files):
1255 fl1 = t1._flags.get(fn, b'')
1258 fl1 = t1._flags.get(fn, b'')
1256 n2 = t2._files.get(fn, None)
1259 n2 = t2._files.get(fn, None)
1257 fl2 = t2._flags.get(fn, b'')
1260 fl2 = t2._flags.get(fn, b'')
1258 if n1 != n2 or fl1 != fl2:
1261 if n1 != n2 or fl1 != fl2:
1259 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1262 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1260 elif clean:
1263 elif clean:
1261 result[t1._subpath(fn)] = None
1264 result[t1._subpath(fn)] = None
1262
1265
1263 for fn, n2 in pycompat.iteritems(t2._files):
1266 for fn, n2 in pycompat.iteritems(t2._files):
1264 if fn not in t1._files:
1267 if fn not in t1._files:
1265 fl2 = t2._flags.get(fn, b'')
1268 fl2 = t2._flags.get(fn, b'')
1266 result[t2._subpath(fn)] = ((None, b''), (n2, fl2))
1269 result[t2._subpath(fn)] = ((None, b''), (n2, fl2))
1267
1270
1268 stackls = []
1271 stackls = []
1269 _iterativediff(self, m2, stackls)
1272 _iterativediff(self, m2, stackls)
1270 while stackls:
1273 while stackls:
1271 t1, t2 = stackls.pop()
1274 t1, t2 = stackls.pop()
1272 # stackls is populated in the function call
1275 # stackls is populated in the function call
1273 _iterativediff(t1, t2, stackls)
1276 _iterativediff(t1, t2, stackls)
1274 return result
1277 return result
1275
1278
1276 def unmodifiedsince(self, m2):
1279 def unmodifiedsince(self, m2):
1277 return not self._dirty and not m2._dirty and self._node == m2._node
1280 return not self._dirty and not m2._dirty and self._node == m2._node
1278
1281
1279 def parse(self, text, readsubtree):
1282 def parse(self, text, readsubtree):
1280 selflazy = self._lazydirs
1283 selflazy = self._lazydirs
1281 subpath = self._subpath
1284 subpath = self._subpath
1282 for f, n, fl in _parse(text):
1285 for f, n, fl in _parse(text):
1283 if fl == b't':
1286 if fl == b't':
1284 f = f + b'/'
1287 f = f + b'/'
1285 # False below means "doesn't need to be copied" and can use the
1288 # False below means "doesn't need to be copied" and can use the
1286 # cached value from readsubtree directly.
1289 # cached value from readsubtree directly.
1287 selflazy[f] = (subpath(f), n, readsubtree, False)
1290 selflazy[f] = (subpath(f), n, readsubtree, False)
1288 elif b'/' in f:
1291 elif b'/' in f:
1289 # This is a flat manifest, so use __setitem__ and setflag rather
1292 # This is a flat manifest, so use __setitem__ and setflag rather
1290 # than assigning directly to _files and _flags, so we can
1293 # than assigning directly to _files and _flags, so we can
1291 # assign a path in a subdirectory, and to mark dirty (compared
1294 # assign a path in a subdirectory, and to mark dirty (compared
1292 # to nullid).
1295 # to nullid).
1293 self[f] = n
1296 self[f] = n
1294 if fl:
1297 if fl:
1295 self.setflag(f, fl)
1298 self.setflag(f, fl)
1296 else:
1299 else:
1297 # Assigning to _files and _flags avoids marking as dirty,
1300 # Assigning to _files and _flags avoids marking as dirty,
1298 # and should be a little faster.
1301 # and should be a little faster.
1299 self._files[f] = n
1302 self._files[f] = n
1300 if fl:
1303 if fl:
1301 self._flags[f] = fl
1304 self._flags[f] = fl
1302
1305
1303 def text(self):
1306 def text(self):
1304 """Get the full data of this manifest as a bytestring."""
1307 """Get the full data of this manifest as a bytestring."""
1305 self._load()
1308 self._load()
1306 return _text(self.iterentries())
1309 return _text(self.iterentries())
1307
1310
1308 def dirtext(self):
1311 def dirtext(self):
1309 """Get the full data of this directory as a bytestring. Make sure that
1312 """Get the full data of this directory as a bytestring. Make sure that
1310 any submanifests have been written first, so their nodeids are correct.
1313 any submanifests have been written first, so their nodeids are correct.
1311 """
1314 """
1312 self._load()
1315 self._load()
1313 flags = self.flags
1316 flags = self.flags
1314 lazydirs = [
1317 lazydirs = [
1315 (d[:-1], v[1], b't') for d, v in pycompat.iteritems(self._lazydirs)
1318 (d[:-1], v[1], b't') for d, v in pycompat.iteritems(self._lazydirs)
1316 ]
1319 ]
1317 dirs = [(d[:-1], self._dirs[d]._node, b't') for d in self._dirs]
1320 dirs = [(d[:-1], self._dirs[d]._node, b't') for d in self._dirs]
1318 files = [(f, self._files[f], flags(f)) for f in self._files]
1321 files = [(f, self._files[f], flags(f)) for f in self._files]
1319 return _text(sorted(dirs + files + lazydirs))
1322 return _text(sorted(dirs + files + lazydirs))
1320
1323
1321 def read(self, gettext, readsubtree):
1324 def read(self, gettext, readsubtree):
1322 def _load_for_read(s):
1325 def _load_for_read(s):
1323 s.parse(gettext(), readsubtree)
1326 s.parse(gettext(), readsubtree)
1324 s._dirty = False
1327 s._dirty = False
1325
1328
1326 self._loadfunc = _load_for_read
1329 self._loadfunc = _load_for_read
1327
1330
1328 def writesubtrees(self, m1, m2, writesubtree, match):
1331 def writesubtrees(self, m1, m2, writesubtree, match):
1329 self._load() # for consistency; should never have any effect here
1332 self._load() # for consistency; should never have any effect here
1330 m1._load()
1333 m1._load()
1331 m2._load()
1334 m2._load()
1332 emptytree = treemanifest()
1335 emptytree = treemanifest()
1333
1336
1334 def getnode(m, d):
1337 def getnode(m, d):
1335 ld = m._lazydirs.get(d)
1338 ld = m._lazydirs.get(d)
1336 if ld:
1339 if ld:
1337 return ld[1]
1340 return ld[1]
1338 return m._dirs.get(d, emptytree)._node
1341 return m._dirs.get(d, emptytree)._node
1339
1342
1340 # let's skip investigating things that `match` says we do not need.
1343 # let's skip investigating things that `match` says we do not need.
1341 visit = match.visitchildrenset(self._dir[:-1])
1344 visit = match.visitchildrenset(self._dir[:-1])
1342 visit = self._loadchildrensetlazy(visit)
1345 visit = self._loadchildrensetlazy(visit)
1343 if visit == b'this' or visit == b'all':
1346 if visit == b'this' or visit == b'all':
1344 visit = None
1347 visit = None
1345 for d, subm in pycompat.iteritems(self._dirs):
1348 for d, subm in pycompat.iteritems(self._dirs):
1346 if visit and d[:-1] not in visit:
1349 if visit and d[:-1] not in visit:
1347 continue
1350 continue
1348 subp1 = getnode(m1, d)
1351 subp1 = getnode(m1, d)
1349 subp2 = getnode(m2, d)
1352 subp2 = getnode(m2, d)
1350 if subp1 == nullid:
1353 if subp1 == nullid:
1351 subp1, subp2 = subp2, subp1
1354 subp1, subp2 = subp2, subp1
1352 writesubtree(subm, subp1, subp2, match)
1355 writesubtree(subm, subp1, subp2, match)
1353
1356
1354 def walksubtrees(self, matcher=None):
1357 def walksubtrees(self, matcher=None):
1355 """Returns an iterator of the subtrees of this manifest, including this
1358 """Returns an iterator of the subtrees of this manifest, including this
1356 manifest itself.
1359 manifest itself.
1357
1360
1358 If `matcher` is provided, it only returns subtrees that match.
1361 If `matcher` is provided, it only returns subtrees that match.
1359 """
1362 """
1360 if matcher and not matcher.visitdir(self._dir[:-1]):
1363 if matcher and not matcher.visitdir(self._dir[:-1]):
1361 return
1364 return
1362 if not matcher or matcher(self._dir[:-1]):
1365 if not matcher or matcher(self._dir[:-1]):
1363 yield self
1366 yield self
1364
1367
1365 self._load()
1368 self._load()
1366 # OPT: use visitchildrenset to avoid loading everything.
1369 # OPT: use visitchildrenset to avoid loading everything.
1367 self._loadalllazy()
1370 self._loadalllazy()
1368 for d, subm in pycompat.iteritems(self._dirs):
1371 for d, subm in pycompat.iteritems(self._dirs):
1369 for subtree in subm.walksubtrees(matcher=matcher):
1372 for subtree in subm.walksubtrees(matcher=matcher):
1370 yield subtree
1373 yield subtree
1371
1374
1372
1375
1373 class manifestfulltextcache(util.lrucachedict):
1376 class manifestfulltextcache(util.lrucachedict):
1374 """File-backed LRU cache for the manifest cache
1377 """File-backed LRU cache for the manifest cache
1375
1378
1376 File consists of entries, up to EOF:
1379 File consists of entries, up to EOF:
1377
1380
1378 - 20 bytes node, 4 bytes length, <length> manifest data
1381 - 20 bytes node, 4 bytes length, <length> manifest data
1379
1382
1380 These are written in reverse cache order (oldest to newest).
1383 These are written in reverse cache order (oldest to newest).
1381
1384
1382 """
1385 """
1383
1386
1384 _file = b'manifestfulltextcache'
1387 _file = b'manifestfulltextcache'
1385
1388
1386 def __init__(self, max):
1389 def __init__(self, max):
1387 super(manifestfulltextcache, self).__init__(max)
1390 super(manifestfulltextcache, self).__init__(max)
1388 self._dirty = False
1391 self._dirty = False
1389 self._read = False
1392 self._read = False
1390 self._opener = None
1393 self._opener = None
1391
1394
1392 def read(self):
1395 def read(self):
1393 if self._read or self._opener is None:
1396 if self._read or self._opener is None:
1394 return
1397 return
1395
1398
1396 try:
1399 try:
1397 with self._opener(self._file) as fp:
1400 with self._opener(self._file) as fp:
1398 set = super(manifestfulltextcache, self).__setitem__
1401 set = super(manifestfulltextcache, self).__setitem__
1399 # ignore trailing data, this is a cache, corruption is skipped
1402 # ignore trailing data, this is a cache, corruption is skipped
1400 while True:
1403 while True:
1401 node = fp.read(20)
1404 node = fp.read(20)
1402 if len(node) < 20:
1405 if len(node) < 20:
1403 break
1406 break
1404 try:
1407 try:
1405 size = struct.unpack(b'>L', fp.read(4))[0]
1408 size = struct.unpack(b'>L', fp.read(4))[0]
1406 except struct.error:
1409 except struct.error:
1407 break
1410 break
1408 value = bytearray(fp.read(size))
1411 value = bytearray(fp.read(size))
1409 if len(value) != size:
1412 if len(value) != size:
1410 break
1413 break
1411 set(node, value)
1414 set(node, value)
1412 except IOError:
1415 except IOError:
1413 # the file is allowed to be missing
1416 # the file is allowed to be missing
1414 pass
1417 pass
1415
1418
1416 self._read = True
1419 self._read = True
1417 self._dirty = False
1420 self._dirty = False
1418
1421
1419 def write(self):
1422 def write(self):
1420 if not self._dirty or self._opener is None:
1423 if not self._dirty or self._opener is None:
1421 return
1424 return
1422 # rotate backwards to the first used node
1425 # rotate backwards to the first used node
1423 with self._opener(
1426 with self._opener(
1424 self._file, b'w', atomictemp=True, checkambig=True
1427 self._file, b'w', atomictemp=True, checkambig=True
1425 ) as fp:
1428 ) as fp:
1426 node = self._head.prev
1429 node = self._head.prev
1427 while True:
1430 while True:
1428 if node.key in self._cache:
1431 if node.key in self._cache:
1429 fp.write(node.key)
1432 fp.write(node.key)
1430 fp.write(struct.pack(b'>L', len(node.value)))
1433 fp.write(struct.pack(b'>L', len(node.value)))
1431 fp.write(node.value)
1434 fp.write(node.value)
1432 if node is self._head:
1435 if node is self._head:
1433 break
1436 break
1434 node = node.prev
1437 node = node.prev
1435
1438
1436 def __len__(self):
1439 def __len__(self):
1437 if not self._read:
1440 if not self._read:
1438 self.read()
1441 self.read()
1439 return super(manifestfulltextcache, self).__len__()
1442 return super(manifestfulltextcache, self).__len__()
1440
1443
1441 def __contains__(self, k):
1444 def __contains__(self, k):
1442 if not self._read:
1445 if not self._read:
1443 self.read()
1446 self.read()
1444 return super(manifestfulltextcache, self).__contains__(k)
1447 return super(manifestfulltextcache, self).__contains__(k)
1445
1448
1446 def __iter__(self):
1449 def __iter__(self):
1447 if not self._read:
1450 if not self._read:
1448 self.read()
1451 self.read()
1449 return super(manifestfulltextcache, self).__iter__()
1452 return super(manifestfulltextcache, self).__iter__()
1450
1453
1451 def __getitem__(self, k):
1454 def __getitem__(self, k):
1452 if not self._read:
1455 if not self._read:
1453 self.read()
1456 self.read()
1454 # the cache lru order can change on read
1457 # the cache lru order can change on read
1455 setdirty = self._cache.get(k) is not self._head
1458 setdirty = self._cache.get(k) is not self._head
1456 value = super(manifestfulltextcache, self).__getitem__(k)
1459 value = super(manifestfulltextcache, self).__getitem__(k)
1457 if setdirty:
1460 if setdirty:
1458 self._dirty = True
1461 self._dirty = True
1459 return value
1462 return value
1460
1463
1461 def __setitem__(self, k, v):
1464 def __setitem__(self, k, v):
1462 if not self._read:
1465 if not self._read:
1463 self.read()
1466 self.read()
1464 super(manifestfulltextcache, self).__setitem__(k, v)
1467 super(manifestfulltextcache, self).__setitem__(k, v)
1465 self._dirty = True
1468 self._dirty = True
1466
1469
1467 def __delitem__(self, k):
1470 def __delitem__(self, k):
1468 if not self._read:
1471 if not self._read:
1469 self.read()
1472 self.read()
1470 super(manifestfulltextcache, self).__delitem__(k)
1473 super(manifestfulltextcache, self).__delitem__(k)
1471 self._dirty = True
1474 self._dirty = True
1472
1475
1473 def get(self, k, default=None):
1476 def get(self, k, default=None):
1474 if not self._read:
1477 if not self._read:
1475 self.read()
1478 self.read()
1476 return super(manifestfulltextcache, self).get(k, default=default)
1479 return super(manifestfulltextcache, self).get(k, default=default)
1477
1480
1478 def clear(self, clear_persisted_data=False):
1481 def clear(self, clear_persisted_data=False):
1479 super(manifestfulltextcache, self).clear()
1482 super(manifestfulltextcache, self).clear()
1480 if clear_persisted_data:
1483 if clear_persisted_data:
1481 self._dirty = True
1484 self._dirty = True
1482 self.write()
1485 self.write()
1483 self._read = False
1486 self._read = False
1484
1487
1485
1488
1486 # and upper bound of what we expect from compression
1489 # and upper bound of what we expect from compression
1487 # (real live value seems to be "3")
1490 # (real live value seems to be "3")
1488 MAXCOMPRESSION = 3
1491 MAXCOMPRESSION = 3
1489
1492
1490
1493
1494 class FastdeltaUnavailable(Exception):
1495 """Exception raised when fastdelta isn't usable on a manifest."""
1496
1497
1491 @interfaceutil.implementer(repository.imanifeststorage)
1498 @interfaceutil.implementer(repository.imanifeststorage)
1492 class manifestrevlog(object):
1499 class manifestrevlog(object):
1493 '''A revlog that stores manifest texts. This is responsible for caching the
1500 '''A revlog that stores manifest texts. This is responsible for caching the
1494 full-text manifest contents.
1501 full-text manifest contents.
1495 '''
1502 '''
1496
1503
1497 def __init__(
1504 def __init__(
1498 self,
1505 self,
1499 opener,
1506 opener,
1500 tree=b'',
1507 tree=b'',
1501 dirlogcache=None,
1508 dirlogcache=None,
1502 indexfile=None,
1509 indexfile=None,
1503 treemanifest=False,
1510 treemanifest=False,
1504 ):
1511 ):
1505 """Constructs a new manifest revlog
1512 """Constructs a new manifest revlog
1506
1513
1507 `indexfile` - used by extensions to have two manifests at once, like
1514 `indexfile` - used by extensions to have two manifests at once, like
1508 when transitioning between flatmanifeset and treemanifests.
1515 when transitioning between flatmanifeset and treemanifests.
1509
1516
1510 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1517 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1511 options can also be used to make this a tree manifest revlog. The opener
1518 options can also be used to make this a tree manifest revlog. The opener
1512 option takes precedence, so if it is set to True, we ignore whatever
1519 option takes precedence, so if it is set to True, we ignore whatever
1513 value is passed in to the constructor.
1520 value is passed in to the constructor.
1514 """
1521 """
1515 # During normal operations, we expect to deal with not more than four
1522 # During normal operations, we expect to deal with not more than four
1516 # revs at a time (such as during commit --amend). When rebasing large
1523 # revs at a time (such as during commit --amend). When rebasing large
1517 # stacks of commits, the number can go up, hence the config knob below.
1524 # stacks of commits, the number can go up, hence the config knob below.
1518 cachesize = 4
1525 cachesize = 4
1519 optiontreemanifest = False
1526 optiontreemanifest = False
1520 opts = getattr(opener, 'options', None)
1527 opts = getattr(opener, 'options', None)
1521 if opts is not None:
1528 if opts is not None:
1522 cachesize = opts.get(b'manifestcachesize', cachesize)
1529 cachesize = opts.get(b'manifestcachesize', cachesize)
1523 optiontreemanifest = opts.get(b'treemanifest', False)
1530 optiontreemanifest = opts.get(b'treemanifest', False)
1524
1531
1525 self._treeondisk = optiontreemanifest or treemanifest
1532 self._treeondisk = optiontreemanifest or treemanifest
1526
1533
1527 self._fulltextcache = manifestfulltextcache(cachesize)
1534 self._fulltextcache = manifestfulltextcache(cachesize)
1528
1535
1529 if tree:
1536 if tree:
1530 assert self._treeondisk, b'opts is %r' % opts
1537 assert self._treeondisk, b'opts is %r' % opts
1531
1538
1532 if indexfile is None:
1539 if indexfile is None:
1533 indexfile = b'00manifest.i'
1540 indexfile = b'00manifest.i'
1534 if tree:
1541 if tree:
1535 indexfile = b"meta/" + tree + indexfile
1542 indexfile = b"meta/" + tree + indexfile
1536
1543
1537 self.tree = tree
1544 self.tree = tree
1538
1545
1539 # The dirlogcache is kept on the root manifest log
1546 # The dirlogcache is kept on the root manifest log
1540 if tree:
1547 if tree:
1541 self._dirlogcache = dirlogcache
1548 self._dirlogcache = dirlogcache
1542 else:
1549 else:
1543 self._dirlogcache = {b'': self}
1550 self._dirlogcache = {b'': self}
1544
1551
1545 self._revlog = revlog.revlog(
1552 self._revlog = revlog.revlog(
1546 opener,
1553 opener,
1547 indexfile,
1554 indexfile,
1548 # only root indexfile is cached
1555 # only root indexfile is cached
1549 checkambig=not bool(tree),
1556 checkambig=not bool(tree),
1550 mmaplargeindex=True,
1557 mmaplargeindex=True,
1551 upperboundcomp=MAXCOMPRESSION,
1558 upperboundcomp=MAXCOMPRESSION,
1552 )
1559 )
1553
1560
1554 self.index = self._revlog.index
1561 self.index = self._revlog.index
1555 self.version = self._revlog.version
1562 self.version = self._revlog.version
1556 self._generaldelta = self._revlog._generaldelta
1563 self._generaldelta = self._revlog._generaldelta
1557
1564
1558 def _setupmanifestcachehooks(self, repo):
1565 def _setupmanifestcachehooks(self, repo):
1559 """Persist the manifestfulltextcache on lock release"""
1566 """Persist the manifestfulltextcache on lock release"""
1560 if not util.safehasattr(repo, b'_wlockref'):
1567 if not util.safehasattr(repo, b'_wlockref'):
1561 return
1568 return
1562
1569
1563 self._fulltextcache._opener = repo.wcachevfs
1570 self._fulltextcache._opener = repo.wcachevfs
1564 if repo._currentlock(repo._wlockref) is None:
1571 if repo._currentlock(repo._wlockref) is None:
1565 return
1572 return
1566
1573
1567 reporef = weakref.ref(repo)
1574 reporef = weakref.ref(repo)
1568 manifestrevlogref = weakref.ref(self)
1575 manifestrevlogref = weakref.ref(self)
1569
1576
1570 def persistmanifestcache(success):
1577 def persistmanifestcache(success):
1571 # Repo is in an unknown state, do not persist.
1578 # Repo is in an unknown state, do not persist.
1572 if not success:
1579 if not success:
1573 return
1580 return
1574
1581
1575 repo = reporef()
1582 repo = reporef()
1576 self = manifestrevlogref()
1583 self = manifestrevlogref()
1577 if repo is None or self is None:
1584 if repo is None or self is None:
1578 return
1585 return
1579 if repo.manifestlog.getstorage(b'') is not self:
1586 if repo.manifestlog.getstorage(b'') is not self:
1580 # there's a different manifest in play now, abort
1587 # there's a different manifest in play now, abort
1581 return
1588 return
1582 self._fulltextcache.write()
1589 self._fulltextcache.write()
1583
1590
1584 repo._afterlock(persistmanifestcache)
1591 repo._afterlock(persistmanifestcache)
1585
1592
1586 @property
1593 @property
1587 def fulltextcache(self):
1594 def fulltextcache(self):
1588 return self._fulltextcache
1595 return self._fulltextcache
1589
1596
1590 def clearcaches(self, clear_persisted_data=False):
1597 def clearcaches(self, clear_persisted_data=False):
1591 self._revlog.clearcaches()
1598 self._revlog.clearcaches()
1592 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1599 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1593 self._dirlogcache = {self.tree: self}
1600 self._dirlogcache = {self.tree: self}
1594
1601
1595 def dirlog(self, d):
1602 def dirlog(self, d):
1596 if d:
1603 if d:
1597 assert self._treeondisk
1604 assert self._treeondisk
1598 if d not in self._dirlogcache:
1605 if d not in self._dirlogcache:
1599 mfrevlog = manifestrevlog(
1606 mfrevlog = manifestrevlog(
1600 self.opener, d, self._dirlogcache, treemanifest=self._treeondisk
1607 self.opener, d, self._dirlogcache, treemanifest=self._treeondisk
1601 )
1608 )
1602 self._dirlogcache[d] = mfrevlog
1609 self._dirlogcache[d] = mfrevlog
1603 return self._dirlogcache[d]
1610 return self._dirlogcache[d]
1604
1611
1605 def add(
1612 def add(
1606 self,
1613 self,
1607 m,
1614 m,
1608 transaction,
1615 transaction,
1609 link,
1616 link,
1610 p1,
1617 p1,
1611 p2,
1618 p2,
1612 added,
1619 added,
1613 removed,
1620 removed,
1614 readtree=None,
1621 readtree=None,
1615 match=None,
1622 match=None,
1616 ):
1623 ):
1617 if p1 in self.fulltextcache and util.safehasattr(m, b'fastdelta'):
1624 try:
1625 if p1 not in self.fulltextcache:
1626 raise FastdeltaUnavailable()
1618 # If our first parent is in the manifest cache, we can
1627 # If our first parent is in the manifest cache, we can
1619 # compute a delta here using properties we know about the
1628 # compute a delta here using properties we know about the
1620 # manifest up-front, which may save time later for the
1629 # manifest up-front, which may save time later for the
1621 # revlog layer.
1630 # revlog layer.
1622
1631
1623 _checkforbidden(added)
1632 _checkforbidden(added)
1624 # combine the changed lists into one sorted iterator
1633 # combine the changed lists into one sorted iterator
1625 work = heapq.merge(
1634 work = heapq.merge(
1626 [(x, False) for x in sorted(added)],
1635 [(x, False) for x in sorted(added)],
1627 [(x, True) for x in sorted(removed)],
1636 [(x, True) for x in sorted(removed)],
1628 )
1637 )
1629
1638
1630 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1639 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1631 cachedelta = self._revlog.rev(p1), deltatext
1640 cachedelta = self._revlog.rev(p1), deltatext
1632 text = util.buffer(arraytext)
1641 text = util.buffer(arraytext)
1633 n = self._revlog.addrevision(
1642 n = self._revlog.addrevision(
1634 text, transaction, link, p1, p2, cachedelta
1643 text, transaction, link, p1, p2, cachedelta
1635 )
1644 )
1636 else:
1645 except FastdeltaUnavailable:
1637 # The first parent manifest isn't already loaded, so we'll
1646 # The first parent manifest isn't already loaded or the
1638 # just encode a fulltext of the manifest and pass that
1647 # manifest implementation doesn't support fastdelta, so
1639 # through to the revlog layer, and let it handle the delta
1648 # we'll just encode a fulltext of the manifest and pass
1640 # process.
1649 # that through to the revlog layer, and let it handle the
1650 # delta process.
1641 if self._treeondisk:
1651 if self._treeondisk:
1642 assert readtree, b"readtree must be set for treemanifest writes"
1652 assert readtree, b"readtree must be set for treemanifest writes"
1643 assert match, b"match must be specified for treemanifest writes"
1653 assert match, b"match must be specified for treemanifest writes"
1644 m1 = readtree(self.tree, p1)
1654 m1 = readtree(self.tree, p1)
1645 m2 = readtree(self.tree, p2)
1655 m2 = readtree(self.tree, p2)
1646 n = self._addtree(
1656 n = self._addtree(
1647 m, transaction, link, m1, m2, readtree, match=match
1657 m, transaction, link, m1, m2, readtree, match=match
1648 )
1658 )
1649 arraytext = None
1659 arraytext = None
1650 else:
1660 else:
1651 text = m.text()
1661 text = m.text()
1652 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1662 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1653 arraytext = bytearray(text)
1663 arraytext = bytearray(text)
1654
1664
1655 if arraytext is not None:
1665 if arraytext is not None:
1656 self.fulltextcache[n] = arraytext
1666 self.fulltextcache[n] = arraytext
1657
1667
1658 return n
1668 return n
1659
1669
1660 def _addtree(self, m, transaction, link, m1, m2, readtree, match):
1670 def _addtree(self, m, transaction, link, m1, m2, readtree, match):
1661 # If the manifest is unchanged compared to one parent,
1671 # If the manifest is unchanged compared to one parent,
1662 # don't write a new revision
1672 # don't write a new revision
1663 if self.tree != b'' and (
1673 if self.tree != b'' and (
1664 m.unmodifiedsince(m1) or m.unmodifiedsince(m2)
1674 m.unmodifiedsince(m1) or m.unmodifiedsince(m2)
1665 ):
1675 ):
1666 return m.node()
1676 return m.node()
1667
1677
1668 def writesubtree(subm, subp1, subp2, match):
1678 def writesubtree(subm, subp1, subp2, match):
1669 sublog = self.dirlog(subm.dir())
1679 sublog = self.dirlog(subm.dir())
1670 sublog.add(
1680 sublog.add(
1671 subm,
1681 subm,
1672 transaction,
1682 transaction,
1673 link,
1683 link,
1674 subp1,
1684 subp1,
1675 subp2,
1685 subp2,
1676 None,
1686 None,
1677 None,
1687 None,
1678 readtree=readtree,
1688 readtree=readtree,
1679 match=match,
1689 match=match,
1680 )
1690 )
1681
1691
1682 m.writesubtrees(m1, m2, writesubtree, match)
1692 m.writesubtrees(m1, m2, writesubtree, match)
1683 text = m.dirtext()
1693 text = m.dirtext()
1684 n = None
1694 n = None
1685 if self.tree != b'':
1695 if self.tree != b'':
1686 # Double-check whether contents are unchanged to one parent
1696 # Double-check whether contents are unchanged to one parent
1687 if text == m1.dirtext():
1697 if text == m1.dirtext():
1688 n = m1.node()
1698 n = m1.node()
1689 elif text == m2.dirtext():
1699 elif text == m2.dirtext():
1690 n = m2.node()
1700 n = m2.node()
1691
1701
1692 if not n:
1702 if not n:
1693 n = self._revlog.addrevision(
1703 n = self._revlog.addrevision(
1694 text, transaction, link, m1.node(), m2.node()
1704 text, transaction, link, m1.node(), m2.node()
1695 )
1705 )
1696
1706
1697 # Save nodeid so parent manifest can calculate its nodeid
1707 # Save nodeid so parent manifest can calculate its nodeid
1698 m.setnode(n)
1708 m.setnode(n)
1699 return n
1709 return n
1700
1710
1701 def __len__(self):
1711 def __len__(self):
1702 return len(self._revlog)
1712 return len(self._revlog)
1703
1713
1704 def __iter__(self):
1714 def __iter__(self):
1705 return self._revlog.__iter__()
1715 return self._revlog.__iter__()
1706
1716
1707 def rev(self, node):
1717 def rev(self, node):
1708 return self._revlog.rev(node)
1718 return self._revlog.rev(node)
1709
1719
1710 def node(self, rev):
1720 def node(self, rev):
1711 return self._revlog.node(rev)
1721 return self._revlog.node(rev)
1712
1722
1713 def lookup(self, value):
1723 def lookup(self, value):
1714 return self._revlog.lookup(value)
1724 return self._revlog.lookup(value)
1715
1725
1716 def parentrevs(self, rev):
1726 def parentrevs(self, rev):
1717 return self._revlog.parentrevs(rev)
1727 return self._revlog.parentrevs(rev)
1718
1728
1719 def parents(self, node):
1729 def parents(self, node):
1720 return self._revlog.parents(node)
1730 return self._revlog.parents(node)
1721
1731
1722 def linkrev(self, rev):
1732 def linkrev(self, rev):
1723 return self._revlog.linkrev(rev)
1733 return self._revlog.linkrev(rev)
1724
1734
1725 def checksize(self):
1735 def checksize(self):
1726 return self._revlog.checksize()
1736 return self._revlog.checksize()
1727
1737
1728 def revision(self, node, _df=None, raw=False):
1738 def revision(self, node, _df=None, raw=False):
1729 return self._revlog.revision(node, _df=_df, raw=raw)
1739 return self._revlog.revision(node, _df=_df, raw=raw)
1730
1740
1731 def rawdata(self, node, _df=None):
1741 def rawdata(self, node, _df=None):
1732 return self._revlog.rawdata(node, _df=_df)
1742 return self._revlog.rawdata(node, _df=_df)
1733
1743
1734 def revdiff(self, rev1, rev2):
1744 def revdiff(self, rev1, rev2):
1735 return self._revlog.revdiff(rev1, rev2)
1745 return self._revlog.revdiff(rev1, rev2)
1736
1746
1737 def cmp(self, node, text):
1747 def cmp(self, node, text):
1738 return self._revlog.cmp(node, text)
1748 return self._revlog.cmp(node, text)
1739
1749
1740 def deltaparent(self, rev):
1750 def deltaparent(self, rev):
1741 return self._revlog.deltaparent(rev)
1751 return self._revlog.deltaparent(rev)
1742
1752
1743 def emitrevisions(
1753 def emitrevisions(
1744 self,
1754 self,
1745 nodes,
1755 nodes,
1746 nodesorder=None,
1756 nodesorder=None,
1747 revisiondata=False,
1757 revisiondata=False,
1748 assumehaveparentrevisions=False,
1758 assumehaveparentrevisions=False,
1749 deltamode=repository.CG_DELTAMODE_STD,
1759 deltamode=repository.CG_DELTAMODE_STD,
1750 ):
1760 ):
1751 return self._revlog.emitrevisions(
1761 return self._revlog.emitrevisions(
1752 nodes,
1762 nodes,
1753 nodesorder=nodesorder,
1763 nodesorder=nodesorder,
1754 revisiondata=revisiondata,
1764 revisiondata=revisiondata,
1755 assumehaveparentrevisions=assumehaveparentrevisions,
1765 assumehaveparentrevisions=assumehaveparentrevisions,
1756 deltamode=deltamode,
1766 deltamode=deltamode,
1757 )
1767 )
1758
1768
1759 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1769 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1760 return self._revlog.addgroup(
1770 return self._revlog.addgroup(
1761 deltas, linkmapper, transaction, addrevisioncb=addrevisioncb
1771 deltas, linkmapper, transaction, addrevisioncb=addrevisioncb
1762 )
1772 )
1763
1773
1764 def rawsize(self, rev):
1774 def rawsize(self, rev):
1765 return self._revlog.rawsize(rev)
1775 return self._revlog.rawsize(rev)
1766
1776
1767 def getstrippoint(self, minlink):
1777 def getstrippoint(self, minlink):
1768 return self._revlog.getstrippoint(minlink)
1778 return self._revlog.getstrippoint(minlink)
1769
1779
1770 def strip(self, minlink, transaction):
1780 def strip(self, minlink, transaction):
1771 return self._revlog.strip(minlink, transaction)
1781 return self._revlog.strip(minlink, transaction)
1772
1782
1773 def files(self):
1783 def files(self):
1774 return self._revlog.files()
1784 return self._revlog.files()
1775
1785
1776 def clone(self, tr, destrevlog, **kwargs):
1786 def clone(self, tr, destrevlog, **kwargs):
1777 if not isinstance(destrevlog, manifestrevlog):
1787 if not isinstance(destrevlog, manifestrevlog):
1778 raise error.ProgrammingError(b'expected manifestrevlog to clone()')
1788 raise error.ProgrammingError(b'expected manifestrevlog to clone()')
1779
1789
1780 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1790 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1781
1791
1782 def storageinfo(
1792 def storageinfo(
1783 self,
1793 self,
1784 exclusivefiles=False,
1794 exclusivefiles=False,
1785 sharedfiles=False,
1795 sharedfiles=False,
1786 revisionscount=False,
1796 revisionscount=False,
1787 trackedsize=False,
1797 trackedsize=False,
1788 storedsize=False,
1798 storedsize=False,
1789 ):
1799 ):
1790 return self._revlog.storageinfo(
1800 return self._revlog.storageinfo(
1791 exclusivefiles=exclusivefiles,
1801 exclusivefiles=exclusivefiles,
1792 sharedfiles=sharedfiles,
1802 sharedfiles=sharedfiles,
1793 revisionscount=revisionscount,
1803 revisionscount=revisionscount,
1794 trackedsize=trackedsize,
1804 trackedsize=trackedsize,
1795 storedsize=storedsize,
1805 storedsize=storedsize,
1796 )
1806 )
1797
1807
1798 @property
1808 @property
1799 def indexfile(self):
1809 def indexfile(self):
1800 return self._revlog.indexfile
1810 return self._revlog.indexfile
1801
1811
1802 @indexfile.setter
1812 @indexfile.setter
1803 def indexfile(self, value):
1813 def indexfile(self, value):
1804 self._revlog.indexfile = value
1814 self._revlog.indexfile = value
1805
1815
1806 @property
1816 @property
1807 def opener(self):
1817 def opener(self):
1808 return self._revlog.opener
1818 return self._revlog.opener
1809
1819
1810 @opener.setter
1820 @opener.setter
1811 def opener(self, value):
1821 def opener(self, value):
1812 self._revlog.opener = value
1822 self._revlog.opener = value
1813
1823
1814
1824
1815 @interfaceutil.implementer(repository.imanifestlog)
1825 @interfaceutil.implementer(repository.imanifestlog)
1816 class manifestlog(object):
1826 class manifestlog(object):
1817 """A collection class representing the collection of manifest snapshots
1827 """A collection class representing the collection of manifest snapshots
1818 referenced by commits in the repository.
1828 referenced by commits in the repository.
1819
1829
1820 In this situation, 'manifest' refers to the abstract concept of a snapshot
1830 In this situation, 'manifest' refers to the abstract concept of a snapshot
1821 of the list of files in the given commit. Consumers of the output of this
1831 of the list of files in the given commit. Consumers of the output of this
1822 class do not care about the implementation details of the actual manifests
1832 class do not care about the implementation details of the actual manifests
1823 they receive (i.e. tree or flat or lazily loaded, etc)."""
1833 they receive (i.e. tree or flat or lazily loaded, etc)."""
1824
1834
1825 def __init__(self, opener, repo, rootstore, narrowmatch):
1835 def __init__(self, opener, repo, rootstore, narrowmatch):
1826 usetreemanifest = False
1836 usetreemanifest = False
1827 cachesize = 4
1837 cachesize = 4
1828
1838
1829 opts = getattr(opener, 'options', None)
1839 opts = getattr(opener, 'options', None)
1830 if opts is not None:
1840 if opts is not None:
1831 usetreemanifest = opts.get(b'treemanifest', usetreemanifest)
1841 usetreemanifest = opts.get(b'treemanifest', usetreemanifest)
1832 cachesize = opts.get(b'manifestcachesize', cachesize)
1842 cachesize = opts.get(b'manifestcachesize', cachesize)
1833
1843
1834 self._treemanifests = usetreemanifest
1844 self._treemanifests = usetreemanifest
1835
1845
1836 self._rootstore = rootstore
1846 self._rootstore = rootstore
1837 self._rootstore._setupmanifestcachehooks(repo)
1847 self._rootstore._setupmanifestcachehooks(repo)
1838 self._narrowmatch = narrowmatch
1848 self._narrowmatch = narrowmatch
1839
1849
1840 # A cache of the manifestctx or treemanifestctx for each directory
1850 # A cache of the manifestctx or treemanifestctx for each directory
1841 self._dirmancache = {}
1851 self._dirmancache = {}
1842 self._dirmancache[b''] = util.lrucachedict(cachesize)
1852 self._dirmancache[b''] = util.lrucachedict(cachesize)
1843
1853
1844 self._cachesize = cachesize
1854 self._cachesize = cachesize
1845
1855
1846 def __getitem__(self, node):
1856 def __getitem__(self, node):
1847 """Retrieves the manifest instance for the given node. Throws a
1857 """Retrieves the manifest instance for the given node. Throws a
1848 LookupError if not found.
1858 LookupError if not found.
1849 """
1859 """
1850 return self.get(b'', node)
1860 return self.get(b'', node)
1851
1861
1852 def get(self, tree, node, verify=True):
1862 def get(self, tree, node, verify=True):
1853 """Retrieves the manifest instance for the given node. Throws a
1863 """Retrieves the manifest instance for the given node. Throws a
1854 LookupError if not found.
1864 LookupError if not found.
1855
1865
1856 `verify` - if True an exception will be thrown if the node is not in
1866 `verify` - if True an exception will be thrown if the node is not in
1857 the revlog
1867 the revlog
1858 """
1868 """
1859 if node in self._dirmancache.get(tree, ()):
1869 if node in self._dirmancache.get(tree, ()):
1860 return self._dirmancache[tree][node]
1870 return self._dirmancache[tree][node]
1861
1871
1862 if not self._narrowmatch.always():
1872 if not self._narrowmatch.always():
1863 if not self._narrowmatch.visitdir(tree[:-1]):
1873 if not self._narrowmatch.visitdir(tree[:-1]):
1864 return excludeddirmanifestctx(tree, node)
1874 return excludeddirmanifestctx(tree, node)
1865 if tree:
1875 if tree:
1866 if self._rootstore._treeondisk:
1876 if self._rootstore._treeondisk:
1867 if verify:
1877 if verify:
1868 # Side-effect is LookupError is raised if node doesn't
1878 # Side-effect is LookupError is raised if node doesn't
1869 # exist.
1879 # exist.
1870 self.getstorage(tree).rev(node)
1880 self.getstorage(tree).rev(node)
1871
1881
1872 m = treemanifestctx(self, tree, node)
1882 m = treemanifestctx(self, tree, node)
1873 else:
1883 else:
1874 raise error.Abort(
1884 raise error.Abort(
1875 _(
1885 _(
1876 b"cannot ask for manifest directory '%s' in a flat "
1886 b"cannot ask for manifest directory '%s' in a flat "
1877 b"manifest"
1887 b"manifest"
1878 )
1888 )
1879 % tree
1889 % tree
1880 )
1890 )
1881 else:
1891 else:
1882 if verify:
1892 if verify:
1883 # Side-effect is LookupError is raised if node doesn't exist.
1893 # Side-effect is LookupError is raised if node doesn't exist.
1884 self._rootstore.rev(node)
1894 self._rootstore.rev(node)
1885
1895
1886 if self._treemanifests:
1896 if self._treemanifests:
1887 m = treemanifestctx(self, b'', node)
1897 m = treemanifestctx(self, b'', node)
1888 else:
1898 else:
1889 m = manifestctx(self, node)
1899 m = manifestctx(self, node)
1890
1900
1891 if node != nullid:
1901 if node != nullid:
1892 mancache = self._dirmancache.get(tree)
1902 mancache = self._dirmancache.get(tree)
1893 if not mancache:
1903 if not mancache:
1894 mancache = util.lrucachedict(self._cachesize)
1904 mancache = util.lrucachedict(self._cachesize)
1895 self._dirmancache[tree] = mancache
1905 self._dirmancache[tree] = mancache
1896 mancache[node] = m
1906 mancache[node] = m
1897 return m
1907 return m
1898
1908
1899 def getstorage(self, tree):
1909 def getstorage(self, tree):
1900 return self._rootstore.dirlog(tree)
1910 return self._rootstore.dirlog(tree)
1901
1911
1902 def clearcaches(self, clear_persisted_data=False):
1912 def clearcaches(self, clear_persisted_data=False):
1903 self._dirmancache.clear()
1913 self._dirmancache.clear()
1904 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1914 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1905
1915
1906 def rev(self, node):
1916 def rev(self, node):
1907 return self._rootstore.rev(node)
1917 return self._rootstore.rev(node)
1908
1918
1909
1919
1910 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1920 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1911 class memmanifestctx(object):
1921 class memmanifestctx(object):
1912 def __init__(self, manifestlog):
1922 def __init__(self, manifestlog):
1913 self._manifestlog = manifestlog
1923 self._manifestlog = manifestlog
1914 self._manifestdict = manifestdict()
1924 self._manifestdict = manifestdict()
1915
1925
1916 def _storage(self):
1926 def _storage(self):
1917 return self._manifestlog.getstorage(b'')
1927 return self._manifestlog.getstorage(b'')
1918
1928
1919 def copy(self):
1929 def copy(self):
1920 memmf = memmanifestctx(self._manifestlog)
1930 memmf = memmanifestctx(self._manifestlog)
1921 memmf._manifestdict = self.read().copy()
1931 memmf._manifestdict = self.read().copy()
1922 return memmf
1932 return memmf
1923
1933
1924 def read(self):
1934 def read(self):
1925 return self._manifestdict
1935 return self._manifestdict
1926
1936
1927 def write(self, transaction, link, p1, p2, added, removed, match=None):
1937 def write(self, transaction, link, p1, p2, added, removed, match=None):
1928 return self._storage().add(
1938 return self._storage().add(
1929 self._manifestdict,
1939 self._manifestdict,
1930 transaction,
1940 transaction,
1931 link,
1941 link,
1932 p1,
1942 p1,
1933 p2,
1943 p2,
1934 added,
1944 added,
1935 removed,
1945 removed,
1936 match=match,
1946 match=match,
1937 )
1947 )
1938
1948
1939
1949
1940 @interfaceutil.implementer(repository.imanifestrevisionstored)
1950 @interfaceutil.implementer(repository.imanifestrevisionstored)
1941 class manifestctx(object):
1951 class manifestctx(object):
1942 """A class representing a single revision of a manifest, including its
1952 """A class representing a single revision of a manifest, including its
1943 contents, its parent revs, and its linkrev.
1953 contents, its parent revs, and its linkrev.
1944 """
1954 """
1945
1955
1946 def __init__(self, manifestlog, node):
1956 def __init__(self, manifestlog, node):
1947 self._manifestlog = manifestlog
1957 self._manifestlog = manifestlog
1948 self._data = None
1958 self._data = None
1949
1959
1950 self._node = node
1960 self._node = node
1951
1961
1952 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1962 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1953 # but let's add it later when something needs it and we can load it
1963 # but let's add it later when something needs it and we can load it
1954 # lazily.
1964 # lazily.
1955 # self.p1, self.p2 = store.parents(node)
1965 # self.p1, self.p2 = store.parents(node)
1956 # rev = store.rev(node)
1966 # rev = store.rev(node)
1957 # self.linkrev = store.linkrev(rev)
1967 # self.linkrev = store.linkrev(rev)
1958
1968
1959 def _storage(self):
1969 def _storage(self):
1960 return self._manifestlog.getstorage(b'')
1970 return self._manifestlog.getstorage(b'')
1961
1971
1962 def node(self):
1972 def node(self):
1963 return self._node
1973 return self._node
1964
1974
1965 def copy(self):
1975 def copy(self):
1966 memmf = memmanifestctx(self._manifestlog)
1976 memmf = memmanifestctx(self._manifestlog)
1967 memmf._manifestdict = self.read().copy()
1977 memmf._manifestdict = self.read().copy()
1968 return memmf
1978 return memmf
1969
1979
1970 @propertycache
1980 @propertycache
1971 def parents(self):
1981 def parents(self):
1972 return self._storage().parents(self._node)
1982 return self._storage().parents(self._node)
1973
1983
1974 def read(self):
1984 def read(self):
1975 if self._data is None:
1985 if self._data is None:
1976 if self._node == nullid:
1986 if self._node == nullid:
1977 self._data = manifestdict()
1987 self._data = manifestdict()
1978 else:
1988 else:
1979 store = self._storage()
1989 store = self._storage()
1980 if self._node in store.fulltextcache:
1990 if self._node in store.fulltextcache:
1981 text = pycompat.bytestr(store.fulltextcache[self._node])
1991 text = pycompat.bytestr(store.fulltextcache[self._node])
1982 else:
1992 else:
1983 text = store.revision(self._node)
1993 text = store.revision(self._node)
1984 arraytext = bytearray(text)
1994 arraytext = bytearray(text)
1985 store.fulltextcache[self._node] = arraytext
1995 store.fulltextcache[self._node] = arraytext
1986 self._data = manifestdict(text)
1996 self._data = manifestdict(text)
1987 return self._data
1997 return self._data
1988
1998
1989 def readfast(self, shallow=False):
1999 def readfast(self, shallow=False):
1990 '''Calls either readdelta or read, based on which would be less work.
2000 '''Calls either readdelta or read, based on which would be less work.
1991 readdelta is called if the delta is against the p1, and therefore can be
2001 readdelta is called if the delta is against the p1, and therefore can be
1992 read quickly.
2002 read quickly.
1993
2003
1994 If `shallow` is True, nothing changes since this is a flat manifest.
2004 If `shallow` is True, nothing changes since this is a flat manifest.
1995 '''
2005 '''
1996 store = self._storage()
2006 store = self._storage()
1997 r = store.rev(self._node)
2007 r = store.rev(self._node)
1998 deltaparent = store.deltaparent(r)
2008 deltaparent = store.deltaparent(r)
1999 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
2009 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
2000 return self.readdelta()
2010 return self.readdelta()
2001 return self.read()
2011 return self.read()
2002
2012
2003 def readdelta(self, shallow=False):
2013 def readdelta(self, shallow=False):
2004 '''Returns a manifest containing just the entries that are present
2014 '''Returns a manifest containing just the entries that are present
2005 in this manifest, but not in its p1 manifest. This is efficient to read
2015 in this manifest, but not in its p1 manifest. This is efficient to read
2006 if the revlog delta is already p1.
2016 if the revlog delta is already p1.
2007
2017
2008 Changing the value of `shallow` has no effect on flat manifests.
2018 Changing the value of `shallow` has no effect on flat manifests.
2009 '''
2019 '''
2010 store = self._storage()
2020 store = self._storage()
2011 r = store.rev(self._node)
2021 r = store.rev(self._node)
2012 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
2022 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
2013 return manifestdict(d)
2023 return manifestdict(d)
2014
2024
2015 def find(self, key):
2025 def find(self, key):
2016 return self.read().find(key)
2026 return self.read().find(key)
2017
2027
2018
2028
2019 @interfaceutil.implementer(repository.imanifestrevisionwritable)
2029 @interfaceutil.implementer(repository.imanifestrevisionwritable)
2020 class memtreemanifestctx(object):
2030 class memtreemanifestctx(object):
2021 def __init__(self, manifestlog, dir=b''):
2031 def __init__(self, manifestlog, dir=b''):
2022 self._manifestlog = manifestlog
2032 self._manifestlog = manifestlog
2023 self._dir = dir
2033 self._dir = dir
2024 self._treemanifest = treemanifest()
2034 self._treemanifest = treemanifest()
2025
2035
2026 def _storage(self):
2036 def _storage(self):
2027 return self._manifestlog.getstorage(b'')
2037 return self._manifestlog.getstorage(b'')
2028
2038
2029 def copy(self):
2039 def copy(self):
2030 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
2040 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
2031 memmf._treemanifest = self._treemanifest.copy()
2041 memmf._treemanifest = self._treemanifest.copy()
2032 return memmf
2042 return memmf
2033
2043
2034 def read(self):
2044 def read(self):
2035 return self._treemanifest
2045 return self._treemanifest
2036
2046
2037 def write(self, transaction, link, p1, p2, added, removed, match=None):
2047 def write(self, transaction, link, p1, p2, added, removed, match=None):
2038 def readtree(dir, node):
2048 def readtree(dir, node):
2039 return self._manifestlog.get(dir, node).read()
2049 return self._manifestlog.get(dir, node).read()
2040
2050
2041 return self._storage().add(
2051 return self._storage().add(
2042 self._treemanifest,
2052 self._treemanifest,
2043 transaction,
2053 transaction,
2044 link,
2054 link,
2045 p1,
2055 p1,
2046 p2,
2056 p2,
2047 added,
2057 added,
2048 removed,
2058 removed,
2049 readtree=readtree,
2059 readtree=readtree,
2050 match=match,
2060 match=match,
2051 )
2061 )
2052
2062
2053
2063
2054 @interfaceutil.implementer(repository.imanifestrevisionstored)
2064 @interfaceutil.implementer(repository.imanifestrevisionstored)
2055 class treemanifestctx(object):
2065 class treemanifestctx(object):
2056 def __init__(self, manifestlog, dir, node):
2066 def __init__(self, manifestlog, dir, node):
2057 self._manifestlog = manifestlog
2067 self._manifestlog = manifestlog
2058 self._dir = dir
2068 self._dir = dir
2059 self._data = None
2069 self._data = None
2060
2070
2061 self._node = node
2071 self._node = node
2062
2072
2063 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
2073 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
2064 # we can instantiate treemanifestctx objects for directories we don't
2074 # we can instantiate treemanifestctx objects for directories we don't
2065 # have on disk.
2075 # have on disk.
2066 # self.p1, self.p2 = store.parents(node)
2076 # self.p1, self.p2 = store.parents(node)
2067 # rev = store.rev(node)
2077 # rev = store.rev(node)
2068 # self.linkrev = store.linkrev(rev)
2078 # self.linkrev = store.linkrev(rev)
2069
2079
2070 def _storage(self):
2080 def _storage(self):
2071 narrowmatch = self._manifestlog._narrowmatch
2081 narrowmatch = self._manifestlog._narrowmatch
2072 if not narrowmatch.always():
2082 if not narrowmatch.always():
2073 if not narrowmatch.visitdir(self._dir[:-1]):
2083 if not narrowmatch.visitdir(self._dir[:-1]):
2074 return excludedmanifestrevlog(self._dir)
2084 return excludedmanifestrevlog(self._dir)
2075 return self._manifestlog.getstorage(self._dir)
2085 return self._manifestlog.getstorage(self._dir)
2076
2086
2077 def read(self):
2087 def read(self):
2078 if self._data is None:
2088 if self._data is None:
2079 store = self._storage()
2089 store = self._storage()
2080 if self._node == nullid:
2090 if self._node == nullid:
2081 self._data = treemanifest()
2091 self._data = treemanifest()
2082 # TODO accessing non-public API
2092 # TODO accessing non-public API
2083 elif store._treeondisk:
2093 elif store._treeondisk:
2084 m = treemanifest(dir=self._dir)
2094 m = treemanifest(dir=self._dir)
2085
2095
2086 def gettext():
2096 def gettext():
2087 return store.revision(self._node)
2097 return store.revision(self._node)
2088
2098
2089 def readsubtree(dir, subm):
2099 def readsubtree(dir, subm):
2090 # Set verify to False since we need to be able to create
2100 # Set verify to False since we need to be able to create
2091 # subtrees for trees that don't exist on disk.
2101 # subtrees for trees that don't exist on disk.
2092 return self._manifestlog.get(dir, subm, verify=False).read()
2102 return self._manifestlog.get(dir, subm, verify=False).read()
2093
2103
2094 m.read(gettext, readsubtree)
2104 m.read(gettext, readsubtree)
2095 m.setnode(self._node)
2105 m.setnode(self._node)
2096 self._data = m
2106 self._data = m
2097 else:
2107 else:
2098 if self._node in store.fulltextcache:
2108 if self._node in store.fulltextcache:
2099 text = pycompat.bytestr(store.fulltextcache[self._node])
2109 text = pycompat.bytestr(store.fulltextcache[self._node])
2100 else:
2110 else:
2101 text = store.revision(self._node)
2111 text = store.revision(self._node)
2102 arraytext = bytearray(text)
2112 arraytext = bytearray(text)
2103 store.fulltextcache[self._node] = arraytext
2113 store.fulltextcache[self._node] = arraytext
2104 self._data = treemanifest(dir=self._dir, text=text)
2114 self._data = treemanifest(dir=self._dir, text=text)
2105
2115
2106 return self._data
2116 return self._data
2107
2117
2108 def node(self):
2118 def node(self):
2109 return self._node
2119 return self._node
2110
2120
2111 def copy(self):
2121 def copy(self):
2112 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
2122 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
2113 memmf._treemanifest = self.read().copy()
2123 memmf._treemanifest = self.read().copy()
2114 return memmf
2124 return memmf
2115
2125
2116 @propertycache
2126 @propertycache
2117 def parents(self):
2127 def parents(self):
2118 return self._storage().parents(self._node)
2128 return self._storage().parents(self._node)
2119
2129
2120 def readdelta(self, shallow=False):
2130 def readdelta(self, shallow=False):
2121 '''Returns a manifest containing just the entries that are present
2131 '''Returns a manifest containing just the entries that are present
2122 in this manifest, but not in its p1 manifest. This is efficient to read
2132 in this manifest, but not in its p1 manifest. This is efficient to read
2123 if the revlog delta is already p1.
2133 if the revlog delta is already p1.
2124
2134
2125 If `shallow` is True, this will read the delta for this directory,
2135 If `shallow` is True, this will read the delta for this directory,
2126 without recursively reading subdirectory manifests. Instead, any
2136 without recursively reading subdirectory manifests. Instead, any
2127 subdirectory entry will be reported as it appears in the manifest, i.e.
2137 subdirectory entry will be reported as it appears in the manifest, i.e.
2128 the subdirectory will be reported among files and distinguished only by
2138 the subdirectory will be reported among files and distinguished only by
2129 its 't' flag.
2139 its 't' flag.
2130 '''
2140 '''
2131 store = self._storage()
2141 store = self._storage()
2132 if shallow:
2142 if shallow:
2133 r = store.rev(self._node)
2143 r = store.rev(self._node)
2134 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
2144 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
2135 return manifestdict(d)
2145 return manifestdict(d)
2136 else:
2146 else:
2137 # Need to perform a slow delta
2147 # Need to perform a slow delta
2138 r0 = store.deltaparent(store.rev(self._node))
2148 r0 = store.deltaparent(store.rev(self._node))
2139 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
2149 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
2140 m1 = self.read()
2150 m1 = self.read()
2141 md = treemanifest(dir=self._dir)
2151 md = treemanifest(dir=self._dir)
2142 for f, ((n0, fl0), (n1, fl1)) in pycompat.iteritems(m0.diff(m1)):
2152 for f, ((n0, fl0), (n1, fl1)) in pycompat.iteritems(m0.diff(m1)):
2143 if n1:
2153 if n1:
2144 md[f] = n1
2154 md[f] = n1
2145 if fl1:
2155 if fl1:
2146 md.setflag(f, fl1)
2156 md.setflag(f, fl1)
2147 return md
2157 return md
2148
2158
2149 def readfast(self, shallow=False):
2159 def readfast(self, shallow=False):
2150 '''Calls either readdelta or read, based on which would be less work.
2160 '''Calls either readdelta or read, based on which would be less work.
2151 readdelta is called if the delta is against the p1, and therefore can be
2161 readdelta is called if the delta is against the p1, and therefore can be
2152 read quickly.
2162 read quickly.
2153
2163
2154 If `shallow` is True, it only returns the entries from this manifest,
2164 If `shallow` is True, it only returns the entries from this manifest,
2155 and not any submanifests.
2165 and not any submanifests.
2156 '''
2166 '''
2157 store = self._storage()
2167 store = self._storage()
2158 r = store.rev(self._node)
2168 r = store.rev(self._node)
2159 deltaparent = store.deltaparent(r)
2169 deltaparent = store.deltaparent(r)
2160 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
2170 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
2161 return self.readdelta(shallow=shallow)
2171 return self.readdelta(shallow=shallow)
2162
2172
2163 if shallow:
2173 if shallow:
2164 return manifestdict(store.revision(self._node))
2174 return manifestdict(store.revision(self._node))
2165 else:
2175 else:
2166 return self.read()
2176 return self.read()
2167
2177
2168 def find(self, key):
2178 def find(self, key):
2169 return self.read().find(key)
2179 return self.read().find(key)
2170
2180
2171
2181
2172 class excludeddir(treemanifest):
2182 class excludeddir(treemanifest):
2173 """Stand-in for a directory that is excluded from the repository.
2183 """Stand-in for a directory that is excluded from the repository.
2174
2184
2175 With narrowing active on a repository that uses treemanifests,
2185 With narrowing active on a repository that uses treemanifests,
2176 some of the directory revlogs will be excluded from the resulting
2186 some of the directory revlogs will be excluded from the resulting
2177 clone. This is a huge storage win for clients, but means we need
2187 clone. This is a huge storage win for clients, but means we need
2178 some sort of pseudo-manifest to surface to internals so we can
2188 some sort of pseudo-manifest to surface to internals so we can
2179 detect a merge conflict outside the narrowspec. That's what this
2189 detect a merge conflict outside the narrowspec. That's what this
2180 class is: it stands in for a directory whose node is known, but
2190 class is: it stands in for a directory whose node is known, but
2181 whose contents are unknown.
2191 whose contents are unknown.
2182 """
2192 """
2183
2193
2184 def __init__(self, dir, node):
2194 def __init__(self, dir, node):
2185 super(excludeddir, self).__init__(dir)
2195 super(excludeddir, self).__init__(dir)
2186 self._node = node
2196 self._node = node
2187 # Add an empty file, which will be included by iterators and such,
2197 # Add an empty file, which will be included by iterators and such,
2188 # appearing as the directory itself (i.e. something like "dir/")
2198 # appearing as the directory itself (i.e. something like "dir/")
2189 self._files[b''] = node
2199 self._files[b''] = node
2190 self._flags[b''] = b't'
2200 self._flags[b''] = b't'
2191
2201
2192 # Manifests outside the narrowspec should never be modified, so avoid
2202 # Manifests outside the narrowspec should never be modified, so avoid
2193 # copying. This makes a noticeable difference when there are very many
2203 # copying. This makes a noticeable difference when there are very many
2194 # directories outside the narrowspec. Also, it makes sense for the copy to
2204 # directories outside the narrowspec. Also, it makes sense for the copy to
2195 # be of the same type as the original, which would not happen with the
2205 # be of the same type as the original, which would not happen with the
2196 # super type's copy().
2206 # super type's copy().
2197 def copy(self):
2207 def copy(self):
2198 return self
2208 return self
2199
2209
2200
2210
2201 class excludeddirmanifestctx(treemanifestctx):
2211 class excludeddirmanifestctx(treemanifestctx):
2202 """context wrapper for excludeddir - see that docstring for rationale"""
2212 """context wrapper for excludeddir - see that docstring for rationale"""
2203
2213
2204 def __init__(self, dir, node):
2214 def __init__(self, dir, node):
2205 self._dir = dir
2215 self._dir = dir
2206 self._node = node
2216 self._node = node
2207
2217
2208 def read(self):
2218 def read(self):
2209 return excludeddir(self._dir, self._node)
2219 return excludeddir(self._dir, self._node)
2210
2220
2211 def write(self, *args):
2221 def write(self, *args):
2212 raise error.ProgrammingError(
2222 raise error.ProgrammingError(
2213 b'attempt to write manifest from excluded dir %s' % self._dir
2223 b'attempt to write manifest from excluded dir %s' % self._dir
2214 )
2224 )
2215
2225
2216
2226
2217 class excludedmanifestrevlog(manifestrevlog):
2227 class excludedmanifestrevlog(manifestrevlog):
2218 """Stand-in for excluded treemanifest revlogs.
2228 """Stand-in for excluded treemanifest revlogs.
2219
2229
2220 When narrowing is active on a treemanifest repository, we'll have
2230 When narrowing is active on a treemanifest repository, we'll have
2221 references to directories we can't see due to the revlog being
2231 references to directories we can't see due to the revlog being
2222 skipped. This class exists to conform to the manifestrevlog
2232 skipped. This class exists to conform to the manifestrevlog
2223 interface for those directories and proactively prevent writes to
2233 interface for those directories and proactively prevent writes to
2224 outside the narrowspec.
2234 outside the narrowspec.
2225 """
2235 """
2226
2236
2227 def __init__(self, dir):
2237 def __init__(self, dir):
2228 self._dir = dir
2238 self._dir = dir
2229
2239
2230 def __len__(self):
2240 def __len__(self):
2231 raise error.ProgrammingError(
2241 raise error.ProgrammingError(
2232 b'attempt to get length of excluded dir %s' % self._dir
2242 b'attempt to get length of excluded dir %s' % self._dir
2233 )
2243 )
2234
2244
2235 def rev(self, node):
2245 def rev(self, node):
2236 raise error.ProgrammingError(
2246 raise error.ProgrammingError(
2237 b'attempt to get rev from excluded dir %s' % self._dir
2247 b'attempt to get rev from excluded dir %s' % self._dir
2238 )
2248 )
2239
2249
2240 def linkrev(self, node):
2250 def linkrev(self, node):
2241 raise error.ProgrammingError(
2251 raise error.ProgrammingError(
2242 b'attempt to get linkrev from excluded dir %s' % self._dir
2252 b'attempt to get linkrev from excluded dir %s' % self._dir
2243 )
2253 )
2244
2254
2245 def node(self, rev):
2255 def node(self, rev):
2246 raise error.ProgrammingError(
2256 raise error.ProgrammingError(
2247 b'attempt to get node from excluded dir %s' % self._dir
2257 b'attempt to get node from excluded dir %s' % self._dir
2248 )
2258 )
2249
2259
2250 def add(self, *args, **kwargs):
2260 def add(self, *args, **kwargs):
2251 # We should never write entries in dirlogs outside the narrow clone.
2261 # We should never write entries in dirlogs outside the narrow clone.
2252 # However, the method still gets called from writesubtree() in
2262 # However, the method still gets called from writesubtree() in
2253 # _addtree(), so we need to handle it. We should possibly make that
2263 # _addtree(), so we need to handle it. We should possibly make that
2254 # avoid calling add() with a clean manifest (_dirty is always False
2264 # avoid calling add() with a clean manifest (_dirty is always False
2255 # in excludeddir instances).
2265 # in excludeddir instances).
2256 pass
2266 pass
General Comments 0
You need to be logged in to leave comments. Login now