From 0a9f6f2e3f17ee1b0c875f475a44d46b3dc8716e Mon Sep 17 00:00:00 2001 From: Gasper Stukelj Date: Sat, 1 Aug 2026 18:52:30 +0200 Subject: [PATCH 1/2] cli/sparse: export SparseSetArgs as pub(crate) Expose SparseSetArgs so that it can be used for exposing the same set of arguments for sparse set subcommand as part of another command as well. It might be desirable to offer knobs for setting sparse patterns as part of another command (such as `run` or `workspace`). --- cli/src/commands/sparse/mod.rs | 2 +- cli/src/commands/sparse/set.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/src/commands/sparse/mod.rs b/cli/src/commands/sparse/mod.rs index d9ac0817aad..e3d83cf2f7b 100644 --- a/cli/src/commands/sparse/mod.rs +++ b/cli/src/commands/sparse/mod.rs @@ -27,7 +27,7 @@ use self::list::SparseListArgs; use self::list::cmd_sparse_list; use self::reset::SparseResetArgs; use self::reset::cmd_sparse_reset; -use self::set::SparseSetArgs; +pub(crate) use self::set::SparseSetArgs; use self::set::cmd_sparse_set; use crate::cli_util::CommandHelper; use crate::cli_util::WorkspaceCommandHelper; diff --git a/cli/src/commands/sparse/set.rs b/cli/src/commands/sparse/set.rs index 9e581845df0..f5149b8b63d 100644 --- a/cli/src/commands/sparse/set.rs +++ b/cli/src/commands/sparse/set.rs @@ -36,7 +36,7 @@ pub struct SparseSetArgs { value_hint = clap::ValueHint::AnyPath, value_parser = |s: &str| RepoPathBuf::from_relative_path(s), )] - add: Vec, + pub(crate) add: Vec, /// Patterns to remove from the working copy #[arg( @@ -45,11 +45,11 @@ pub struct SparseSetArgs { value_hint = clap::ValueHint::AnyPath, value_parser = |s: &str| RepoPathBuf::from_relative_path(s), )] - remove: Vec, + pub(crate) remove: Vec, /// Include no files in the working copy (combine with --add) #[arg(long)] - clear: bool, + pub(crate) clear: bool, } #[instrument(skip_all)] From fba9130fd3c72b47033faf659916c57030c6e958 Mon Sep 17 00:00:00 2001 From: Gasper Stukelj Date: Sat, 1 Aug 2026 18:52:30 +0200 Subject: [PATCH 2/2] cli/run: add option to set sparse patterns This uses previously exposed sparse::SparseSetArgs to add a familiar interface for setting sparse patterns in the "workspace" in which the command is run. This gives the user an option to forego expensive materializations when unneeded. For example, if a user only needs to run a command only for two files `bar` and `foo`, they can now do so with: `jj run --clear --add bar --add foo -- ` --- cli/src/commands/run.rs | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cli/src/commands/run.rs b/cli/src/commands/run.rs index 95a4b2be37c..029bed9311c 100644 --- a/cli/src/commands/run.rs +++ b/cli/src/commands/run.rs @@ -69,6 +69,7 @@ use crate::cli_util::WorkspaceCommandHelper; use crate::cli_util::WorkspaceCommandTransaction; use crate::command_error::CommandError; use crate::command_error::CommandErrorKind; +use crate::commands::sparse::SparseSetArgs; use crate::ui::Ui; #[derive(Debug, thiserror::Error)] @@ -168,6 +169,15 @@ struct WorkspacePool { /// When true, wipe each slot's working copy on acquisition so every commit /// starts from a freshly checked-out tree (no artifact reuse). clean: bool, + // TODO: (TBD) should we just define our own args? Currently, help seems + // to lack sufficient information on these options, yet using the whole + // sparse::set subcommand smells like poor UI as well. + // + // Another thing to discuss is the apparent poor performance of --remove + // option as compared to a semantically equivalent set of --clear and --add + // options, so we just might want to steer the user towards only using the + // latter two. + sparse_args: Option, } impl WorkspacePool { @@ -176,6 +186,7 @@ impl WorkspacePool { size: NonZeroUsize, auto_tracking_matcher: Box, clean: bool, + sparse_args: Option, ) -> Result { // The parent() call is needed to not write under `.jj/repo/`. let base_path = repo_path.parent().unwrap().join("run").join("default"); @@ -185,6 +196,7 @@ impl WorkspacePool { size, auto_tracking_matcher, clean, + sparse_args, }) } @@ -259,6 +271,32 @@ impl WorkspacePool { ) }; + // TODO: Sparse patterns will persist across `run` invocations, unless --clean + // is passed, which almost certainly violates the principle of least + // surprise. Persistence should probably be opt-in (and perhaps even + // required opt-in per `run` invocation?). TBD. + if let Some(sparse_args) = &self.sparse_args { + // Adopted almost verbatim from `commands::sparse::cmd_sparse_set`. + let new_patterns: Vec<_> = { + let mut new = HashSet::new(); + if !sparse_args.clear { + new.extend(tree_state.sparse_patterns().iter().cloned()); + for path in &sparse_args.remove { + new.remove(path); + } + } + for path in &sparse_args.add { + new.insert(path.to_owned()); + } + new.into_iter().sorted_unstable().collect() + }; + + // TODO: define a separate error. + tree_state + .set_sparse_patterns(new_patterns) + .map_err(|_| RunError::FailedCheckout(commit.id().clone()))?; + } + tree_state .check_out(&commit.tree()) .map_err(|_| RunError::FailedCheckout(commit.id().clone()))?; @@ -660,6 +698,9 @@ pub struct RunArgs { /// `jj run` itself. #[arg(long)] ignore_errors: bool, + + #[clap(flatten)] + sparse_args: Option, } /// Precedence: `--jobs`, `run.jobs` config, 1. @@ -769,6 +810,7 @@ pub async fn cmd_run( jobs, auto_tracking_matcher, args.clean, + args.sparse_args.clone(), )?); let spec = Arc::new(CommandSpec {