diff --git a/mercurial/wireprotov2server.py b/mercurial/wireprotov2server.py --- a/mercurial/wireprotov2server.py +++ b/mercurial/wireprotov2server.py @@ -984,9 +984,7 @@ def getfilestore(repo, proto, path): return fl -def emitfilerevisions(repo, path, revisions, fields): - clnode = repo.changelog.node - +def emitfilerevisions(repo, path, revisions, linknodes, fields): for revision in revisions: d = { b'node': revision.node, @@ -996,13 +994,7 @@ def emitfilerevisions(repo, path, revisi d[b'parents'] = [revision.p1node, revision.p2node] if b'linknode' in fields: - # TODO by creating the filectx against a specific file revision - # instead of changeset, linkrev() is always used. This is wrong for - # cases where linkrev() may refer to a hidden changeset. We need an - # API for performing linkrev adjustment that takes this into - # account. - fctx = repo.filectx(path, fileid=revision.node) - d[b'linknode'] = clnode(fctx.introrev()) + d[b'linknode'] = linknodes[revision.node] followingmeta = [] followingdata = [] @@ -1086,6 +1078,9 @@ def filedata(repo, proto, haveparents, n except FileAccessError as e: raise error.WireprotoCommandError(e.msg, e.args) + clnode = repo.changelog.node + linknodes = {} + # Validate requested nodes. for node in nodes: try: @@ -1094,6 +1089,14 @@ def filedata(repo, proto, haveparents, n raise error.WireprotoCommandError('unknown file node: %s', (hex(node),)) + # TODO by creating the filectx against a specific file revision + # instead of changeset, linkrev() is always used. This is wrong for + # cases where linkrev() may refer to a hidden changeset. But since this + # API doesn't know anything about changesets, we're not sure how to + # disambiguate the linknode. Perhaps we should delete this API? + fctx = repo.filectx(path, fileid=node) + linknodes[node] = clnode(fctx.introrev()) + revisions = store.emitrevisions(nodes, revisiondata=b'revision' in fields, assumehaveparentrevisions=haveparents) @@ -1102,7 +1105,7 @@ def filedata(repo, proto, haveparents, n b'totalitems': len(nodes), } - for o in emitfilerevisions(repo, path, revisions, fields): + for o in emitfilerevisions(repo, path, revisions, linknodes, fields): yield o def filesdatacapabilities(repo, proto): @@ -1154,12 +1157,14 @@ def filesdata(repo, proto, haveparents, # changeset. cl = repo.changelog + clnode = cl.node outgoing = resolvenodes(repo, revisions) filematcher = makefilematcher(repo, pathfilter) # Figure out what needs to be emitted. changedpaths = set() - fnodes = collections.defaultdict(set) + # path -> {fnode: linknode} + fnodes = collections.defaultdict(dict) for node in outgoing: ctx = repo[node] @@ -1182,7 +1187,7 @@ def filesdata(repo, proto, haveparents, linkrev = store.linkrev(rev) if linkrev in outgoingclrevs: - fnodes[path].add(store.node(rev)) + fnodes[path].setdefault(store.node(rev), clnode(linkrev)) # If ancestors aren't known, we walk the manifests and send all # encountered file revisions. @@ -1192,7 +1197,7 @@ def filesdata(repo, proto, haveparents, for path, fnode in mctx.read().items(): if filematcher(path): - fnodes[path].add(fnode) + fnodes[path].setdefault(fnode, node) yield { b'totalpaths': len(fnodes), @@ -1210,11 +1215,11 @@ def filesdata(repo, proto, haveparents, b'totalitems': len(filenodes), } - revisions = store.emitrevisions(filenodes, + revisions = store.emitrevisions(filenodes.keys(), revisiondata=b'revision' in fields, assumehaveparentrevisions=haveparents) - for o in emitfilerevisions(repo, path, revisions, fields): + for o in emitfilerevisions(repo, path, revisions, filenodes, fields): yield o @wireprotocommand( diff --git a/tests/test-wireproto-command-filesdata.t b/tests/test-wireproto-command-filesdata.t --- a/tests/test-wireproto-command-filesdata.t +++ b/tests/test-wireproto-command-filesdata.t @@ -1239,7 +1239,6 @@ Request for changeset introducing fileno Request for changeset where recorded linknode isn't in DAG ancestry will get rewritten accordingly -TODO this is buggy $ sendhttpv2peer << EOF > command filesdata @@ -1263,7 +1262,7 @@ TODO this is buggy b'totalitems': 1 }, { - b'linknode': b'\xb1l\xce)g\xc1t\x9e\xf4\xf4\xe3\x08j\x80l\xfb\xad\x8a:\xf7', + b'linknode': b'G\xfc0X\t\x11#,\xb2dg[@(\x19\xde\xdd\xf6\xc6\xf0', b'node': b'.\xd2\xa3\x91*\x0b$P C\xea\xe8N\xe4\xb2y\xc1\x8b\x90\xdd' } ] diff --git a/tests/test-wireproto-exchangev2-shallow.t b/tests/test-wireproto-exchangev2-shallow.t --- a/tests/test-wireproto-exchangev2-shallow.t +++ b/tests/test-wireproto-exchangev2-shallow.t @@ -196,7 +196,7 @@ Shallow clone pulls down latest revision node = I\x1d\xa1\xbb\x89\xeax\xc0\xc0\xa2s[\x16\xce}\x93\x1d\xc8\xe2\r (esc) p1rev = -1 p2rev = -1 - linkrev = 4 + linkrev = 5 flags = 2 id = 4 @@ -205,7 +205,7 @@ Shallow clone pulls down latest revision node = S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4& (esc) p1rev = -1 p2rev = -1 - linkrev = 1 + linkrev = 5 flags = 0 id = 5 @@ -214,7 +214,7 @@ Shallow clone pulls down latest revision node = ]\xf3\xac\xd8\xd0\xc7\xfaP\x98\xd0'\x9a\x044\xc3\x02\x9e+x\xe1 (esc) p1rev = -1 p2rev = -1 - linkrev = 4 + linkrev = 5 flags = 2 id = 6 @@ -223,7 +223,7 @@ Shallow clone pulls down latest revision node = (\xc7v\xae\x08\xd0\xd5^\xb4\x06H\xb4\x01\xb9\x0f\xf5DH4\x8e (esc) p1rev = -1 p2rev = -1 - linkrev = 4 + linkrev = 5 flags = 2 Test a shallow clone with only some files @@ -342,7 +342,7 @@ Test a shallow clone with only some file node = I\x1d\xa1\xbb\x89\xeax\xc0\xc0\xa2s[\x16\xce}\x93\x1d\xc8\xe2\r (esc) p1rev = -1 p2rev = -1 - linkrev = 4 + linkrev = 5 flags = 2 id = 2 @@ -351,7 +351,7 @@ Test a shallow clone with only some file node = S\x82\x06\xdc\x97\x1eR\x15@\xd6\x84:\xbf\xe6\xd1`2\xf6\xd4& (esc) p1rev = -1 p2rev = -1 - linkrev = 1 + linkrev = 5 flags = 0 Cloning an old revision with depth=1 works