# HG changeset patch # User Raphaël Gomès # Date 1657526080 -7200 # Node ID 259df3e3152c1fbb4d95c2fab682ca93581cf9ca # Parent 79b2c98ab7b4895a07b0ad3659c0b9d126f04e93# Parent 55adff8105464f6247983940ba109684d36b689d branching: merge stable into default diff -r 79b2c98ab7b4 -r 259df3e3152c .hgsigs --- a/.hgsigs Thu Jun 16 15:20:48 2022 +0200 +++ b/.hgsigs Mon Jul 11 09:54:40 2022 +0200 @@ -229,3 +229,5 @@ 0ddd5e1f5f67438af85d12e4ce6c39021dde9916 0 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 diff -r 79b2c98ab7b4 -r 259df3e3152c .hgtags --- a/.hgtags Thu Jun 16 15:20:48 2022 +0200 +++ b/.hgtags Mon Jul 11 09:54:40 2022 +0200 @@ -242,3 +242,5 @@ 0ddd5e1f5f67438af85d12e4ce6c39021dde9916 6.1.2 6b10151b962108f65bfa12b3918b1021ca334f73 6.1.3 0cc5f74ff7f0f4ac2427096bddbe102dbc2453ae 6.1.4 +288de6f5d724bba7bf1669e2838f196962bb7528 6.2rc0 +094a5fa3cf52f936e0de3f1e507c818bee5ece6b 6.2 diff -r 79b2c98ab7b4 -r 259df3e3152c contrib/import-checker.py --- a/contrib/import-checker.py Thu Jun 16 15:20:48 2022 +0200 +++ b/contrib/import-checker.py Mon Jul 11 09:54:40 2022 +0200 @@ -47,6 +47,7 @@ 'mercurial.thirdparty.zope', 'mercurial.thirdparty.zope.interface', 'typing', + 'xml.etree.ElementTree', ) # Allow list of symbols that can be directly imported. diff -r 79b2c98ab7b4 -r 259df3e3152c hgext/convert/darcs.py --- a/hgext/convert/darcs.py Thu Jun 16 15:20:48 2022 +0200 +++ b/hgext/convert/darcs.py Mon Jul 11 09:54:40 2022 +0200 @@ -8,6 +8,10 @@ import os import re import shutil +from xml.etree.ElementTree import ( + ElementTree, + XMLParser, +) from mercurial.i18n import _ from mercurial import ( @@ -20,26 +24,6 @@ NoRepo = common.NoRepo -# The naming drift of ElementTree is fun! - -try: - import xml.etree.cElementTree.ElementTree as ElementTree - import xml.etree.cElementTree.XMLParser as XMLParser -except ImportError: - try: - import xml.etree.ElementTree.ElementTree as ElementTree - import xml.etree.ElementTree.XMLParser as XMLParser - except ImportError: - try: - import elementtree.cElementTree.ElementTree as ElementTree - import elementtree.cElementTree.XMLParser as XMLParser - except ImportError: - try: - import elementtree.ElementTree.ElementTree as ElementTree - import elementtree.ElementTree.XMLParser as XMLParser - except ImportError: - pass - class darcs_source(common.converter_source, common.commandline): def __init__(self, ui, repotype, path, revs=None): @@ -58,7 +42,7 @@ _(b'darcs version 2.1 or newer needed (found %r)') % version ) - if b"ElementTree" not in globals(): + if "ElementTree" not in globals(): raise error.Abort(_(b"Python ElementTree module is not available")) self.path = os.path.realpath(path) @@ -94,9 +78,9 @@ ) tagname = None child = None - for elt in tree.findall(b'patch'): - node = elt.get(b'hash') - name = elt.findtext(b'name', b'') + for elt in tree.findall('patch'): + node = self.recode(elt.get('hash')) + name = self.recode(elt.findtext('name', '')) if name.startswith(b'TAG '): tagname = name[4:].strip() elif tagname is not None: @@ -126,7 +110,7 @@ # While we are decoding the XML as latin-1 to be as liberal as # possible, etree will still raise an exception if any # non-printable characters are in the XML changelog. - parser = XMLParser(encoding=b'latin-1') + parser = XMLParser(encoding='latin-1') p = self._run(cmd, **kwargs) etree.parse(p.stdout, parser=parser) p.wait() @@ -136,7 +120,7 @@ def format(self): output, status = self.run(b'show', b'repo', repodir=self.path) self.checkexit(status) - m = re.search(r'^\s*Format:\s*(.*)$', output, re.MULTILINE) + m = re.search(br'^\s*Format:\s*(.*)$', output, re.MULTILINE) if not m: return None return b','.join(sorted(f.strip() for f in m.group(1).split(b','))) @@ -159,13 +143,13 @@ def getcommit(self, rev): elt = self.changes[rev] dateformat = b'%a %b %d %H:%M:%S %Z %Y' - date = dateutil.strdate(elt.get(b'local_date'), dateformat) - desc = elt.findtext(b'name') + b'\n' + elt.findtext(b'comment', b'') + date = dateutil.strdate(elt.get('local_date'), dateformat) + desc = elt.findtext('name') + '\n' + elt.findtext('comment', '') # etree can return unicode objects for name, comment, and author, # so recode() is used to ensure str objects are emitted. newdateformat = b'%Y-%m-%d %H:%M:%S %1%2' return common.commit( - author=self.recode(elt.get(b'author')), + author=self.recode(elt.get('author')), date=dateutil.datestr(date, newdateformat), desc=self.recode(desc).strip(), parents=self.parents[rev], @@ -176,7 +160,7 @@ b'pull', self.path, all=True, - match=b'hash %s' % rev, + match=b'hash %s' % self.recode(rev), no_test=True, no_posthook=True, external_merge=b'/bin/false', @@ -194,13 +178,14 @@ copies = {} changes = [] man = None - for elt in self.changes[rev].find(b'summary').getchildren(): - if elt.tag in (b'add_directory', b'remove_directory'): + for elt in self.changes[rev].find('summary'): + if elt.tag in ('add_directory', 'remove_directory'): continue - if elt.tag == b'move': + if elt.tag == 'move': if man is None: man = self.manifest() - source, dest = elt.get(b'from'), elt.get(b'to') + source = self.recode(elt.get('from')) + dest = self.recode(elt.get('to')) if source in man: # File move changes.append((source, rev)) @@ -217,7 +202,7 @@ changes.append((fdest, rev)) copies[fdest] = f else: - changes.append((elt.text.strip(), rev)) + changes.append((self.recode(elt.text.strip()), rev)) self.pull(rev) self.lastrev = rev return sorted(changes), copies, set() diff -r 79b2c98ab7b4 -r 259df3e3152c mercurial/upgrade_utils/actions.py --- a/mercurial/upgrade_utils/actions.py Thu Jun 16 15:20:48 2022 +0200 +++ b/mercurial/upgrade_utils/actions.py Mon Jul 11 09:54:40 2022 +0200 @@ -683,7 +683,11 @@ newactions.append(d) - newactions.extend(o for o in sorted(optimizations) if o not in newactions) + newactions.extend( + o + for o in sorted(optimizations, key=(lambda x: x.name)) + if o not in newactions + ) # FUTURE consider adding some optimizations here for certain transitions. # e.g. adding generaldelta could schedule parent redeltas. diff -r 79b2c98ab7b4 -r 259df3e3152c mercurial/utils/procutil.py --- a/mercurial/utils/procutil.py Thu Jun 16 15:20:48 2022 +0200 +++ b/mercurial/utils/procutil.py Mon Jul 11 09:54:40 2022 +0200 @@ -80,12 +80,32 @@ def make_line_buffered(stream): - if not isinstance(stream, io.BufferedIOBase): - # On Python 3, buffered streams can be expected to subclass - # BufferedIOBase. This is definitively the case for the streams - # initialized by the interpreter. For unbuffered streams, we don't need - # to emulate line buffering. + # First, check if we need to wrap the stream. + check_stream = stream + while True: + if isinstance(check_stream, WriteAllWrapper): + check_stream = check_stream.orig + elif pycompat.iswindows and isinstance( + check_stream, + # pytype: disable=module-attr + platform.winstdout + # pytype: enable=module-attr + ): + check_stream = check_stream.fp + else: + break + if isinstance(check_stream, io.RawIOBase): + # The stream is unbuffered, we don't need to emulate line buffering. return stream + elif isinstance(check_stream, io.BufferedIOBase): + # The stream supports some kind of buffering. We can't assume that + # lines are flushed. Fall back to wrapping the stream. + pass + else: + raise NotImplementedError( + "can't determine whether stream is buffered or not" + ) + if isinstance(stream, LineBufferedWrapper): return stream return LineBufferedWrapper(stream) diff -r 79b2c98ab7b4 -r 259df3e3152c relnotes/6.2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/relnotes/6.2 Mon Jul 11 09:54:40 2022 +0200 @@ -0,0 +1,69 @@ += Mercurial 6.2rc0 = + +'''This is the first release to support Python 3.6+ ''only''''' + +== New Features == + * Introduce a way to auto-upgrade a repo for certain requirements (see `hg help config.format`) + * filemerge: add support for partial conflict resolution by external tool + * contrib: add a partial-merge tool for sorted lists (such as Python imports) + * revlog: reorder p1 and p2 when p1 is null and p2 is not while respecting issue6528 + * rhg: add support for ignoring all extensions + * completion: install completers to conventional locations + * revert: ask user to confirm before tracking new file when interactive + * Rust implementation now uses the new dirstate API + * sslutil: be less strict about which ciphers are allowed when using --insecure + * sslutil: support TLSV1_ALERT_PROTOCOL_VERSION reason code + * absorb: make `--edit-lines` imply `--apply-changes` + * diff: add help text to highlight the ability to do merge diffs + * censor: make rhg fall back to python when encountering a censored node + * clone: use better names for temp files + * debuglock: make the command more useful in non-interactive mode + * debugdeltachain: distinct between snapshot and other diffs + * debugindex: rename to debugindex debug-revlog-index + * Make debug-revlog-index give out more information + * sparse: use the rust code even when sparse is present + +== Bug Fixes == + * Python 3 bugfixes + * Windows bugfixes + * templates: make `firstline` filter not keep '\v', '\f' and similar + * rhg: sort unsupported extensions in error message + * Improve performance of all functions that extract the first line of a text + * crecord: avoid duplicating lines when reverting noeol->eol change + * Some config.path options are now discoverable via config + * mail: don't complain about a multi-word email.method + * bundlespec: do not overwrite bundlespec value with the config one + * bundlespec: do not check for `-` in the params portion of the bundlespec + * bundlespec: handle the presence of obsmarker part + * sparse: start moving away from the global variable for detection of usage + * rust-changelog: don't skip empty lines when iterating over changeset lines + * narrow: support debugupgraderepo + * bundle: quick fix to ludicrous performance penalty + * followlines: don't put Unicode directly into the .js file (issue6559) + * manifest: improve error message in case for tree manifest + * revlog: use %d to format int instead of %lu (issue6565) + * revlog: use appropriate format char for int ("i" instead of I") + * worker: stop relying on garbage collection to release memoryview + * worker: implement _blockingreader.readinto() (issue6444) + * worker: avoid potential partial write of pickled data + +== Backwards Compatibility Changes == + * '''Removed Python 2 support''': this includes a lot of cleanup in our codebase, automation, testing, etc. + * debugindex: rename to debugindex debug-revlog-index + +== Miscellaneous == + + * Fix typos and add missing items from documentation + * dirstate-tree: optimize HashMap lookups with raw_entry_mut + * Rust dependencies have been upgraded + * revlog: rank computation is done by Rust when available + * Improve discovery test tooling + * Audit the number of queries done in discovery + * Improved .hgignore of the mercurial-devel repository itself + * Improve test coverage of dirstate-v2 + * rust-requirements: allow loading repos with `bookmarksinstore` requirement + * Various Rust refactorings to help with revlog management + * Improve debugability of Rust structs + * Improve unit testing of the Rust dirstatemap + * Improve robustness of the Rust dirstatemap to corruption + * Improve changelog-v2 upgrade system diff -r 79b2c98ab7b4 -r 259df3e3152c rust/hg-core/src/dirstate/entry.rs --- a/rust/hg-core/src/dirstate/entry.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/hg-core/src/dirstate/entry.rs Mon Jul 11 09:54:40 2022 +0200 @@ -83,7 +83,7 @@ second_ambiguous, }) } else { - Err(DirstateV2ParseError) + Err(DirstateV2ParseError::new("when reading datetime")) } } diff -r 79b2c98ab7b4 -r 259df3e3152c rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs Mon Jul 11 09:54:40 2022 +0200 @@ -463,7 +463,7 @@ if let Some(data) = on_disk.get(..data_size) { Ok(on_disk::read(data, metadata)?) } else { - Err(DirstateV2ParseError.into()) + Err(DirstateV2ParseError::new("not enough bytes on disk").into()) } } diff -r 79b2c98ab7b4 -r 259df3e3152c rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs Mon Jul 11 09:54:40 2022 +0200 @@ -175,11 +175,21 @@ /// /// This should only happen if Mercurial is buggy or a repository is corrupted. #[derive(Debug)] -pub struct DirstateV2ParseError; +pub struct DirstateV2ParseError { + message: String, +} + +impl DirstateV2ParseError { + pub fn new>(message: S) -> Self { + Self { + message: message.into(), + } + } +} impl From for HgError { - fn from(_: DirstateV2ParseError) -> Self { - HgError::corrupted("dirstate-v2 parse error") + fn from(e: DirstateV2ParseError) -> Self { + HgError::corrupted(format!("dirstate-v2 parse error: {}", e.message)) } } @@ -262,13 +272,16 @@ pub fn read_docket( on_disk: &[u8], ) -> Result, DirstateV2ParseError> { - let (header, uuid) = - DocketHeader::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; + let (header, uuid) = DocketHeader::from_bytes(on_disk).map_err(|e| { + DirstateV2ParseError::new(format!("when reading docket, {}", e)) + })?; let uuid_size = header.uuid_size as usize; if header.marker == *V2_FORMAT_MARKER && uuid.len() == uuid_size { Ok(Docket { header, uuid }) } else { - Err(DirstateV2ParseError) + Err(DirstateV2ParseError::new( + "invalid format marker or uuid size", + )) } } @@ -281,14 +294,17 @@ map.dirstate_version = DirstateVersion::V2; return Ok(map); } - let (meta, _) = TreeMetadata::from_bytes(metadata) - .map_err(|_| DirstateV2ParseError)?; + let (meta, _) = TreeMetadata::from_bytes(metadata).map_err(|e| { + DirstateV2ParseError::new(format!("when parsing tree metadata, {}", e)) + })?; let dirstate_map = DirstateMap { on_disk, - root: dirstate_map::ChildNodes::OnDisk(read_nodes( - on_disk, - meta.root_nodes, - )?), + root: dirstate_map::ChildNodes::OnDisk( + read_nodes(on_disk, meta.root_nodes).map_err(|mut e| { + e.message = format!("{}, when reading root notes", e.message); + e + })?, + ), nodes_with_entry_count: meta.nodes_with_entry_count.get(), nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(), ignore_patterns_hash: meta.ignore_patterns_hash, @@ -317,7 +333,7 @@ .expect("dirstate-v2 base_name_start out of bounds"); Ok(start) } else { - Err(DirstateV2ParseError) + Err(DirstateV2ParseError::new("not enough bytes for base name")) } } @@ -571,11 +587,19 @@ // `&[u8]` cannot occupy the entire addess space. let start = start.get().try_into().unwrap_or(std::usize::MAX); let len = len.try_into().unwrap_or(std::usize::MAX); - on_disk - .get(start..) - .and_then(|bytes| T::slice_from_bytes(bytes, len).ok()) + let bytes = match on_disk.get(start..) { + Some(bytes) => bytes, + None => { + return Err(DirstateV2ParseError::new( + "not enough bytes from disk", + )) + } + }; + T::slice_from_bytes(bytes, len) + .map_err(|e| { + DirstateV2ParseError::new(format!("when reading a slice, {}", e)) + }) .map(|(slice, _rest)| slice) - .ok_or_else(|| DirstateV2ParseError) } pub(crate) fn for_each_tracked_path<'on_disk>( @@ -583,8 +607,9 @@ metadata: &[u8], mut f: impl FnMut(&'on_disk HgPath), ) -> Result<(), DirstateV2ParseError> { - let (meta, _) = TreeMetadata::from_bytes(metadata) - .map_err(|_| DirstateV2ParseError)?; + let (meta, _) = TreeMetadata::from_bytes(metadata).map_err(|e| { + DirstateV2ParseError::new(format!("when parsing tree metadata, {}", e)) + })?; fn recur<'on_disk>( on_disk: &'on_disk [u8], nodes: ChildNodes, diff -r 79b2c98ab7b4 -r 259df3e3152c rust/hg-core/src/revlog/revlog.rs --- a/rust/hg-core/src/revlog/revlog.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/hg-core/src/revlog/revlog.rs Mon Jul 11 09:54:40 2022 +0200 @@ -5,7 +5,6 @@ use std::path::Path; use flate2::read::ZlibDecoder; -use micro_timer::timed; use sha1::{Digest, Sha1}; use zstd; @@ -49,18 +48,20 @@ fn from(error: NodeMapError) -> Self { match error { NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix, - NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(), + NodeMapError::RevisionNotInIndex(rev) => RevlogError::corrupted( + format!("nodemap point to revision {} not in index", rev), + ), } } } -fn corrupted() -> HgError { - HgError::corrupted("corrupted revlog") +fn corrupted>(context: S) -> HgError { + HgError::corrupted(format!("corrupted revlog, {}", context.as_ref())) } impl RevlogError { - fn corrupted() -> Self { - RevlogError::Other(corrupted()) + fn corrupted>(context: S) -> Self { + RevlogError::Other(corrupted(context)) } } @@ -81,7 +82,6 @@ /// /// It will also open the associated data file if index and data are not /// interleaved. - #[timed] pub fn open( store_vfs: &Vfs, index_path: impl AsRef, @@ -155,7 +155,6 @@ /// Return the revision number for the given node ID, if it exists in this /// revlog - #[timed] pub fn rev_from_node( &self, node: NodePrefix, @@ -205,7 +204,6 @@ /// All entries required to build the final data out of deltas will be /// retrieved as needed, and the deltas will be applied to the inital /// snapshot to rebuild the final data. - #[timed] pub fn get_rev_data( &self, rev: Revision, @@ -240,7 +238,6 @@ /// Build the full data of a revision out its snapshot /// and its deltas. - #[timed] fn build_data_from_deltas( snapshot: RevlogEntry, deltas: &[RevlogEntry], @@ -329,7 +326,8 @@ &self, rev: Revision, ) -> Result { - return self.get_entry(rev).map_err(|_| corrupted()); + self.get_entry(rev) + .map_err(|_| corrupted(format!("revision {} out of range", rev))) } } @@ -449,7 +447,10 @@ ) { Ok(data) } else { - Err(corrupted()) + Err(corrupted(format!( + "hash check failed for revision {}", + self.rev + ))) } } @@ -478,7 +479,10 @@ // zstd data. b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)), // A proper new format should have had a repo/store requirement. - _format_type => Err(corrupted()), + format_type => Err(corrupted(format!( + "unknown compression header '{}'", + format_type + ))), } } @@ -486,12 +490,16 @@ let mut decoder = ZlibDecoder::new(self.bytes); if self.is_delta() { let mut buf = Vec::with_capacity(self.compressed_len as usize); - decoder.read_to_end(&mut buf).map_err(|_| corrupted())?; + decoder + .read_to_end(&mut buf) + .map_err(|e| corrupted(e.to_string()))?; Ok(buf) } else { let cap = self.uncompressed_len.max(0) as usize; let mut buf = vec![0; cap]; - decoder.read_exact(&mut buf).map_err(|_| corrupted())?; + decoder + .read_exact(&mut buf) + .map_err(|e| corrupted(e.to_string()))?; Ok(buf) } } @@ -500,15 +508,15 @@ if self.is_delta() { let mut buf = Vec::with_capacity(self.compressed_len as usize); zstd::stream::copy_decode(self.bytes, &mut buf) - .map_err(|_| corrupted())?; + .map_err(|e| corrupted(e.to_string()))?; Ok(buf) } else { let cap = self.uncompressed_len.max(0) as usize; let mut buf = vec![0; cap]; let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf) - .map_err(|_| corrupted())?; + .map_err(|e| corrupted(e.to_string()))?; if len != self.uncompressed_len as usize { - Err(corrupted()) + Err(corrupted("uncompressed length does not match")) } else { Ok(buf) } diff -r 79b2c98ab7b4 -r 259df3e3152c rust/hg-cpython/src/revlog.rs --- a/rust/hg-cpython/src/revlog.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/hg-cpython/src/revlog.rs Mon Jul 11 09:54:40 2022 +0200 @@ -107,7 +107,10 @@ String::from_utf8_lossy(node.data(py)).to_string() }; - let prefix = NodePrefix::from_hex(&node_as_string).map_err(|_| PyErr::new::(py, "Invalid node or prefix"))?; + let prefix = NodePrefix::from_hex(&node_as_string) + .map_err(|_| PyErr::new::( + py, format!("Invalid node or prefix '{}'", node_as_string)) + )?; nt.find_bin(idx, prefix) // TODO make an inner API returning the node directly diff -r 79b2c98ab7b4 -r 259df3e3152c rust/rhg/src/commands/cat.rs --- a/rust/rhg/src/commands/cat.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/rhg/src/commands/cat.rs Mon Jul 11 09:54:40 2022 +0200 @@ -67,10 +67,19 @@ let message = "`..` or `.` path segment"; return Err(CommandError::unsupported(message)); } + let relative_path = working_directory + .strip_prefix(&cwd) + .unwrap_or(&working_directory); let stripped = normalized .strip_prefix(&working_directory) - // TODO: error message for path arguments outside of the repo - .map_err(|_| CommandError::abort(""))?; + .map_err(|_| { + CommandError::abort(format!( + "abort: {} not under root '{}'\n(consider using '--cwd {}')", + file, + working_directory.display(), + relative_path.display(), + )) + })?; let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) .map_err(|e| CommandError::abort(e.to_string()))?; files.push(hg_file); diff -r 79b2c98ab7b4 -r 259df3e3152c rust/rhg/src/commands/status.rs --- a/rust/rhg/src/commands/status.rs Thu Jun 16 15:20:48 2022 +0200 +++ b/rust/rhg/src/commands/status.rs Mon Jul 11 09:54:40 2022 +0200 @@ -517,10 +517,13 @@ } let filelog = repo.filelog(hg_path)?; let fs_len = fs_metadata.len(); - let filelog_entry = - filelog.entry_for_node(entry.node_id()?).map_err(|_| { - HgError::corrupted("filelog missing node from manifest") - })?; + let file_node = entry.node_id()?; + let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| { + HgError::corrupted(format!( + "filelog missing node {:?} from manifest", + file_node + )) + })?; if filelog_entry.file_data_len_not_equal_to(fs_len) { // No need to read file contents: // it cannot be equal if it has a different length. diff -r 79b2c98ab7b4 -r 259df3e3152c tests/test-upgrade-repo.t --- a/tests/test-upgrade-repo.t Thu Jun 16 15:20:48 2022 +0200 +++ b/tests/test-upgrade-repo.t Mon Jul 11 09:54:40 2022 +0200 @@ -467,6 +467,7 @@ re-delta-fulladd every revision will be re-added as if it was new content. It will go through the full storage mechanism giving extensions a chance to process it (eg. lfs). This is similar to "re-delta-all" but even slower since more logic is involved. + $ hg debugupgrade --optimize re-delta-parent --quiet requirements preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-rust !) @@ -480,6 +481,20 @@ - manifest +passing multiple optimization: + + $ hg debugupgrade --optimize re-delta-parent --optimize re-delta-multibase --quiet + requirements + preserved: * (glob) + + optimisations: re-delta-multibase, re-delta-parent + + processed revlogs: + - all-filelogs + - changelog + - manifest + + unknown optimization: $ hg debugupgrade --optimize foobar