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