# HG changeset patch # User Spencer Baugh # Date 1690987607 14400 # Node ID c112cc9effdc8200f60fd209365e56ef32176eda # Parent f874342fa5683c3d0efde65b0ce3d0685456b55d rhg: support "status FILE" This change adds a new [file] argument to rhg status, parses them as patterns, canonicalizes the paths, and constructs a new PatternMatcher to intersect with the existing matcher being passed to the status implementation. We also make filepatterns a public module so we can access the pattern-parsing functionality we need from commands/status.rs. diff -r f874342fa568 -r c112cc9effdc rust/hg-core/src/filepatterns.rs --- a/rust/hg-core/src/filepatterns.rs Wed Aug 02 10:16:33 2023 -0400 +++ b/rust/hg-core/src/filepatterns.rs Wed Aug 02 10:46:47 2023 -0400 @@ -366,6 +366,7 @@ pattern: &[u8], source: &Path, default: PatternSyntax, + normalize: bool, ) -> IgnorePattern { let mut pattern_bytes: &[u8] = pattern; let mut syntax = default; @@ -378,7 +379,19 @@ } } - let pattern = pattern_bytes.to_vec(); + let pattern = match syntax { + PatternSyntax::RootGlob + | PatternSyntax::Path + | PatternSyntax::Glob + | PatternSyntax::RelGlob + | PatternSyntax::RelPath + | PatternSyntax::RootFiles + if normalize => + { + normalize_path_bytes(pattern_bytes) + } + _ => pattern_bytes.to_vec(), + }; IgnorePattern { syntax, @@ -438,6 +451,7 @@ line, file_path, current_syntax.clone(), + false, ); inputs.push(if relativize { pattern.to_relative() @@ -449,6 +463,35 @@ Ok((inputs, warnings)) } +pub fn parse_pattern_args( + patterns: Vec>, + cwd: &Path, + root: &Path, +) -> Result, HgPathError> { + let mut ignore_patterns: Vec = Vec::new(); + for pattern in patterns { + let pattern = parse_one_pattern( + &pattern, + Path::new(""), + PatternSyntax::RelPath, + true, + ); + match pattern.syntax { + PatternSyntax::RelGlob | PatternSyntax::RelPath => { + let name = get_path_from_bytes(&pattern.pattern); + let canon = canonical_path(root, cwd, name)?; + ignore_patterns.push(IgnorePattern { + syntax: pattern.syntax, + pattern: get_bytes_from_path(canon), + source: pattern.source, + }) + } + _ => ignore_patterns.push(pattern.to_owned()), + }; + } + Ok(ignore_patterns) +} + pub fn read_pattern_file( file_path: &Path, warn: bool, diff -r f874342fa568 -r c112cc9effdc rust/hg-core/src/lib.rs --- a/rust/hg-core/src/lib.rs Wed Aug 02 10:16:33 2023 -0400 +++ b/rust/hg-core/src/lib.rs Wed Aug 02 10:46:47 2023 -0400 @@ -25,7 +25,7 @@ DirstateEntry, DirstateParents, EntryState, }; pub mod copy_tracing; -mod filepatterns; +pub mod filepatterns; pub mod matchers; pub mod repo; pub mod revlog; diff -r f874342fa568 -r c112cc9effdc rust/rhg/src/commands/status.rs --- a/rust/rhg/src/commands/status.rs Wed Aug 02 10:16:33 2023 -0400 +++ b/rust/rhg/src/commands/status.rs Wed Aug 02 10:46:47 2023 -0400 @@ -18,13 +18,15 @@ use hg::dirstate::status::StatusPath; use hg::dirstate::TruncatedTimestamp; use hg::errors::{HgError, IoResultExt}; +use hg::filepatterns::parse_pattern_args; use hg::lock::LockError; use hg::manifest::Manifest; use hg::matchers::{AlwaysMatcher, IntersectionMatcher}; use hg::repo::Repo; use hg::utils::debug::debug_wait_for_file; -use hg::utils::files::get_bytes_from_os_string; -use hg::utils::files::get_path_from_bytes; +use hg::utils::files::{ + get_bytes_from_os_str, get_bytes_from_os_string, get_path_from_bytes, +}; use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; use hg::DirstateStatus; use hg::PatternFileWarning; @@ -49,6 +51,12 @@ .alias("st") .about(HELP_TEXT) .arg( + Arg::new("file") + .value_parser(clap::value_parser!(std::ffi::OsString)) + .help("show only these files") + .action(clap::ArgAction::Append), + ) + .arg( Arg::new("all") .help("show status of all files") .short('A') @@ -367,10 +375,8 @@ .expect("commands.status.relative should have a default value"); let relativize_paths = relative_status || { - // TODO should be dependent on whether patterns are passed once - // we support those. // See in Python code with `getuipathfn` usage in `commands.py`. - let legacy_relative_behavior = false; + let legacy_relative_behavior = args.contains_id("file"); match relative_paths(invocation.config)? { RelativePaths::Legacy => legacy_relative_behavior, RelativePaths::Bool(v) => v, @@ -429,6 +435,29 @@ (false, true) => sparse_matcher, (false, false) => Box::new(AlwaysMatcher), }; + let matcher = match args.get_many::("file") { + None => matcher, + Some(files) => { + let patterns: Vec> = files + .filter(|s| !s.is_empty()) + .map(get_bytes_from_os_str) + .collect(); + for file in &patterns { + if file.starts_with(b"set:") { + return Err(CommandError::unsupported("fileset")); + } + } + let cwd = hg::utils::current_dir()?; + let root = repo.working_directory_path(); + let ignore_patterns = parse_pattern_args(patterns, &cwd, root)?; + let files_matcher = + hg::matchers::PatternMatcher::new(ignore_patterns)?; + Box::new(IntersectionMatcher::new( + Box::new(files_matcher), + matcher, + )) + } + }; print_narrow_sparse_warnings( &narrow_warnings, diff -r f874342fa568 -r c112cc9effdc rust/rhg/src/error.rs --- a/rust/rhg/src/error.rs Wed Aug 02 10:16:33 2023 -0400 +++ b/rust/rhg/src/error.rs Wed Aug 02 10:46:47 2023 -0400 @@ -10,7 +10,8 @@ use hg::revlog::RevlogError; use hg::sparse::SparseConfigError; use hg::utils::files::get_bytes_from_path; -use hg::{DirstateError, DirstateMapError, StatusError}; +use hg::utils::hg_path::HgPathError; +use hg::{DirstateError, DirstateMapError, PatternError, StatusError}; use std::convert::From; /// The kind of command error @@ -230,6 +231,18 @@ } } +impl From for CommandError { + fn from(error: HgPathError) -> Self { + CommandError::unsupported(format!("{}", error)) + } +} + +impl From for CommandError { + fn from(error: PatternError) -> Self { + CommandError::unsupported(format!("{}", error)) + } +} + impl From for CommandError { fn from(error: DirstateMapError) -> Self { CommandError::abort(format!("{}", error))