comparison 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
comparison
equal deleted inserted replaced
23377:c00b156d6e76 23381:cc0ff93d0c0c
314 progress = self._progress 314 progress = self._progress
315 315
316 # for progress output 316 # for progress output
317 msgbundling = _('bundling') 317 msgbundling = _('bundling')
318 318
319 clrevorder = {}
319 mfs = {} # needed manifests 320 mfs = {} # needed manifests
320 fnodes = {} # needed file nodes 321 fnodes = {} # needed file nodes
321 changedfiles = set() 322 changedfiles = set()
322 323
323 # Callback for the changelog, used to collect changed files and manifest 324 # Callback for the changelog, used to collect changed files and manifest
324 # nodes. 325 # nodes.
325 # Returns the linkrev node (identity in the changelog case). 326 # Returns the linkrev node (identity in the changelog case).
326 def lookupcl(x): 327 def lookupcl(x):
327 c = cl.read(x) 328 c = cl.read(x)
329 clrevorder[x] = len(clrevorder)
328 changedfiles.update(c[3]) 330 changedfiles.update(c[3])
329 # record the first changeset introducing this manifest version 331 # record the first changeset introducing this manifest version
330 mfs.setdefault(c[0], x) 332 mfs.setdefault(c[0], x)
331 return x 333 return x
332 334
338 # Callback for the manifest, used to collect linkrevs for filelog 340 # Callback for the manifest, used to collect linkrevs for filelog
339 # revisions. 341 # revisions.
340 # Returns the linkrev node (collected in lookupcl). 342 # Returns the linkrev node (collected in lookupcl).
341 def lookupmf(x): 343 def lookupmf(x):
342 clnode = mfs[x] 344 clnode = mfs[x]
343 if not fastpathlinkrev: 345 if not fastpathlinkrev or reorder:
344 mdata = mf.readfast(x) 346 mdata = mf.readfast(x)
345 for f, n in mdata.iteritems(): 347 for f, n in mdata.iteritems():
346 if f in changedfiles: 348 if f in changedfiles:
347 # record the first changeset introducing this filelog 349 # record the first changeset introducing this filelog
348 # version 350 # version
349 fnodes.setdefault(f, {}).setdefault(n, clnode) 351 fclnodes = fnodes.setdefault(f, {})
352 fclnode = fclnodes.setdefault(n, clnode)
353 if clrevorder[clnode] < clrevorder[fclnode]:
354 fclnodes[n] = clnode
350 return clnode 355 return clnode
351 356
352 mfnodes = self.prune(mf, mfs, commonrevs, source) 357 mfnodes = self.prune(mf, mfs, commonrevs, source)
353 for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'), 358 for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'),
354 reorder=reorder): 359 reorder=reorder):
357 362
358 mfs.clear() 363 mfs.clear()
359 needed = set(cl.rev(x) for x in clnodes) 364 needed = set(cl.rev(x) for x in clnodes)
360 365
361 def linknodes(filerevlog, fname): 366 def linknodes(filerevlog, fname):
362 if fastpathlinkrev: 367 if fastpathlinkrev and not reorder:
363 llr = filerevlog.linkrev 368 llr = filerevlog.linkrev
364 def genfilenodes(): 369 def genfilenodes():
365 for r in filerevlog: 370 for r in filerevlog:
366 linkrev = llr(r) 371 linkrev = llr(r)
367 if linkrev in needed: 372 if linkrev in needed: