ok/jj
1
0
Fork 0
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:
Yuya Nishihara 2024-04-08 18:12:11 +09:00
parent 57b423e3d7
commit 05b0fb50f1
4 changed files with 100 additions and 6 deletions

View file

@ -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> {

View file

@ -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

View file

@ -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,

View file

@ -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();