forked from mirrors/jj
cli: add fileset utility functions and debug command
Path parsing will be migrated to parse_union_filesets(), but I haven't decided how we'll go forward: a. migrate everything to fileset b. require flag like "-e FILESET" (note -p conflicts with log -p) c. require flag like "-e FILESET" and deprecate positional PATHs #2554 d. require prefix like "set:FILESET" (not consistent with -r REVSET) I'm currently dogfooding (a). It works for me, but I don't use exotic file names that would require quoting in zsh. #3239
This commit is contained in:
parent
57b423e3d7
commit
05b0fb50f1
4 changed files with 100 additions and 6 deletions
|
@ -69,7 +69,7 @@ use jj_lib::working_copy::{
|
|||
use jj_lib::workspace::{
|
||||
default_working_copy_factories, LockedWorkspace, Workspace, WorkspaceLoadError, WorkspaceLoader,
|
||||
};
|
||||
use jj_lib::{dag_walk, file_util, git, op_heads_store, op_walk, revset};
|
||||
use jj_lib::{dag_walk, file_util, fileset, git, op_heads_store, op_walk, revset};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use tracing::instrument;
|
||||
use tracing_chrome::ChromeLayerBuilder;
|
||||
|
@ -657,10 +657,7 @@ impl WorkspaceCommandHelper {
|
|||
if values.is_empty() {
|
||||
Ok(FilesetExpression::all())
|
||||
} else {
|
||||
let ctx = FilesetParseContext {
|
||||
cwd: &self.cwd,
|
||||
workspace_root: self.workspace.workspace_root(),
|
||||
};
|
||||
let ctx = self.fileset_parse_context();
|
||||
let expressions = values
|
||||
.iter()
|
||||
.map(|v| FilePattern::parse(&ctx, v))
|
||||
|
@ -671,6 +668,26 @@ impl WorkspaceCommandHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses the given fileset expressions and concatenates them all.
|
||||
pub fn parse_union_filesets(
|
||||
&self,
|
||||
file_args: &[String], // TODO: introduce FileArg newtype?
|
||||
) -> Result<FilesetExpression, CommandError> {
|
||||
let ctx = self.fileset_parse_context();
|
||||
let expressions: Vec<_> = file_args
|
||||
.iter()
|
||||
.map(|arg| fileset::parse(arg, &ctx))
|
||||
.try_collect()?;
|
||||
Ok(FilesetExpression::union_all(expressions))
|
||||
}
|
||||
|
||||
pub(crate) fn fileset_parse_context(&self) -> FilesetParseContext<'_> {
|
||||
FilesetParseContext {
|
||||
cwd: &self.cwd,
|
||||
workspace_root: self.workspace.workspace_root(),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub fn base_ignores(&self) -> Result<Arc<GitIgnoreFile>, GitIgnoreError> {
|
||||
fn get_excludes_file_path(config: &gix::config::File) -> Option<PathBuf> {
|
||||
|
|
|
@ -19,6 +19,7 @@ use std::{error, io, iter, str};
|
|||
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::backend::BackendError;
|
||||
use jj_lib::fileset::{FilesetParseError, FilesetParseErrorKind};
|
||||
use jj_lib::git::{GitConfigParseError, GitExportError, GitImportError, GitRemoteManagementError};
|
||||
use jj_lib::gitignore::GitIgnoreError;
|
||||
use jj_lib::op_heads_store::OpHeadResolutionError;
|
||||
|
@ -400,6 +401,24 @@ impl From<RevsetEvaluationError> for CommandError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<FilesetParseError> for CommandError {
|
||||
fn from(err: FilesetParseError) -> Self {
|
||||
let hint = match err.kind() {
|
||||
FilesetParseErrorKind::NoSuchFunction {
|
||||
name: _,
|
||||
candidates,
|
||||
} => format_similarity_hint(candidates),
|
||||
FilesetParseErrorKind::InvalidArguments { .. }
|
||||
| FilesetParseErrorKind::Expression(_) => find_source_parse_error_hint(&err),
|
||||
_ => None,
|
||||
};
|
||||
let mut cmd_err =
|
||||
user_error_with_message(format!("Failed to parse fileset: {}", err.kind()), err);
|
||||
cmd_err.extend_hints(hint);
|
||||
cmd_err
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RevsetParseError> for CommandError {
|
||||
fn from(err: RevsetParseError) -> Self {
|
||||
// Only for the bottom error, which is usually the root cause
|
||||
|
|
|
@ -25,7 +25,7 @@ use jj_lib::object_id::ObjectId;
|
|||
use jj_lib::repo::Repo;
|
||||
use jj_lib::repo_path::RepoPathBuf;
|
||||
use jj_lib::working_copy::WorkingCopy;
|
||||
use jj_lib::{op_walk, revset};
|
||||
use jj_lib::{fileset, op_walk, revset};
|
||||
|
||||
use crate::cli_util::{CommandHelper, RevisionArg};
|
||||
use crate::command_error::{internal_error, user_error, CommandError};
|
||||
|
@ -36,6 +36,7 @@ use crate::{revset_util, template_parser};
|
|||
#[derive(Subcommand, Clone, Debug)]
|
||||
#[command(hide = true)]
|
||||
pub enum DebugCommand {
|
||||
Fileset(DebugFilesetArgs),
|
||||
Revset(DebugRevsetArgs),
|
||||
#[command(name = "workingcopy")]
|
||||
WorkingCopy(DebugWorkingCopyArgs),
|
||||
|
@ -50,6 +51,13 @@ pub enum DebugCommand {
|
|||
Watchman(DebugWatchmanSubcommand),
|
||||
}
|
||||
|
||||
/// Parse fileset expression
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub struct DebugFilesetArgs {
|
||||
#[arg(value_hint = clap::ValueHint::AnyPath)]
|
||||
path: String,
|
||||
}
|
||||
|
||||
/// Evaluate revset to full commit IDs
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub struct DebugRevsetArgs {
|
||||
|
@ -121,6 +129,7 @@ pub fn cmd_debug(
|
|||
subcommand: &DebugCommand,
|
||||
) -> Result<(), CommandError> {
|
||||
match subcommand {
|
||||
DebugCommand::Fileset(args) => cmd_debug_fileset(ui, command, args),
|
||||
DebugCommand::Revset(args) => cmd_debug_revset(ui, command, args),
|
||||
DebugCommand::WorkingCopy(args) => cmd_debug_working_copy(ui, command, args),
|
||||
DebugCommand::Template(args) => cmd_debug_template(ui, command, args),
|
||||
|
@ -132,6 +141,25 @@ pub fn cmd_debug(
|
|||
}
|
||||
}
|
||||
|
||||
fn cmd_debug_fileset(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &DebugFilesetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let workspace_command = command.workspace_helper(ui)?;
|
||||
let ctx = workspace_command.fileset_parse_context();
|
||||
|
||||
let expression = fileset::parse(&args.path, &ctx)?;
|
||||
writeln!(ui.stdout(), "-- Parsed:")?;
|
||||
writeln!(ui.stdout(), "{expression:#?}")?;
|
||||
writeln!(ui.stdout())?;
|
||||
|
||||
let matcher = expression.to_matcher();
|
||||
writeln!(ui.stdout(), "-- Matcher:")?;
|
||||
writeln!(ui.stdout(), "{matcher:#?}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_debug_revset(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
|
|
|
@ -17,6 +17,36 @@ use regex::Regex;
|
|||
|
||||
use crate::common::TestEnvironment;
|
||||
|
||||
#[test]
|
||||
fn test_debug_fileset() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
|
||||
let workspace_path = test_env.env_root().join("repo");
|
||||
|
||||
let stdout = test_env.jj_cmd_success(&workspace_path, &["debug", "fileset", "all()"]);
|
||||
assert_snapshot!(stdout, @r###"
|
||||
-- Parsed:
|
||||
All
|
||||
|
||||
-- Matcher:
|
||||
EverythingMatcher
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&workspace_path, &["debug", "fileset", "cwd:.."]);
|
||||
assert_snapshot!(stderr.replace('\\', "/"), @r###"
|
||||
Error: Failed to parse fileset: Invalid file pattern
|
||||
Caused by:
|
||||
1: --> 1:1
|
||||
|
|
||||
1 | cwd:..
|
||||
| ^----^
|
||||
|
|
||||
= Invalid file pattern
|
||||
2: Path ".." is not in the repo "."
|
||||
3: Invalid component ".." in repo-relative path "../"
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_revset() {
|
||||
let test_env = TestEnvironment::default();
|
||||
|
|
Loading…
Reference in a new issue