sapling/upgradegeneraldelta.py
Siddharth Agarwal 2d094f51a1 upgradegeneraldelta: only upgrade before pulls
Summary: This is nicer because it only does the upgrade when an operation that'll probably take a while anyway is run.

Test Plan: Enabled the extension on a non-generaldelta repo, then ran `hg status`. Saw that it wasn't upgraded. Next, ran `hg pull`, and saw that it was.

Reviewers: davidsp, rmcelroy, akushner, pyd, daviser, mitrandir, ericsumner, durham

Reviewed By: durham

Differential Revision: https://phabricator.fb.com/D1749026

Signature: t1:1749026:1418940170:6fdecc074294b26b7c3e9bd8c1463eefd13a8fc1
2014-12-18 12:43:32 -08:00

117 lines
4.1 KiB
Python

# upgradegeneraldelta.py
#
# Copyright 2014 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
'''upgrade manifests to generaldelta in-place
Performs an upgrade of this repo's manifest to generaldelta on pull, without
needing to reclone. This tries to be as safe as possible but is inherently not a
completely safe operation.
Filelogs are not touched -- the manifest is often the revlog that benefits the
most from generaldelta. New filelogs will be generaldelta, though.
The following configuration options are available:
:upgradegeneraldelta.upgrade: Set to True to actually perform the
upgrade. Default is False.
:upgradegeneraldelta.dryrun: Write out the generaldelta manifest, but do not
move it into place. This will cause the generaldelta manifest to be
rewritten every single time a Mercurial command is run. Default is False.
:upgradegeneraldelta.backup: Set to False to disable backing up the old manifest
as 00manifestold.{i,d}. Default is True.
'''
from mercurial import commands, extensions, revlog, util
import os, struct, weakref
def wrappull(orig, ui, repo, *args, **kwargs):
_upgrade(ui, repo)
orig(ui, repo, *args, **kwargs)
def _upgrade(ui, repo):
if not util.safehasattr(repo, 'svfs'):
return
if not ui.configbool('upgradegeneraldelta', 'upgrade'):
return
f = repo.svfs('00manifest.i')
i = f.read(4)
f.close()
if len(i) < 4:
# empty manifest
return
# probably only works with revlogng -- it became the default years ago so
# that's fine
v = struct.unpack(revlog.versionformat, i)[0]
isgeneraldelta = v & revlog.REVLOGGENERALDELTA
if isgeneraldelta:
return
# write out a new revlog, this time with generaldelta
oldopts = repo.svfs.options
lock = repo.lock()
tr = repo.transaction('upgradegeneraldelta')
try:
ui.warn('upgrading repository to generaldelta\n')
trp = weakref.proxy(tr)
newopts = oldopts.copy()
newopts['generaldelta'] = 1
repo.svfs.options = newopts
# remove 00manifestgd.i if present
repo.svfs.unlinkpath('00manifestgd.i', ignoremissing=True)
newmf = revlog.revlog(repo.svfs, '00manifestgd.i')
oldmf = repo.manifest
i = oldmf.index
chunk = oldmf._chunk
for rev in oldmf:
ui.progress('upgrading', rev, total=len(oldmf))
e = i[rev]
# if the delta base is the rev, this rev is a fulltext
isdelta = (rev != e[3])
revchunk = chunk(rev)
if isdelta:
newmf.addrevision(None, trp, e[4], i[e[5]][7], i[e[6]][7],
cachedelta=(rev - 1, revchunk),
node=e[7])
else:
newmf.addrevision(revchunk, trp, e[4], i[e[5]][7], i[e[6]][7],
node=e[7])
tr.close()
if not ui.configbool('upgradegeneraldelta', 'dryrun', default=False):
manifesti = repo.sjoin('00manifest.i')
manifestd = repo.sjoin('00manifest.d')
manifestgdi = repo.sjoin('00manifestgd.i')
manifestgdd = repo.sjoin('00manifestgd.d')
manifestoldi = repo.sjoin('00manifestold.i')
manifestoldd = repo.sjoin('00manifestold.d')
# move the newly created manifests over
if ui.configbool('upgradegeneraldelta', 'backup', default=True):
os.rename(manifesti, manifestoldi)
if os.path.exists(manifestd):
os.rename(manifestd, manifestoldd)
os.rename(manifestgdi, manifesti)
if os.path.exists(manifestgdd):
os.rename(manifestgdd, manifestd)
with repo.opener('requires', 'a+') as f:
f.write('generaldelta\n')
repo.invalidate()
finally:
ui.progress('upgrading', None)
if tr:
tr.release()
lock.release()
repo.svfs.options = oldopts
def extsetup(ui):
extensions.wrapcommand(commands.table, 'pull', wrappull)