# HG changeset patch # User Simon Sapin # Date 1615585120 -3600 # Node ID 1a036d33bc1896d86c6bdb2b3bd832fff3cfd8bf # Parent 1bac7764ceef562b66801c0d5a19d3b0f50cb940 rhg: Add an allow-list of ignored extensions Because rhg doesn’t know how a Python extension would affect behavior it implements in Rust, when an unsupported extension is enabled it conservatively falls back to Python-based hg. However many users will have unsupported extensions enabled in practice. Maybe they don’t actually affect rhg behavior, but we don’t know. This adds a `rhg.ignored-extensions` configuration that lets users list extensions that rhg can safely ignore and proceed even if they’re not supported in Rust. Differential Revision: https://phab.mercurial-scm.org/D10188 diff -r 1bac7764ceef -r 1a036d33bc18 rust/hg-core/src/config/config.rs --- a/rust/hg-core/src/config/config.rs Thu Mar 04 10:58:43 2021 +0100 +++ b/rust/hg-core/src/config/config.rs Fri Mar 12 22:38:40 2021 +0100 @@ -13,6 +13,7 @@ ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, }; use crate::utils::files::get_bytes_from_os_str; +use crate::utils::SliceExt; use format_bytes::{write_bytes, DisplayBytes}; use std::collections::HashSet; use std::env; @@ -339,6 +340,31 @@ Ok(self.get_option(section, item)?.unwrap_or(false)) } + /// Returns the corresponding list-value in the config if found, or `None`. + /// + /// This is appropriate for new configuration keys. The value syntax is + /// **not** the same as most existing list-valued config, which has Python + /// parsing implemented in `parselist()` in `mercurial/config.py`. + /// Faithfully porting that parsing algorithm to Rust (including behavior + /// that are arguably bugs) turned out to be non-trivial and hasn’t been + /// completed as of this writing. + /// + /// Instead, the "simple" syntax is: split on comma, then trim leading and + /// trailing whitespace of each component. Quotes or backslashes are not + /// interpreted in any way. Commas are mandatory between values. Values + /// that contain a comma are not supported. + pub fn get_simple_list( + &self, + section: &[u8], + item: &[u8], + ) -> Option> { + self.get(section, item).map(|value| { + value + .split(|&byte| byte == b',') + .map(|component| component.trim()) + }) + } + /// Returns the raw value bytes of the first one found, or `None`. pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> { self.get_inner(section, item) diff -r 1bac7764ceef -r 1a036d33bc18 rust/rhg/src/main.rs --- a/rust/rhg/src/main.rs Thu Mar 04 10:58:43 2021 +0100 +++ b/rust/rhg/src/main.rs Fri Mar 12 22:38:40 2021 +0100 @@ -365,12 +365,20 @@ unsupported.remove(supported); } + if let Some(ignored_list) = + config.get_simple_list(b"rhg", b"ignored-extensions") + { + for ignored in ignored_list { + unsupported.remove(ignored); + } + } + if unsupported.is_empty() { Ok(()) } else { Err(CommandError::UnsupportedFeature { message: format_bytes!( - b"extensions: {}", + b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)", join(unsupported, b", ") ), })