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.
--- 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<Vec<u8>>,
+ cwd: &Path,
+ root: &Path,
+) -> Result<Vec<IgnorePattern>, HgPathError> {
+ let mut ignore_patterns: Vec<IgnorePattern> = Vec::new();
+ for pattern in patterns {
+ let pattern = parse_one_pattern(
+ &pattern,
+ Path::new("<args>"),
+ 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,
--- 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;
--- 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::<std::ffi::OsString>("file") {
+ None => matcher,
+ Some(files) => {
+ let patterns: Vec<Vec<u8>> = 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,
--- 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<HgPathError> for CommandError {
+ fn from(error: HgPathError) -> Self {
+ CommandError::unsupported(format!("{}", error))
+ }
+}
+
+impl From<PatternError> for CommandError {
+ fn from(error: PatternError) -> Self {
+ CommandError::unsupported(format!("{}", error))
+ }
+}
+
impl From<DirstateMapError> for CommandError {
fn from(error: DirstateMapError) -> Self {
CommandError::abort(format!("{}", error))