From ae25bc817858291705d4d4b290c02f7bb004d5db Mon Sep 17 00:00:00 2001 From: Scott Olson Date: Tue, 2 Apr 2024 21:17:03 +0100 Subject: [PATCH] cli: support filtering by paths in `status` --- CHANGELOG.md | 3 +++ cli/src/commands/resolve.rs | 11 +++++++++- cli/src/commands/status.rs | 19 ++++++++++++----- cli/tests/cli-reference@.md.snap | 6 +++++- cli/tests/test_status_command.rs | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f64bce337b..a2f645f6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * new function `working_copies()` for revsets to show the working copy commits of all workspaces. +* `jj status` now supports filtering by paths. For example, `jj status .` will + only list changed files that are descendants of the current directory. + ### Fixed bugs ## [0.15.1] - 2024-03-06 diff --git a/cli/src/commands/resolve.rs b/cli/src/commands/resolve.rs index 5f622fe7b5..4b3e5281fb 100644 --- a/cli/src/commands/resolve.rs +++ b/cli/src/commands/resolve.rs @@ -17,6 +17,7 @@ use std::io::Write; use itertools::Itertools; use jj_lib::backend::TreeValue; +use jj_lib::matchers::{EverythingMatcher, Matcher}; use jj_lib::merge::MergedTreeValue; use jj_lib::object_id::ObjectId; use jj_lib::repo_path::RepoPathBuf; @@ -88,6 +89,7 @@ pub(crate) fn cmd_resolve( &conflicts, ui.stdout_formatter().as_mut(), &workspace_command, + &EverythingMatcher, ); }; @@ -119,7 +121,12 @@ pub(crate) fn cmd_resolve( formatter, "After this operation, some files at this revision still have conflicts:" )?; - print_conflicted_paths(&new_conflicts, formatter.as_mut(), &workspace_command)?; + print_conflicted_paths( + &new_conflicts, + formatter.as_mut(), + &workspace_command, + &EverythingMatcher, + )?; } } Ok(()) @@ -130,9 +137,11 @@ pub(crate) fn print_conflicted_paths( conflicts: &[(RepoPathBuf, MergedTreeValue)], formatter: &mut dyn Formatter, workspace_command: &WorkspaceCommandHelper, + matcher: &dyn Matcher, ) -> Result<(), CommandError> { let formatted_paths = conflicts .iter() + .filter(|(path, _)| matcher.matches(path)) .map(|(path, _conflict)| workspace_command.format_file_path(path)) .collect_vec(); let max_path_len = formatted_paths.iter().map(|p| p.len()).max().unwrap_or(0); diff --git a/cli/src/commands/status.rs b/cli/src/commands/status.rs index 982570843a..aa6fe2289a 100644 --- a/cli/src/commands/status.rs +++ b/cli/src/commands/status.rs @@ -13,7 +13,6 @@ // limitations under the License. use itertools::Itertools; -use jj_lib::matchers::EverythingMatcher; use jj_lib::repo::Repo; use jj_lib::rewrite::merge_commit_trees; use tracing::instrument; @@ -34,13 +33,17 @@ use crate::ui::Ui; /// * Conflicted branches (see https://github.com/martinvonz/jj/blob/main/docs/branches.md) #[derive(clap::Args, Clone, Debug)] #[command(visible_alias = "st")] -pub(crate) struct StatusArgs {} +pub(crate) struct StatusArgs { + /// Restrict the status display to these paths + #[arg(value_hint = clap::ValueHint::AnyPath)] + paths: Vec, +} #[instrument(skip_all)] pub(crate) fn cmd_status( ui: &mut Ui, command: &CommandHelper, - _args: &StatusArgs, + args: &StatusArgs, ) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; let repo = workspace_command.repo(); @@ -48,6 +51,7 @@ pub(crate) fn cmd_status( .get_wc_commit_id() .map(|id| repo.store().get_commit(id)) .transpose()?; + let matcher = workspace_command.matcher_from_values(&args.paths)?; ui.request_pager(); let mut formatter = ui.stdout_formatter(); let formatter = formatter.as_mut(); @@ -62,7 +66,7 @@ pub(crate) fn cmd_status( diff_util::show_diff_summary( formatter, &workspace_command, - parent_tree.diff_stream(&tree, &EverythingMatcher), + parent_tree.diff_stream(&tree, matcher.as_ref()), )?; } @@ -72,7 +76,12 @@ pub(crate) fn cmd_status( formatter.labeled("conflict"), "There are unresolved conflicts at these paths:" )?; - resolve::print_conflicted_paths(&conflicts, formatter, &workspace_command)? + resolve::print_conflicted_paths( + &conflicts, + formatter, + &workspace_command, + matcher.as_ref(), + )? } let template = workspace_command.commit_summary_template(); diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 4957f73fb4..8cae8a5d02 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -1741,7 +1741,11 @@ This includes: * Conflicted branches (see https://github.com/martinvonz/jj/blob/main/docs/branches.md) -**Usage:** `jj status` +**Usage:** `jj status [PATHS]...` + +###### **Arguments:** + +* `` — Restrict the status display to these paths diff --git a/cli/tests/test_status_command.rs b/cli/tests/test_status_command.rs index 574dda0712..06841c8580 100644 --- a/cli/tests/test_status_command.rs +++ b/cli/tests/test_status_command.rs @@ -62,3 +62,39 @@ fn test_status_ignored_gitignore() { Parent commit: zzzzzzzz 00000000 (empty) (no description set) "###); } + +#[test] +fn test_status_filtered() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); + let repo_path = test_env.env_root().join("repo"); + + std::fs::write(repo_path.join("file_1"), "file_1").unwrap(); + std::fs::write(repo_path.join("file_2"), "file_2").unwrap(); + + // The output filtered to file_1 should not list the addition of file_2. + let stdout = test_env.jj_cmd_success(&repo_path, &["status", "file_1"]); + insta::assert_snapshot!(stdout, @r###" + Working copy changes: + A file_1 + Working copy : qpvuntsm abcaaacd (no description set) + Parent commit: zzzzzzzz 00000000 (empty) (no description set) + "###); + + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "first"]); + test_env.jj_cmd_ok(&repo_path, &["new", "@-"]); + std::fs::write(repo_path.join("file_1"), "file_1_modified").unwrap(); + std::fs::write(repo_path.join("file_2"), "file_2_modified").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["new", "first", "@"]); + + // The output filtered to file_1 should not list the conflict in file_2. + let stdout = test_env.jj_cmd_success(&repo_path, &["status", "file_1"]); + insta::assert_snapshot!(stdout, @r###" + The working copy is clean + There are unresolved conflicts at these paths: + file_1 2-sided conflict + Working copy : mzvwutvl 2431e452 (conflict) (empty) (no description set) + Parent commit: qpvuntsm abcaaacd first | (no description set) + Parent commit: zsuskuln 6ba251d6 (no description set) + "###); +}