From 13c2a7823fad147e135691215c37aa52fc148377 Mon Sep 17 00:00:00 2001 From: Durham Goode Date: Wed, 13 Jan 2016 11:25:26 -0800 Subject: [PATCH] Add alternative linkrev lookup logic Summary: The old linkrev lookup logic depended on the repo containing the latest commit to have contained that particular version of the file. If the latest version had been stripped however (like what happens in rebase --abort currently), the linkrev function would attempt to scan history from the current rev, trying to find the linkrev node. If the filectx was not provided with a 'current node', the linkrev function would return None. This caused certain places to break, like the Mercurial merge conflict resolution logic (which constructs a filectx using only a fileid, and no changeid, for the merge ancestor). The fix is to allow scanning all the latest commits in the repo, looking for the appropriate linkrev. This is pretty slow (1 second for every 14,000 commits inspected), but is better than just returning None and crashing. Test Plan: Manually repro'd the issue by making a commit, amending it, stripping the amended version and going back to the original, making two sibling commits on top of the original, then rebasing sibling 1 onto sibling 2 (so that the original commit that had the bad linknode data was the ancestor during the merge). Previously this failed, now it passes. I'd write a test, but it's 11pm and I'm tired and I need this in by early tomorrow morning to make the cut. Reviewers: #sourcecontrol, ttung, rmcelroy Reviewed By: rmcelroy Subscribers: trunkagent, rmcelroy Differential Revision: https://phabricator.fb.com/D2826850 Signature: t1:2826850:1452680293:cb8c1f8c20ce13ad632925137dbdce6e994ab360 --- remotefilelog/remotefilectx.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/remotefilelog/remotefilectx.py b/remotefilelog/remotefilectx.py index 5774bce3fc..339fce9d20 100644 --- a/remotefilelog/remotefilectx.py +++ b/remotefilelog/remotefilectx.py @@ -56,7 +56,26 @@ class remotefilectx(context.filectx): if rev is not None: return rev - # TODO: look up alternate histories in historical blob files + # Search all commits for the appropriate linkrev (slow, but uncommon) + path = self._path + fileid = self._fileid + cl = self._repo.unfiltered().changelog + ma = self._repo.manifest + + # If we don't find it within the top 100,000 commits, it probably + # doesn't exist, so give up early instead of wasting the user's time. + searchlimit = max(0, len(cl) - 100000) + for rev in range(len(cl) - 1, searchlimit, -1): + node = cl.node(rev) + data = cl.read(node) # get changeset data (we avoid object creation) + if path in data[3]: # checking the 'files' field. + # The file has been touched, check if the hash is what we're + # looking for. + if fileid == ma.readfast(data[0]).get(path): + return rev + + # Couldn't find the linkrev. This should generally not happen, and will + # likely cause a crash. return None def introrev(self):