diff mercurial/changegroup.py @ 23381:cc0ff93d0c0c stable

changegroup: fix file linkrevs during reorders (issue4462) Previously, if reorder was true during the creation of a changegroup bundle, it was possible that the manifest and filelogs would be reordered such that the resulting bundle filelog had a linkrev that pointed to a commit that was not the earliest instance of the filelog revision. For example: With commits: 0<-1<---3<-4 \ / --2<--- if 2 and 3 added the same version of a file, if the manifests of 2 and 3 have their order reversed, but the changelog did not, it could produce a filelog with linkrevs 0<-3 instead of 0<-2, which meant if commit 3 was stripped, it would delete that file data from the repository and commit 2 would be corrupt (as would any future pulls that tried to build upon that version of the file). The fix is to make the linkrev fixup smarter. Previously it considered the first manifest that added a file to be the first commit that added that file, which is not true. Now, for every file revision we add to the bundle we make sure we attach it to the earliest applicable linkrev.
author Durham Goode <durham@fb.com>
date Thu, 20 Nov 2014 16:30:57 -0800
parents 5dcaed20b27c
children a81c76106d90
line wrap: on
line diff
--- a/mercurial/changegroup.py	Fri Nov 21 13:58:49 2014 +0800
+++ b/mercurial/changegroup.py	Thu Nov 20 16:30:57 2014 -0800
@@ -316,6 +316,7 @@
         # for progress output
         msgbundling = _('bundling')
 
+        clrevorder = {}
         mfs = {} # needed manifests
         fnodes = {} # needed file nodes
         changedfiles = set()
@@ -325,6 +326,7 @@
         # Returns the linkrev node (identity in the changelog case).
         def lookupcl(x):
             c = cl.read(x)
+            clrevorder[x] = len(clrevorder)
             changedfiles.update(c[3])
             # record the first changeset introducing this manifest version
             mfs.setdefault(c[0], x)
@@ -340,13 +342,16 @@
         # Returns the linkrev node (collected in lookupcl).
         def lookupmf(x):
             clnode = mfs[x]
-            if not fastpathlinkrev:
+            if not fastpathlinkrev or reorder:
                 mdata = mf.readfast(x)
                 for f, n in mdata.iteritems():
                     if f in changedfiles:
                         # record the first changeset introducing this filelog
                         # version
-                        fnodes.setdefault(f, {}).setdefault(n, clnode)
+                        fclnodes = fnodes.setdefault(f, {})
+                        fclnode = fclnodes.setdefault(n, clnode)
+                        if clrevorder[clnode] < clrevorder[fclnode]:
+                            fclnodes[n] = clnode
             return clnode
 
         mfnodes = self.prune(mf, mfs, commonrevs, source)
@@ -359,7 +364,7 @@
         needed = set(cl.rev(x) for x in clnodes)
 
         def linknodes(filerevlog, fname):
-            if fastpathlinkrev:
+            if fastpathlinkrev and not reorder:
                 llr = filerevlog.linkrev
                 def genfilenodes():
                     for r in filerevlog: