Mercurial > public > mercurial-scm > hg
view rust/hg-core/src/narrow.rs @ 53042:cdd7bf612c7b stable tip
bundle-spec: properly format boolean parameter (issue6960)
This was breaking automatic clone bundle generation. This changeset fixes it and
add a test to catch it in the future.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Tue, 11 Mar 2025 02:29:42 +0100 |
parents | b019b5798e8f |
children |
line wrap: on
line source
use std::path::Path; use crate::{ errors::HgError, exit_codes, filepatterns::parse_pattern_file_contents, matchers::{ AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher, NeverMatcher, }, repo::Repo, requirements::NARROW_REQUIREMENT, sparse::{self, SparseConfigError, SparseWarning}, }; /// The file in .hg/store/ that indicates which paths exit in the store const FILENAME: &str = "narrowspec"; /// The file in .hg/ that indicates which paths exit in the dirstate const DIRSTATE_FILENAME: &str = "narrowspec.dirstate"; /// Pattern prefixes that are allowed in narrow patterns. This list MUST /// only contain patterns that are fast and safe to evaluate. Keep in mind /// that patterns are supplied by clients and executed on remote servers /// as part of wire protocol commands. That means that changes to this /// data structure influence the wire protocol and should not be taken /// lightly - especially removals. pub const VALID_PREFIXES: [&str; 2] = ["path:", "rootfilesin:"]; /// Return the matcher for the current narrow spec, and all configuration /// warnings to display. pub fn matcher( repo: &Repo, ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> { let mut warnings = vec![]; if !repo.requirements().contains(NARROW_REQUIREMENT) { return Ok((Box::new(AlwaysMatcher), warnings)); } // Treat "narrowspec does not exist" the same as "narrowspec file exists // and is empty". let store_spec = repo.store_vfs().try_read(FILENAME)?.unwrap_or_default(); let working_copy_spec = repo .hg_vfs() .try_read(DIRSTATE_FILENAME)? .unwrap_or_default(); if store_spec != working_copy_spec { return Err(HgError::abort( "abort: working copy's narrowspec is stale", exit_codes::STATE_ERROR, Some("run 'hg tracked --update-working-copy'".into()), ) .into()); } let config = sparse::parse_config( &store_spec, sparse::SparseConfigContext::Narrow, )?; warnings.extend(config.warnings); if !config.profiles.is_empty() { // TODO (from Python impl) maybe do something with profiles? return Err(SparseConfigError::IncludesInNarrow); } validate_patterns(&config.includes)?; validate_patterns(&config.excludes)?; if config.includes.is_empty() { return Ok((Box::new(NeverMatcher), warnings)); } let (patterns, subwarnings) = parse_pattern_file_contents( &config.includes, Path::new(""), None, false, true, )?; warnings.extend(subwarnings.into_iter().map(From::from)); let mut m: Box<dyn Matcher + Sync> = Box::new(IncludeMatcher::new(patterns)?); let (patterns, subwarnings) = parse_pattern_file_contents( &config.excludes, Path::new(""), None, false, true, )?; if !patterns.is_empty() { warnings.extend(subwarnings.into_iter().map(From::from)); let exclude_matcher = Box::new(IncludeMatcher::new(patterns)?); m = Box::new(DifferenceMatcher::new(m, exclude_matcher)); } Ok((m, warnings)) } fn is_whitespace(b: &u8) -> bool { // should match what .strip() in Python does b.is_ascii_whitespace() || *b == 0x0b } fn starts_or_ends_with_whitespace(s: &[u8]) -> bool { let w = |b: Option<&u8>| b.map(is_whitespace).unwrap_or(false); w(s.first()) || w(s.last()) } fn validate_pattern(pattern: &[u8]) -> Result<(), SparseConfigError> { if starts_or_ends_with_whitespace(pattern) { return Err(SparseConfigError::WhitespaceAtEdgeOfPattern( pattern.to_owned(), )); } for prefix in VALID_PREFIXES.iter() { if pattern.starts_with(prefix.as_bytes()) { return Ok(()); } } Err(SparseConfigError::InvalidNarrowPrefix(pattern.to_owned())) } fn validate_patterns(patterns: &[u8]) -> Result<(), SparseConfigError> { for pattern in patterns.split(|c| *c == b'\n') { if pattern.is_empty() { // TODO: probably not intentionally allowed (only because `split` // produces "fake" empty line at the end) continue; } validate_pattern(pattern)? } Ok(()) }