diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c07e878f0..c871505f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Dropped support for automatic upgrade of repo formats used by versions before 0.12.0. +* `jj fix` now defaults to the broader revset `-s reachable(@, mutable())` + instead of `-s @`. + ### Deprecations ### New features * Show paths to config files when configuration errors occur +* `jj fix` now supports configuring the default revset for `-s` using the + `revsets.fix` config. + ### Fixed bugs ## [0.18.0] - 2024-06-05 diff --git a/cli/src/commands/fix.rs b/cli/src/commands/fix.rs index 6112e0ef87..590dfb9948 100644 --- a/cli/src/commands/fix.rs +++ b/cli/src/commands/fix.rs @@ -65,7 +65,9 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] #[command(verbatim_doc_comment)] pub(crate) struct FixArgs { - /// Fix files in the specified revision(s) and their descendants + /// Fix files in the specified revision(s) and their descendants. If no + /// revisions are specified, this defaults to the `revsets.fix` setting, or + /// `reachable(@, mutable())` if it is not set. #[arg(long, short)] source: Vec, /// Fix only these paths @@ -80,14 +82,15 @@ pub(crate) fn cmd_fix( args: &FixArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let root_commits: Vec = workspace_command - .parse_union_revsets(if args.source.is_empty() { - &[RevisionArg::AT] - } else { - &args.source - })? - .evaluate_to_commit_ids()? - .collect(); + let root_commits: Vec = if args.source.is_empty() { + workspace_command.parse_revset(&RevisionArg::from( + command.settings().config().get_string("revsets.fix")?, + ))? + } else { + workspace_command.parse_union_revsets(&args.source)? + } + .evaluate_to_commit_ids()? + .collect(); workspace_command.check_rewritable(root_commits.iter())?; let matcher = workspace_command .parse_file_patterns(&args.paths)? diff --git a/cli/src/config-schema.json b/cli/src/config-schema.json index d9267ac88c..25934f95de 100644 --- a/cli/src/config-schema.json +++ b/cli/src/config-schema.json @@ -346,6 +346,11 @@ "type": "object", "description": "Revset expressions used by various commands", "properties": { + "fix": { + "type": "string", + "description": "Default set of revisions to fix when no explicit revset is given for jj fix", + "default": "reachable(@, mutable())" + }, "log": { "type": "string", "description": "Default set of revisions to show when no explicit revset is given for jj log and similar commands", diff --git a/cli/src/config/revsets.toml b/cli/src/config/revsets.toml index 60517d3ffb..d84a745b3e 100644 --- a/cli/src/config/revsets.toml +++ b/cli/src/config/revsets.toml @@ -2,6 +2,7 @@ # adding/updating any of these aliases [revsets] +fix = "reachable(@, mutable())" log = "@ | ancestors(immutable_heads().., 2) | trunk()" [revset-aliases] diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 8b074b21e1..49ab95d811 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -798,7 +798,7 @@ And then run the command `jj fix -s @`. ###### **Options:** -* `-s`, `--source ` — Fix files in the specified revision(s) and their descendants +* `-s`, `--source ` — Fix files in the specified revision(s) and their descendants. If no revisions are specified, this defaults to the `revsets.fix` setting, or `reachable(@, mutable())` if it is not set diff --git a/cli/tests/test_fix_command.rs b/cli/tests/test_fix_command.rs index 2db3e696c3..695d7b2b1b 100644 --- a/cli/tests/test_fix_command.rs +++ b/cli/tests/test_fix_command.rs @@ -139,6 +139,80 @@ fn test_fix_sibling_commit() { insta::assert_snapshot!(content, @"child2"); } +#[test] +fn test_default_revset() { + let (test_env, repo_path) = init_with_fake_formatter(&["--uppercase"]); + std::fs::write(repo_path.join("file"), "trunk1").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "trunk1"]); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "trunk2").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "trunk2"]); + test_env.jj_cmd_ok(&repo_path, &["new", "trunk1"]); + std::fs::write(repo_path.join("file"), "foo").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]); + test_env.jj_cmd_ok(&repo_path, &["new", "trunk1"]); + std::fs::write(repo_path.join("file"), "bar1").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar1"]); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "bar2").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar2"]); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "bar3").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar3"]); + test_env.jj_cmd_ok(&repo_path, &["edit", "bar2"]); + + // With no args and no revset configuration, we fix `reachable(@, mutable())`, + // which includes bar{1,2,3} and excludes trunk{1,2} (which is immutable) and + // foo (which is mutable but not reachable). + test_env.add_config(r#"revset-aliases."immutable_heads()" = "trunk2""#); + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix"]); + insta::assert_snapshot!(stdout, @""); + insta::assert_snapshot!(stderr, @r###" + Fixed 3 commits of 3 checked. + Working copy now at: yostqsxw 0bd830d2 bar2 | (no description set) + Parent commit : yqosqzyt 4747dd17 bar1 | (no description set) + Added 0 files, modified 1 files, removed 0 files + "###); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "trunk1"]); + insta::assert_snapshot!(content, @"trunk1"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "trunk2"]); + insta::assert_snapshot!(content, @"trunk2"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "foo"]); + insta::assert_snapshot!(content, @"foo"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "bar1"]); + insta::assert_snapshot!(content, @"BAR1"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "bar2"]); + insta::assert_snapshot!(content, @"BAR2"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "bar3"]); + insta::assert_snapshot!(content, @"BAR3"); +} + +#[test] +fn test_custom_default_revset() { + let (test_env, repo_path) = init_with_fake_formatter(&["--uppercase"]); + + std::fs::write(repo_path.join("file"), "foo").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "bar").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar"]); + + // Check out a different commit so that the schema default `reachable(@, + // mutable())` would behave differently from our customized default. + test_env.jj_cmd_ok(&repo_path, &["new", "-r", "foo"]); + test_env.add_config(r#"revsets.fix = "bar""#); + + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix"]); + insta::assert_snapshot!(stdout, @""); + insta::assert_snapshot!(stderr, @r###" + Fixed 1 commits of 1 checked. + "###); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "foo"]); + insta::assert_snapshot!(content, @"foo"); + let content = test_env.jj_cmd_success(&repo_path, &["print", "file", "-r", "bar"]); + insta::assert_snapshot!(content, @"BAR"); +} + #[test] fn test_fix_immutable_commit() { let (test_env, repo_path) = init_with_fake_formatter(&["--uppercase"]);