comparison mercurial/wireprotov2server.py @ 40924:08cfa77d7288

wireprotov2: unify file revision collection and linknode derivation The old mechanism for choosing which file revisions to send in the haveparents=True case was buggy in multiple ways - the most severe of which being that file revisions were excluded when they shouldn't have been. This commit unifies the logic for deriving the filenodes that will be sent by the "filesdata" command. We now consistently read files data from manifests. The "haveparents" argument now controls whether we iterate ctx.files() or use the full manifest to derive relevant files. The logic here is still woefully lacking to fully support shallow clones. It will require an API break to fully address. This commit should at least make the server APIs emit proper data, which is strictly better than before. Differential Revision: https://phab.mercurial-scm.org/D5406
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 10 Dec 2018 18:55:08 +0000
parents 3ed77780f4a6
children 5cc5a5561c3f
comparison
equal deleted inserted replaced
40923:3ed77780f4a6 40924:08cfa77d7288
1154 # is a race between a client making a push that obsoletes a changeset and 1154 # is a race between a client making a push that obsoletes a changeset and
1155 # another client fetching files data for that changeset. If a client has a 1155 # another client fetching files data for that changeset. If a client has a
1156 # changeset, it should probably be allowed to access files data for that 1156 # changeset, it should probably be allowed to access files data for that
1157 # changeset. 1157 # changeset.
1158 1158
1159 cl = repo.changelog
1160 clnode = cl.node
1161 outgoing = resolvenodes(repo, revisions) 1159 outgoing = resolvenodes(repo, revisions)
1162 filematcher = makefilematcher(repo, pathfilter) 1160 filematcher = makefilematcher(repo, pathfilter)
1163 1161
1164 # Figure out what needs to be emitted.
1165 changedpaths = set()
1166 # path -> {fnode: linknode} 1162 # path -> {fnode: linknode}
1167 fnodes = collections.defaultdict(dict) 1163 fnodes = collections.defaultdict(dict)
1168 1164
1165 # We collect the set of relevant file revisions by iterating the changeset
1166 # revisions and either walking the set of files recorded in the changeset
1167 # or by walking the manifest at that revision. There is probably room for a
1168 # storage-level API to request this data, as it can be expensive to compute
1169 # and would benefit from caching or alternate storage from what revlogs
1170 # provide.
1169 for node in outgoing: 1171 for node in outgoing:
1170 ctx = repo[node] 1172 ctx = repo[node]
1171 changedpaths.update(ctx.files()) 1173 mctx = ctx.manifestctx()
1172 1174 md = mctx.read()
1173 changedpaths = sorted(p for p in changedpaths if filematcher(p)) 1175
1174 1176 if haveparents:
1175 # If ancestors are known, we send file revisions having a linkrev in the 1177 checkpaths = ctx.files()
1176 # outgoing set of changeset revisions. 1178 else:
1177 if haveparents: 1179 checkpaths = md.keys()
1178 outgoingclrevs = set(cl.rev(n) for n in outgoing) 1180
1179 1181 for path in checkpaths:
1180 for path in changedpaths: 1182 fnode = md[path]
1181 try: 1183
1182 store = getfilestore(repo, proto, path) 1184 if path in fnodes and fnode in fnodes[path]:
1183 except FileAccessError as e: 1185 continue
1184 raise error.WireprotoCommandError(e.msg, e.args) 1186
1185 1187 if not filematcher(path):
1186 for rev in store: 1188 continue
1187 linkrev = store.linkrev(rev) 1189
1188 1190 fnodes[path].setdefault(fnode, node)
1189 if linkrev in outgoingclrevs:
1190 fnodes[path].setdefault(store.node(rev), clnode(linkrev))
1191
1192 # If ancestors aren't known, we walk the manifests and send all
1193 # encountered file revisions.
1194 else:
1195 for node in outgoing:
1196 mctx = repo[node].manifestctx()
1197
1198 for path, fnode in mctx.read().items():
1199 if filematcher(path):
1200 fnodes[path].setdefault(fnode, node)
1201 1191
1202 yield { 1192 yield {
1203 b'totalpaths': len(fnodes), 1193 b'totalpaths': len(fnodes),
1204 b'totalitems': sum(len(v) for v in fnodes.values()) 1194 b'totalitems': sum(len(v) for v in fnodes.values())
1205 } 1195 }