Mercurial > public > mercurial-scm > hg-stable
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: |