cli: add a jj debug tree command for printing the tree

I'm about to make conflicts also get materialized in executable
files. We'll lose some of the test coverage in `test_chmod_command.rs`
then, because the those tests rely on the materialized content to
describe the executable bits. So this commit adds a debug command for
printing tree values and uses that in the tests.
This commit is contained in:
Martin von Zweigbergk 2023-10-23 21:47:19 -07:00 committed by Martin von Zweigbergk
parent 21b11df8a9
commit daa23173c5
2 changed files with 65 additions and 2 deletions

View file

@ -23,7 +23,7 @@ use jj_lib::local_working_copy::{LocalWorkingCopy, LockedLocalWorkingCopy};
use jj_lib::revset;
use jj_lib::working_copy::WorkingCopy;
use crate::cli_util::{resolve_op_for_load, user_error, CommandError, CommandHelper};
use crate::cli_util::{resolve_op_for_load, user_error, CommandError, CommandHelper, RevisionArg};
use crate::template_parser;
use crate::ui::Ui;
@ -40,6 +40,7 @@ pub enum DebugCommands {
ReIndex(DebugReIndexArgs),
#[command(visible_alias = "view")]
Operation(DebugOperationArgs),
Tree(DebugTreeArgs),
#[command(subcommand)]
Watchman(DebugWatchmanSubcommand),
}
@ -89,6 +90,15 @@ pub enum DebugOperationDisplay {
All,
}
/// List the recursive entries of a tree.
#[derive(clap::Args, Clone, Debug)]
pub struct DebugTreeArgs {
#[arg(long, short = 'r', default_value = "@")]
revision: RevisionArg,
paths: Vec<String>,
// TODO: Add an option to include trees that are ancestors of the matched paths
}
#[derive(Subcommand, Clone, Debug)]
pub enum DebugWatchmanSubcommand {
QueryClock,
@ -198,6 +208,7 @@ pub fn cmd_debug(
writeln!(ui.stdout(), "{:#?}", op.view()?.store_view())?;
}
}
DebugCommands::Tree(sub_args) => cmd_debug_tree(ui, command, sub_args)?,
DebugCommands::Watchman(watchman_subcommand) => {
cmd_debug_watchman(ui, command, watchman_subcommand)?;
}
@ -242,6 +253,23 @@ fn cmd_debug_revset(
Ok(())
}
fn cmd_debug_tree(
ui: &mut Ui,
command: &CommandHelper,
args: &DebugTreeArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
let commit = workspace_command.resolve_single_rev(&args.revision, ui)?;
let tree = commit.tree()?;
let matcher = workspace_command.matcher_from_values(&args.paths)?;
for (path, value) in tree.entries_matching(matcher.as_ref()) {
let ui_path = workspace_command.format_file_path(&path);
writeln!(ui.stdout(), "{ui_path}: {value:?}")?;
}
Ok(())
}
#[cfg(feature = "watchman")]
fn cmd_debug_watchman(
ui: &mut Ui,

View file

@ -65,6 +65,11 @@ fn test_chmod_regular_conflict() {
base
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: false })], adds: [Some(File { id: FileId("587be6b4c3f93f93c489c0111bba5596147a26cb"), executable: true }), Some(File { id: FileId("8ba3a16384aacc37d01564b28401755ce8053f51"), executable: false })] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -76,6 +81,11 @@ fn test_chmod_regular_conflict() {
// Test chmodding a conflict
test_env.jj_cmd_ok(&repo_path, &["chmod", "x", "file"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: true })], adds: [Some(File { id: FileId("587be6b4c3f93f93c489c0111bba5596147a26cb"), executable: true }), Some(File { id: FileId("8ba3a16384aacc37d01564b28401755ce8053f51"), executable: true })] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -85,6 +95,11 @@ fn test_chmod_regular_conflict() {
Adding executable file with id 8ba3a16384aacc37d01564b28401755ce8053f51
"###);
test_env.jj_cmd_ok(&repo_path, &["chmod", "n", "file"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: false })], adds: [Some(File { id: FileId("587be6b4c3f93f93c489c0111bba5596147a26cb"), executable: false }), Some(File { id: FileId("8ba3a16384aacc37d01564b28401755ce8053f51"), executable: false })] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -103,6 +118,11 @@ fn test_chmod_regular_conflict() {
insta::assert_snapshot!(stderr, @r###"
Error: No such path at 'nonexistent'.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: false })], adds: [Some(File { id: FileId("587be6b4c3f93f93c489c0111bba5596147a26cb"), executable: false }), Some(File { id: FileId("8ba3a16384aacc37d01564b28401755ce8053f51"), executable: false })] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -161,6 +181,11 @@ fn test_chmod_file_dir_deletion_conflicts() {
"###);
// The file-dir conflict cannot be chmod-ed
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree", "-r=file_dir"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: false })], adds: [Some(File { id: FileId("78981922613b2afb6025042ff6bd878ac1994e85"), executable: false }), Some(Tree(TreeId("133bb38fc4e4bf6b551f1f04db7e48f04cac2877")))] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "-r=file_dir", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -175,6 +200,11 @@ fn test_chmod_file_dir_deletion_conflicts() {
"###);
// The file_deletion conflict can be chmod-ed
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree", "-r=file_deletion"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: false })], adds: [Some(File { id: FileId("78981922613b2afb6025042ff6bd878ac1994e85"), executable: false }), None] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "-r=file_deletion", "file"]);
insta::assert_snapshot!(stdout,
@r###"
@ -189,11 +219,16 @@ fn test_chmod_file_dir_deletion_conflicts() {
test_env.jj_cmd_ok(&repo_path, &["chmod", "x", "file", "-r=file_deletion"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Working copy now at: kmkuslsw 8b70a1d2 file_deletion | (conflict) file_deletion
Working copy now at: kmkuslsw 4cc432b5 file_deletion | (conflict) file_deletion
Parent commit : zsuskuln c51c9c55 file | file
Parent commit : royxmykx 6b18b3c1 deletion | deletion
Added 0 files, modified 1 files, removed 0 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "tree", "-r=file_deletion"]);
insta::assert_snapshot!(stdout,
@r###"
file: Conflicted { removes: [Some(File { id: FileId("df967b96a579e45a18b8251732d16804b2e56a55"), executable: true })], adds: [Some(File { id: FileId("78981922613b2afb6025042ff6bd878ac1994e85"), executable: true }), None] }
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["cat", "-r=file_deletion", "file"]);
insta::assert_snapshot!(stdout,
@r###"