Mercurial > hg
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 } |