@@ -45,6 +45,12 @@ 0
1
RevlogNG (*next generation*). It replaced version 0 when it was
implemented in 2006.
+2
+ In-development version incorporating accumulated knowledge and
+ missing features from 10+ years of revlog version 1.
+57005 (0xdead)
+ Reserved for internal testing of new versions. No defined format
+ beyond 32-bit header.
The feature flags short consists of bit flags. Where 0 is the least
significant bit, the following bit offsets define flags:
@@ -142,6 +148,14 @@ length from bytes 8-11 define how to acc
The first 4 bytes of the revlog are shared between the revlog header
and the 6 byte absolute offset field from the first revlog entry.
+Version 2 Format
+================
+
+(In development. Format not finalized or stable.)
+
+Version 2 is currently identical to version 1. This will obviously
+change.
+
Delta Chains
============
@@ -245,6 +245,10 @@ class locallegacypeer(localpeer):
def changegroupsubset(self, bases, heads, source):
return changegroup.changegroupsubset(self._repo, bases, heads, source)
+# Increment the sub-version when the revlog v2 format changes to lock out old
+# clients.
+REVLOGV2_REQUIREMENT = 'exp-revlogv2.0'
+
class localrepository(object):
supportedformats = {
@@ -252,6 +256,7 @@ class localrepository(object):
'generaldelta',
'treemanifest',
'manifestv2',
+ REVLOGV2_REQUIREMENT,
}
_basesupported = supportedformats | {
'store',
@@ -441,6 +446,10 @@ class localrepository(object):
if r.startswith('exp-compression-'):
self.svfs.options['compengine'] = r[len('exp-compression-'):]
+ # TODO move "revlogv2" to openerreqs once finalized.
+ if REVLOGV2_REQUIREMENT in self.requirements:
+ self.svfs.options['revlogv2'] = True
+
def _writerequirements(self):
scmutil.writerequires(self.vfs, self.requirements)
@@ -2070,4 +2079,11 @@ def newreporequirements(repo):
if ui.configbool('experimental', 'manifestv2', False):
requirements.add('manifestv2')
+ revlogv2 = ui.config('experimental', 'revlogv2')
+ if revlogv2 == 'enable-unstable-format-and-corrupt-my-data':
+ requirements.remove('revlogv1')
+ # generaldelta is implied by revlogv2.
+ requirements.discard('generaldelta')
+ requirements.add(REVLOGV2_REQUIREMENT)
+
return requirements
@@ -46,12 +46,16 @@ from . import (
# revlog header flags
REVLOGV0 = 0
REVLOGV1 = 1
+# Dummy value until file format is finalized.
+# Reminder: change the bounds check in revlog.__init__ when this is changed.
+REVLOGV2 = 0xDEAD
FLAG_INLINE_DATA = (1 << 16)
FLAG_GENERALDELTA = (1 << 17)
REVLOG_DEFAULT_FLAGS = FLAG_INLINE_DATA
REVLOG_DEFAULT_FORMAT = REVLOGV1
REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
REVLOGV1_FLAGS = FLAG_INLINE_DATA | FLAG_GENERALDELTA
+REVLOGV2_FLAGS = REVLOGV1_FLAGS
# revlog index flags
REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
@@ -286,7 +290,10 @@ class revlog(object):
v = REVLOG_DEFAULT_VERSION
opts = getattr(opener, 'options', None)
if opts is not None:
- if 'revlogv1' in opts:
+ if 'revlogv2' in opts:
+ # version 2 revlogs always use generaldelta.
+ v = REVLOGV2 | FLAG_GENERALDELTA | FLAG_INLINE_DATA
+ elif 'revlogv1' in opts:
if 'generaldelta' in opts:
v |= FLAG_GENERALDELTA
else:
@@ -336,6 +343,11 @@ class revlog(object):
raise RevlogError(_('unknown flags (%#04x) in version %d '
'revlog %s') %
(flags >> 16, fmt, self.indexfile))
+ elif fmt == REVLOGV2:
+ if flags & ~REVLOGV2_FLAGS:
+ raise RevlogError(_('unknown flags (%#04x) in version %d '
+ 'revlog %s') %
+ (flags >> 16, fmt, self.indexfile))
elif fmt > REVLOGV1:
raise RevlogError(_('unknown version (%d) in revlog %s') %
(fmt, self.indexfile))
new file mode 100644
@@ -0,0 +1,62 @@
+A repo with unknown revlogv2 requirement string cannot be opened
+
+ $ hg init invalidreq
+ $ cd invalidreq
+ $ echo exp-revlogv2.unknown >> .hg/requires
+ $ hg log
+ abort: repository requires features unknown to this Mercurial: exp-revlogv2.unknown!
+ (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
+ [255]
+ $ cd ..
+
+Can create and open repo with revlog v2 requirement
+
+ $ cat >> $HGRCPATH << EOF
+ > [experimental]
+ > revlogv2 = enable-unstable-format-and-corrupt-my-data
+ > EOF
+
+ $ hg init empty-repo
+ $ cd empty-repo
+ $ cat .hg/requires
+ dotencode
+ exp-revlogv2.0
+ fncache
+ store
+
+ $ hg log
+
+Unknown flags to revlog are rejected
+
+ >>> with open('.hg/store/00changelog.i', 'wb') as fh:
+ ... fh.write('\x00\x04\xde\xad')
+
+ $ hg log
+ abort: unknown flags (0x04) in version 57005 revlog 00changelog.i!
+ [255]
+
+ $ cd ..
+
+Writing a simple revlog v2 works
+
+ $ hg init simple
+ $ cd simple
+ $ touch foo
+ $ hg -q commit -A -m initial
+
+ $ hg log
+ changeset: 0:96ee1d7354c4
+ tag: tip
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: initial
+
+Header written as expected (changelog always disables generaldelta)
+
+ $ f --hexdump --bytes 4 .hg/store/00changelog.i
+ .hg/store/00changelog.i:
+ 0000: 00 01 de ad |....|
+
+ $ f --hexdump --bytes 4 .hg/store/data/foo.i
+ .hg/store/data/foo.i:
+ 0000: 00 03 de ad |....|