Mercurial > hg
comparison hgext/remotefilelog/metadatastore.py @ 40495:3a333a582d7b
remotefilelog: import pruned-down remotefilelog extension from hg-experimental
This is remotefilelog as of my recent patches for compatibility with
current tip of hg, minus support for old versions of Mercurial and
some FB-specific features like their treemanifest extension and
fetching linkrev data from a patched phabricator. The file extutil.py
moved from hgext3rd to remotefilelog.
This is not yet ready to be landed, consider it a preview for
now. Planned changes include:
* replace lz4 with zstd
* rename some capabilities, requirements and wireproto commands to mark
them as experimental
* consolidate bits of shallowutil with related functions (eg readfile)
I'm certainly open to other (small) changes, but my rough mission is
to land this largely as-is so we can use it as a model of the
functionality we need going forward for lazy-fetching of file contents
from a server.
# no-check-commit because of a few foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D4782
author | Augie Fackler <augie@google.com> |
---|---|
date | Thu, 27 Sep 2018 13:03:19 -0400 |
parents | |
children | 13d4ad8d7801 |
comparison
equal
deleted
inserted
replaced
40494:9aeb9e2d28a7 | 40495:3a333a582d7b |
---|---|
1 from __future__ import absolute_import | |
2 | |
3 from mercurial.node import hex, nullid | |
4 from . import ( | |
5 basestore, | |
6 shallowutil, | |
7 ) | |
8 | |
9 class unionmetadatastore(basestore.baseunionstore): | |
10 def __init__(self, *args, **kwargs): | |
11 super(unionmetadatastore, self).__init__(*args, **kwargs) | |
12 | |
13 self.stores = args | |
14 self.writestore = kwargs.get('writestore') | |
15 | |
16 # If allowincomplete==True then the union store can return partial | |
17 # ancestor lists, otherwise it will throw a KeyError if a full | |
18 # history can't be found. | |
19 self.allowincomplete = kwargs.get('allowincomplete', False) | |
20 | |
21 def getancestors(self, name, node, known=None): | |
22 """Returns as many ancestors as we're aware of. | |
23 | |
24 return value: { | |
25 node: (p1, p2, linknode, copyfrom), | |
26 ... | |
27 } | |
28 """ | |
29 if known is None: | |
30 known = set() | |
31 if node in known: | |
32 return [] | |
33 | |
34 ancestors = {} | |
35 def traverse(curname, curnode): | |
36 # TODO: this algorithm has the potential to traverse parts of | |
37 # history twice. Ex: with A->B->C->F and A->B->D->F, both D and C | |
38 # may be queued as missing, then B and A are traversed for both. | |
39 queue = [(curname, curnode)] | |
40 missing = [] | |
41 seen = set() | |
42 while queue: | |
43 name, node = queue.pop() | |
44 if (name, node) in seen: | |
45 continue | |
46 seen.add((name, node)) | |
47 value = ancestors.get(node) | |
48 if not value: | |
49 missing.append((name, node)) | |
50 continue | |
51 p1, p2, linknode, copyfrom = value | |
52 if p1 != nullid and p1 not in known: | |
53 queue.append((copyfrom or curname, p1)) | |
54 if p2 != nullid and p2 not in known: | |
55 queue.append((curname, p2)) | |
56 return missing | |
57 | |
58 missing = [(name, node)] | |
59 while missing: | |
60 curname, curnode = missing.pop() | |
61 try: | |
62 ancestors.update(self._getpartialancestors(curname, curnode, | |
63 known=known)) | |
64 newmissing = traverse(curname, curnode) | |
65 missing.extend(newmissing) | |
66 except KeyError: | |
67 # If we allow incomplete histories, don't throw. | |
68 if not self.allowincomplete: | |
69 raise | |
70 # If the requested name+node doesn't exist, always throw. | |
71 if (curname, curnode) == (name, node): | |
72 raise | |
73 | |
74 # TODO: ancestors should probably be (name, node) -> (value) | |
75 return ancestors | |
76 | |
77 @basestore.baseunionstore.retriable | |
78 def _getpartialancestors(self, name, node, known=None): | |
79 for store in self.stores: | |
80 try: | |
81 return store.getancestors(name, node, known=known) | |
82 except KeyError: | |
83 pass | |
84 | |
85 raise KeyError((name, hex(node))) | |
86 | |
87 @basestore.baseunionstore.retriable | |
88 def getnodeinfo(self, name, node): | |
89 for store in self.stores: | |
90 try: | |
91 return store.getnodeinfo(name, node) | |
92 except KeyError: | |
93 pass | |
94 | |
95 raise KeyError((name, hex(node))) | |
96 | |
97 def add(self, name, node, data): | |
98 raise RuntimeError("cannot add content only to remotefilelog " | |
99 "contentstore") | |
100 | |
101 def getmissing(self, keys): | |
102 missing = keys | |
103 for store in self.stores: | |
104 if missing: | |
105 missing = store.getmissing(missing) | |
106 return missing | |
107 | |
108 def markledger(self, ledger, options=None): | |
109 for store in self.stores: | |
110 store.markledger(ledger, options) | |
111 | |
112 def getmetrics(self): | |
113 metrics = [s.getmetrics() for s in self.stores] | |
114 return shallowutil.sumdicts(*metrics) | |
115 | |
116 class remotefilelogmetadatastore(basestore.basestore): | |
117 def getancestors(self, name, node, known=None): | |
118 """Returns as many ancestors as we're aware of. | |
119 | |
120 return value: { | |
121 node: (p1, p2, linknode, copyfrom), | |
122 ... | |
123 } | |
124 """ | |
125 data = self._getdata(name, node) | |
126 ancestors = shallowutil.ancestormap(data) | |
127 return ancestors | |
128 | |
129 def getnodeinfo(self, name, node): | |
130 return self.getancestors(name, node)[node] | |
131 | |
132 def add(self, name, node, parents, linknode): | |
133 raise RuntimeError("cannot add metadata only to remotefilelog " | |
134 "metadatastore") | |
135 | |
136 class remotemetadatastore(object): | |
137 def __init__(self, ui, fileservice, shared): | |
138 self._fileservice = fileservice | |
139 self._shared = shared | |
140 | |
141 def getancestors(self, name, node, known=None): | |
142 self._fileservice.prefetch([(name, hex(node))], force=True, | |
143 fetchdata=False, fetchhistory=True) | |
144 return self._shared.getancestors(name, node, known=known) | |
145 | |
146 def getnodeinfo(self, name, node): | |
147 return self.getancestors(name, node)[node] | |
148 | |
149 def add(self, name, node, data): | |
150 raise RuntimeError("cannot add to a remote store") | |
151 | |
152 def getmissing(self, keys): | |
153 return keys | |
154 | |
155 def markledger(self, ledger, options=None): | |
156 pass |