Details
Details
- Reviewers
indygreg - Group Reviewers
hg-reviewers - Commits
- rHG6c906eaedd0d: nodemap: track the tip_node for validation
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
| indygreg |
| hg-reviewers |
| No Linters Available |
| No Unit Test Coverage |
| Path | Packages | |||
|---|---|---|---|---|
| M | mercurial/configitems.py (2 lines) | |||
| M | mercurial/debugcommands.py (1 line) | |||
| M | mercurial/revlog.py (7 lines) | |||
| M | mercurial/revlogutils/nodemap.py (19 lines) | |||
| M | tests/test-persistent-nodemap.t (56 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| d0dd19d0c19f | 44b036c3fd95 | Pierre-Yves David | Feb 27 2020, 10:32 AM |
| Status | Author | Revision | |
|---|---|---|---|
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | Alphare | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | marmoute | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | Alphare | ||
| Closed | marmoute | ||
| Closed | marmoute |
| ) | ) | ||||
| coreconfigitem( | coreconfigitem( | ||||
| b'devel', b'legacy.exchange', default=list, | b'devel', b'legacy.exchange', default=list, | ||||
| ) | ) | ||||
| # TODO before getting `persistent-nodemap` out of experimental | # TODO before getting `persistent-nodemap` out of experimental | ||||
| # | # | ||||
| # * code/tests around aborted transaction | # * code/tests around aborted transaction | ||||
| # * code/tests around pending data for hooks | # * code/tests around pending data for hooks | ||||
| # * code/tests around detection of invalid cache | |||||
| # (eg: after strip from an incompatible client) | |||||
| # * regenerate a new nodemap when the unused/total ration is to high | # * regenerate a new nodemap when the unused/total ration is to high | ||||
| # * decide for a "status" of the persistent nodemap and associated location | # * decide for a "status" of the persistent nodemap and associated location | ||||
| # - part of the store next the revlog itself (new requirements) | # - part of the store next the revlog itself (new requirements) | ||||
| # - part of the cache directory | # - part of the cache directory | ||||
| # - part of an `index` directory | # - part of an `index` directory | ||||
| # (https://www.mercurial-scm.org/wiki/ComputedIndexPlan) | # (https://www.mercurial-scm.org/wiki/ComputedIndexPlan) | ||||
| # * do we want to use this for more than just changelog? if so we need: | # * do we want to use this for more than just changelog? if so we need: | ||||
| # - simpler "pending" logic for them | # - simpler "pending" logic for them | ||||
| elif opts['metadata']: | elif opts['metadata']: | ||||
| unfi = repo.unfiltered() | unfi = repo.unfiltered() | ||||
| cl = unfi.changelog | cl = unfi.changelog | ||||
| nm_data = nodemap.persisted_data(cl) | nm_data = nodemap.persisted_data(cl) | ||||
| if nm_data is not None: | if nm_data is not None: | ||||
| docket, data = nm_data | docket, data = nm_data | ||||
| ui.write((b"uid: %s\n") % docket.uid) | ui.write((b"uid: %s\n") % docket.uid) | ||||
| ui.write((b"tip-rev: %d\n") % docket.tip_rev) | ui.write((b"tip-rev: %d\n") % docket.tip_rev) | ||||
| ui.write((b"tip-node: %s\n") % hex(docket.tip_node)) | |||||
| ui.write((b"data-length: %d\n") % docket.data_length) | ui.write((b"data-length: %d\n") % docket.data_length) | ||||
| ui.write((b"data-unused: %d\n") % docket.data_unused) | ui.write((b"data-unused: %d\n") % docket.data_unused) | ||||
| @command( | @command( | ||||
| b'debugobsolete', | b'debugobsolete', | ||||
| [ | [ | ||||
| (b'', b'flags', 0, _(b'markers flag')), | (b'', b'flags', 0, _(b'markers flag')), | ||||
| use_nodemap = ( | use_nodemap = ( | ||||
| not self._inline | not self._inline | ||||
| and self.nodemap_file is not None | and self.nodemap_file is not None | ||||
| and util.safehasattr(index, 'update_nodemap_data') | and util.safehasattr(index, 'update_nodemap_data') | ||||
| ) | ) | ||||
| if use_nodemap: | if use_nodemap: | ||||
| nodemap_data = nodemaputil.persisted_data(self) | nodemap_data = nodemaputil.persisted_data(self) | ||||
| if nodemap_data is not None: | if nodemap_data is not None: | ||||
| self._nodemap_docket = nodemap_data[0] | docket = nodemap_data[0] | ||||
| if d[0][docket.tip_rev][7] == docket.tip_node: | |||||
| # no changelog tampering | |||||
| self._nodemap_docket = docket | |||||
| index.update_nodemap_data(*nodemap_data) | index.update_nodemap_data(*nodemap_data) | ||||
| except (ValueError, IndexError): | except (ValueError, IndexError): | ||||
| raise error.RevlogError( | raise error.RevlogError( | ||||
| _(b"index %s is corrupted") % self.indexfile | _(b"index %s is corrupted") % self.indexfile | ||||
| ) | ) | ||||
| self.index, self._chunkcache = d | self.index, self._chunkcache = d | ||||
| if not self._chunkcache: | if not self._chunkcache: | ||||
| self._chunkclear() | self._chunkclear() | ||||
| # revnum -> (chain-length, sum-delta-length) | # revnum -> (chain-length, sum-delta-length) | ||||
| if not pdata: | if not pdata: | ||||
| return None | return None | ||||
| offset = 0 | offset = 0 | ||||
| (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size]) | (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size]) | ||||
| if version != ONDISK_VERSION: | if version != ONDISK_VERSION: | ||||
| return None | return None | ||||
| offset += S_VERSION.size | offset += S_VERSION.size | ||||
| headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size]) | headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size]) | ||||
| uid_size, tip_rev, data_length, data_unused = headers | uid_size, tip_rev, data_length, data_unused, tip_node_size = headers | ||||
| offset += S_HEADER.size | offset += S_HEADER.size | ||||
| docket = NodeMapDocket(pdata[offset : offset + uid_size]) | docket = NodeMapDocket(pdata[offset : offset + uid_size]) | ||||
| offset += uid_size | |||||
| docket.tip_rev = tip_rev | docket.tip_rev = tip_rev | ||||
| docket.tip_node = pdata[offset : offset + tip_node_size] | |||||
| docket.data_length = data_length | docket.data_length = data_length | ||||
| docket.data_unused = data_unused | docket.data_unused = data_unused | ||||
| filename = _rawdata_filepath(revlog, docket) | filename = _rawdata_filepath(revlog, docket) | ||||
| use_mmap = revlog.opener.options.get("exp-persistent-nodemap.mmap") | use_mmap = revlog.opener.options.get("exp-persistent-nodemap.mmap") | ||||
| try: | try: | ||||
| with revlog.opener(filename) as fd: | with revlog.opener(filename) as fd: | ||||
| if use_mmap: | if use_mmap: | ||||
| if feed_data: | if feed_data: | ||||
| if use_mmap: | if use_mmap: | ||||
| new_data = data | new_data = data | ||||
| else: | else: | ||||
| fd.flush() | fd.flush() | ||||
| new_data = util.buffer(util.mmapread(fd, len(data))) | new_data = util.buffer(util.mmapread(fd, len(data))) | ||||
| target_docket.data_length = len(data) | target_docket.data_length = len(data) | ||||
| target_docket.tip_rev = revlog.tiprev() | target_docket.tip_rev = revlog.tiprev() | ||||
| target_docket.tip_node = revlog.node(target_docket.tip_rev) | |||||
| # EXP-TODO: if this is a cache, this should use a cache vfs, not a | # EXP-TODO: if this is a cache, this should use a cache vfs, not a | ||||
| # store vfs | # store vfs | ||||
| with revlog.opener(revlog.nodemap_file, b'w', atomictemp=True) as fp: | with revlog.opener(revlog.nodemap_file, b'w', atomictemp=True) as fp: | ||||
| fp.write(target_docket.serialize()) | fp.write(target_docket.serialize()) | ||||
| revlog._nodemap_docket = target_docket | revlog._nodemap_docket = target_docket | ||||
| if feed_data: | if feed_data: | ||||
| revlog.index.update_nodemap_data(target_docket, new_data) | revlog.index.update_nodemap_data(target_docket, new_data) | ||||
| # | # | ||||
| # The docket file contains information to find, qualify and validate the raw | # The docket file contains information to find, qualify and validate the raw | ||||
| # data. Its content is currently very light, but it will expand as the on disk | # data. Its content is currently very light, but it will expand as the on disk | ||||
| # nodemap gains the necessary features to be used in production. | # nodemap gains the necessary features to be used in production. | ||||
| # version 0 is experimental, no BC garantee, do no use outside of tests. | # version 0 is experimental, no BC garantee, do no use outside of tests. | ||||
| ONDISK_VERSION = 0 | ONDISK_VERSION = 0 | ||||
| S_VERSION = struct.Struct(">B") | S_VERSION = struct.Struct(">B") | ||||
| S_HEADER = struct.Struct(">BQQQ") | S_HEADER = struct.Struct(">BQQQQ") | ||||
| ID_SIZE = 8 | ID_SIZE = 8 | ||||
| def _make_uid(): | def _make_uid(): | ||||
| """return a new unique identifier. | """return a new unique identifier. | ||||
| The identifier is random and composed of ascii characters.""" | The identifier is random and composed of ascii characters.""" | ||||
| uid = _make_uid() | uid = _make_uid() | ||||
| # a unique identifier for the data file: | # a unique identifier for the data file: | ||||
| # - When new data are appended, it is preserved. | # - When new data are appended, it is preserved. | ||||
| # - When a new data file is created, a new identifier is generated. | # - When a new data file is created, a new identifier is generated. | ||||
| self.uid = uid | self.uid = uid | ||||
| # the tipmost revision stored in the data file. This revision and all | # the tipmost revision stored in the data file. This revision and all | ||||
| # revision before it are expected to be encoded in the data file. | # revision before it are expected to be encoded in the data file. | ||||
| self.tip_rev = None | self.tip_rev = None | ||||
| # the node of that tipmost revision, if it mismatch the current index | |||||
| # data the docket is not valid for the current index and should be | |||||
| # discarded. | |||||
| # | |||||
| # note: this method is not perfect as some destructive operation could | |||||
| # preserve the same tip_rev + tip_node while altering lower revision. | |||||
| # However this multiple other caches have the same vulnerability (eg: | |||||
| # brancmap cache). | |||||
| self.tip_node = None | |||||
| # the size (in bytes) of the persisted data to encode the nodemap valid | # the size (in bytes) of the persisted data to encode the nodemap valid | ||||
| # for `tip_rev`. | # for `tip_rev`. | ||||
| # - data file shorter than this are corrupted, | # - data file shorter than this are corrupted, | ||||
| # - any extra data should be ignored. | # - any extra data should be ignored. | ||||
| self.data_length = None | self.data_length = None | ||||
| # the amount (in bytes) of "dead" data, still in the data file but no | # the amount (in bytes) of "dead" data, still in the data file but no | ||||
| # longer used for the nodemap. | # longer used for the nodemap. | ||||
| self.data_unused = 0 | self.data_unused = 0 | ||||
| def copy(self): | def copy(self): | ||||
| new = NodeMapDocket(uid=self.uid) | new = NodeMapDocket(uid=self.uid) | ||||
| new.tip_rev = self.tip_rev | new.tip_rev = self.tip_rev | ||||
| new.tip_node = self.tip_node | |||||
| new.data_length = self.data_length | new.data_length = self.data_length | ||||
| new.data_unused = self.data_unused | new.data_unused = self.data_unused | ||||
| return new | return new | ||||
| def __cmp__(self, other): | def __cmp__(self, other): | ||||
| if self.uid < other.uid: | if self.uid < other.uid: | ||||
| return -1 | return -1 | ||||
| if self.uid > other.uid: | if self.uid > other.uid: | ||||
| """return serialized bytes for a docket using the passed uid""" | """return serialized bytes for a docket using the passed uid""" | ||||
| data = [] | data = [] | ||||
| data.append(S_VERSION.pack(ONDISK_VERSION)) | data.append(S_VERSION.pack(ONDISK_VERSION)) | ||||
| headers = ( | headers = ( | ||||
| len(self.uid), | len(self.uid), | ||||
| self.tip_rev, | self.tip_rev, | ||||
| self.data_length, | self.data_length, | ||||
| self.data_unused, | self.data_unused, | ||||
| len(self.tip_node), | |||||
| ) | ) | ||||
| data.append(S_HEADER.pack(*headers)) | data.append(S_HEADER.pack(*headers)) | ||||
| data.append(self.uid) | data.append(self.uid) | ||||
| data.append(self.tip_node) | |||||
| return b''.join(data) | return b''.join(data) | ||||
| def _rawdata_filepath(revlog, docket): | def _rawdata_filepath(revlog, docket): | ||||
| """The (vfs relative) nodemap's rawdata file for a given uid""" | """The (vfs relative) nodemap's rawdata file for a given uid""" | ||||
| prefix = revlog.nodemap_file[:-2] | prefix = revlog.nodemap_file[:-2] | ||||
| return b"%s-%s.nd" % (prefix, docket.uid) | return b"%s-%s.nd" % (prefix, docket.uid) | ||||
| =================================== | =================================== | ||||
| Test the persistent on-disk nodemap | Test the persistent on-disk nodemap | ||||
| =================================== | =================================== | ||||
| $ hg init test-repo | $ hg init test-repo | ||||
| $ cd test-repo | $ cd test-repo | ||||
| $ cat << EOF >> .hg/hgrc | $ cat << EOF >> .hg/hgrc | ||||
| > [experimental] | > [experimental] | ||||
| > exp-persistent-nodemap=yes | > exp-persistent-nodemap=yes | ||||
| > [devel] | > [devel] | ||||
| > persistent-nodemap=yes | > persistent-nodemap=yes | ||||
| > EOF | > EOF | ||||
| $ hg debugbuilddag .+5000 | $ hg debugbuilddag .+5000 | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5000 | tip-rev: 5000 | ||||
| tip-node: 06ddac466af534d365326c13c3879f97caca3cb1 | |||||
| data-length: 122880 | data-length: 122880 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| $ f --size .hg/store/00changelog.n | $ f --size .hg/store/00changelog.n | ||||
| .hg/store/00changelog.n: size=42 | .hg/store/00changelog.n: size=70 | ||||
| Simple lookup works | Simple lookup works | ||||
| $ ANYNODE=`hg log --template '{node|short}\n' --rev tip` | $ ANYNODE=`hg log --template '{node|short}\n' --rev tip` | ||||
| $ hg log -r "$ANYNODE" --template '{rev}\n' | $ hg log -r "$ANYNODE" --template '{rev}\n' | ||||
| 5000 | 5000 | ||||
| $ echo foo > foo | $ echo foo > foo | ||||
| $ hg add foo | $ hg add foo | ||||
| $ hg ci -m 'foo' | $ hg ci -m 'foo' | ||||
| #if no-pure no-rust | #if no-pure no-rust | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5001 | tip-rev: 5001 | ||||
| tip-node: 2dd9b5258caa46469ff07d4a3da1eb3529a51f49 | |||||
| data-length: 122880 | data-length: 122880 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| #else | #else | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5001 | tip-rev: 5001 | ||||
| tip-node: 2dd9b5258caa46469ff07d4a3da1eb3529a51f49 | |||||
| data-length: 123072 | data-length: 123072 | ||||
| data-unused: 192 | data-unused: 192 | ||||
| #endif | #endif | ||||
| $ f --size .hg/store/00changelog.n | $ f --size .hg/store/00changelog.n | ||||
| .hg/store/00changelog.n: size=42 | .hg/store/00changelog.n: size=70 | ||||
| (The pure code use the debug code that perform incremental update, the C code reencode from scratch) | (The pure code use the debug code that perform incremental update, the C code reencode from scratch) | ||||
| #if pure | #if pure | ||||
| $ f --sha256 .hg/store/00changelog-*.nd --size | $ f --sha256 .hg/store/00changelog-*.nd --size | ||||
| .hg/store/00changelog-????????????????.nd: size=123072, sha256=136472751566c8198ff09e306a7d2f9bd18bd32298d614752b73da4d6df23340 (glob) | .hg/store/00changelog-????????????????.nd: size=123072, sha256=136472751566c8198ff09e306a7d2f9bd18bd32298d614752b73da4d6df23340 (glob) | ||||
| #endif | #endif | ||||
| revision in index: 5003 | revision in index: 5003 | ||||
| revision in nodemap: 5003 | revision in nodemap: 5003 | ||||
| #if pure | #if pure | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 123328 | data-length: 123328 | ||||
| data-unused: 384 | data-unused: 384 | ||||
| $ f --sha256 .hg/store/00changelog-*.nd --size | $ f --sha256 .hg/store/00changelog-*.nd --size | ||||
| .hg/store/00changelog-????????????????.nd: size=123328, sha256=10d26e9776b6596af0f89143a54eba8cc581e929c38242a02a7b0760698c6c70 (glob) | .hg/store/00changelog-????????????????.nd: size=123328, sha256=10d26e9776b6596af0f89143a54eba8cc581e929c38242a02a7b0760698c6c70 (glob) | ||||
| #endif | #endif | ||||
| #if rust | #if rust | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 123328 | data-length: 123328 | ||||
| data-unused: 384 | data-unused: 384 | ||||
| $ f --sha256 .hg/store/00changelog-*.nd --size | $ f --sha256 .hg/store/00changelog-*.nd --size | ||||
| .hg/store/00changelog-????????????????.nd: size=123328, sha256=081eec9eb6708f2bf085d939b4c97bc0b6762bc8336bc4b93838f7fffa1516bf (glob) | .hg/store/00changelog-????????????????.nd: size=123328, sha256=081eec9eb6708f2bf085d939b4c97bc0b6762bc8336bc4b93838f7fffa1516bf (glob) | ||||
| #endif | #endif | ||||
| #if no-pure no-rust | #if no-pure no-rust | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 122944 | data-length: 122944 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| $ f --sha256 .hg/store/00changelog-*.nd --size | $ f --sha256 .hg/store/00changelog-*.nd --size | ||||
| .hg/store/00changelog-????????????????.nd: size=122944, sha256=755976b22b64ab680401b45395953504e64e7fa8c31ac570f58dee21e15f9bc0 (glob) | .hg/store/00changelog-????????????????.nd: size=122944, sha256=755976b22b64ab680401b45395953504e64e7fa8c31ac570f58dee21e15f9bc0 (glob) | ||||
| #endif | #endif | ||||
| Test force warming the cache | Test force warming the cache | ||||
| $ rm .hg/store/00changelog.n | $ rm .hg/store/00changelog.n | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| $ hg debugupdatecache | $ hg debugupdatecache | ||||
| #if pure | #if pure | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 122944 | data-length: 122944 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| #else | #else | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 122944 | data-length: 122944 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| #endif | #endif | ||||
| Check out of sync nodemap | Check out of sync nodemap | ||||
| ========================= | ========================= | ||||
| First copy old data on the side. | First copy old data on the side. | ||||
| $ hg log -r "$NODE" -T '{rev}\n' | $ hg log -r "$NODE" -T '{rev}\n' | ||||
| 5003 | 5003 | ||||
| If the nodemap is lagging behind, it can catch up fine | If the nodemap is lagging behind, it can catch up fine | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5003 | tip-rev: 5003 | ||||
| tip-node: 5c049e9c4a4af159bdcd65dce1b6bf303a0da6cf | |||||
| data-length: 123200 (pure !) | data-length: 123200 (pure !) | ||||
| data-length: 123200 (rust !) | data-length: 123200 (rust !) | ||||
| data-length: 122944 (no-rust no-pure !) | data-length: 122944 (no-rust no-pure !) | ||||
| data-unused: 256 (pure !) | data-unused: 256 (pure !) | ||||
| data-unused: 256 (rust !) | data-unused: 256 (rust !) | ||||
| data-unused: 0 (no-rust no-pure !) | data-unused: 0 (no-rust no-pure !) | ||||
| $ cp -f ../tmp-copies/* .hg/store/ | $ cp -f ../tmp-copies/* .hg/store/ | ||||
| $ hg debugnodemap --metadata | $ hg debugnodemap --metadata | ||||
| uid: ???????????????? (glob) | uid: ???????????????? (glob) | ||||
| tip-rev: 5002 | tip-rev: 5002 | ||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 122944 | data-length: 122944 | ||||
| data-unused: 0 | data-unused: 0 | ||||
| $ hg log -r "$NODE" -T '{rev}\n' | $ hg log -r "$NODE" -T '{rev}\n' | ||||
| 5003 | 5003 | ||||
| changelog altered | |||||
| ----------------- | |||||
| If the nodemap is not gated behind a requirements, an unaware client can alter | |||||
| the repository so the revlog used to generate the nodemap is not longer | |||||
| compatible with the persistent nodemap. We need to detect that. | |||||
| $ hg up "$NODE~5" | |||||
| 0 files updated, 0 files merged, 2 files removed, 0 files unresolved | |||||
| $ echo bar > babar | |||||
| $ hg add babar | |||||
| $ hg ci -m 'babar' | |||||
| created new head | |||||
| $ OTHERNODE=`hg log -r tip -T '{node}\n'` | |||||
| $ hg log -r "$OTHERNODE" -T '{rev}\n' | |||||
| 5004 | |||||
| $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup | |||||
| the nodemap should detect the changelog have been tampered with and recover. | |||||
| $ hg debugnodemap --metadata | |||||
| uid: ???????????????? (glob) | |||||
| tip-rev: 5002 | |||||
| tip-node: 42bf3068c7ddfdfded53c4eb11d02266faeebfee | |||||
| data-length: 123456 (pure !) | |||||
| data-length: 246464 (rust !) | |||||
| data-length: 123008 (no-pure no-rust !) | |||||
| data-unused: 448 (pure !) | |||||
| data-unused: 123904 (rust !) | |||||
| data-unused: 0 (no-pure no-rust !) | |||||
| $ cp -f ../tmp-copies/* .hg/store/ | |||||
| $ hg debugnodemap --metadata | |||||
| uid: ???????????????? (glob) | |||||
| tip-rev: 5002 | |||||
| tip-node: 6ce944fafcee85af91f29ea5b51654cc6101ad7e | |||||
| data-length: 122944 | |||||
| data-unused: 0 | |||||
| $ hg log -r "$OTHERNODE" -T '{rev}\n' | |||||
| 5002 | |||||