mirror of
https://github.com/jj-vcs/jj.git
synced 2026-07-03 14:02:54 +08:00
Updated the CLI to support line range based file fixing. * We added `line_range_arg` and `run_tool_if_zero_line_ranges` to `ToolConfig` so users can configure how changed line ranges are passed to tools and explicitly specify whether a tool should run when there are zero modified lines. * We introduced the `--all-lines` argument to allow users to fall back to the previous behavior of formatting the entire file when `line_range_arg` is specified. * We use a new `compute_regions_to_format` function to calculate the changed line ranges dynamically before applying each individual formatter tool in the sequence. * Because a formatter tool might add or remove lines, shifting the line numbers for subsequent tools, this determines the exact target ranges by diffing the static base file content against the intermediate output of the previous tool. * To validate this logic, we extended the `fake-formatter` test fixture with a `--line-ranges` argument to restrict execution to specific lines. We also introduced a `--split-even-length-lines` flag to mimic realistic formatters that expand line counts, proving our dynamic range recalculation properly shifts lines in complete end-to-end tests.
2178 lines
73 KiB
Rust
2178 lines
73 KiB
Rust
// Copyright 2024 The Jujutsu Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#[cfg(unix)]
|
|
use std::os::unix::fs::PermissionsExt as _;
|
|
use std::path::PathBuf;
|
|
|
|
use indoc::formatdoc;
|
|
use indoc::indoc;
|
|
use jj_lib::file_util::symlink_file;
|
|
use testutils::TestResult;
|
|
|
|
use crate::common::TestEnvironment;
|
|
use crate::common::to_toml_value;
|
|
|
|
fn set_up_fake_formatter(test_env: &mut TestEnvironment, args: &[&str]) {
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
test_env.add_config(formatdoc! {"
|
|
[fix.tools.fake-formatter]
|
|
command = {command}
|
|
patterns = ['all()']
|
|
",
|
|
command = toml_edit::Value::from_iter(
|
|
[formatter_path.to_str().unwrap()]
|
|
.iter()
|
|
.chain(args)
|
|
.copied()
|
|
)
|
|
});
|
|
test_env.add_paths_to_normalize(formatter_path, "$FAKE_FORMATTER_PATH");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_no_tools() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
|
|
work_dir.write_file("file", "content\n");
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Config error: No `fix.tools` are configured
|
|
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.
|
|
[EOF]
|
|
[exit status: 1]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
content
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_nonexistent_tool() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
test_env.add_config(
|
|
r###"
|
|
[fix.tools.my-tool]
|
|
command = ["nonexistent-fix-tool-binary"]
|
|
patterns = ["glob:**"]
|
|
"###,
|
|
);
|
|
|
|
work_dir.write_file("file", "content\n");
|
|
let output = work_dir.run_jj(["fix"]);
|
|
// We inform the user about the non-existent tool
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Warning: Failed to start `nonexistent-fix-tool-binary`
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_multiple_tools() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo"]
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("foo", "Foo\n");
|
|
work_dir.write_file("bar", "Bar\n");
|
|
work_dir.write_file("baz", "Baz\n");
|
|
|
|
work_dir.run_jj(["fix"]).success();
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
FOO
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
bar
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
Baz
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_multiple_tools_with_same_name() {
|
|
let mut test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
|
|
// Multiple definitions with the same `name` are not allowed, because it is
|
|
// likely to be a mistake, and mistakes are risky when they rewrite files.
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.my-tool]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo"]
|
|
|
|
[fix.tools.my-tool]
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("foo", "Foo\n");
|
|
work_dir.write_file("bar", "Bar\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Config error: Configuration cannot be parsed as TOML document
|
|
Caused by: TOML parse error at line 6, column 20
|
|
|
|
|
6 | [fix.tools.my-tool]
|
|
| ^^^^^^^
|
|
duplicate key
|
|
|
|
Hint: Check the config file: $TEST_ENV/config/config0002.toml
|
|
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.
|
|
[EOF]
|
|
[exit status: 1]
|
|
");
|
|
|
|
test_env.set_config_path("/dev/null");
|
|
let work_dir = test_env.work_dir("repo");
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
Foo
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
Bar
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_disabled_tools() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
# default is enabled
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo"]
|
|
|
|
[fix.tools.tool-2]
|
|
enabled = true
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
|
|
[fix.tools.tool-3]
|
|
enabled = false
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["baz"]
|
|
"###
|
|
));
|
|
|
|
work_dir.write_file("foo", "Foo\n");
|
|
work_dir.write_file("bar", "Bar\n");
|
|
work_dir.write_file("baz", "Baz\n");
|
|
|
|
work_dir.run_jj(["fix"]).success();
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
FOO
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
bar
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
Baz
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_disabled_tools_warning_when_all_tools_are_disabled() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-2]
|
|
enabled = false
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
"###
|
|
));
|
|
|
|
work_dir.write_file("bar", "Bar\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Config error: At least one entry of `fix.tools` must be enabled.
|
|
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.
|
|
[EOF]
|
|
[exit status: 1]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_tables_overlapping_patterns() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--append", "tool-1"]
|
|
patterns = ["foo", "bar"]
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--append", "tool-2"]
|
|
patterns = ["bar", "baz"]
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("foo", "foo\n");
|
|
work_dir.write_file("bar", "bar\n");
|
|
work_dir.write_file("baz", "baz\n");
|
|
|
|
work_dir.run_jj(["fix"]).success();
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
foo
|
|
tool-1[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
bar
|
|
tool-1tool-2[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
baz
|
|
tool-2[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_tables_all_commands_missing() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
test_env.add_config(
|
|
r###"
|
|
[fix.tools.my-tool-missing-command-1]
|
|
patterns = ["foo"]
|
|
|
|
[fix.tools.my-tool-missing-command-2]
|
|
patterns = ['glob:"ba*"']
|
|
"###,
|
|
);
|
|
|
|
work_dir.write_file("foo", "foo\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output.normalize_backslash(), @"
|
|
------- stderr -------
|
|
Config error: Invalid type or value for fix.tools.my-tool-missing-command-1
|
|
Caused by: missing field `command`
|
|
|
|
Hint: Check the config file: $TEST_ENV/config/config0002.toml
|
|
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.
|
|
[EOF]
|
|
[exit status: 1]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
foo
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_tables_some_commands_missing() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo"]
|
|
|
|
[fix.tools.my-tool-missing-command]
|
|
patterns = ['bar']
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("foo", "foo\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output.normalize_backslash(), @"
|
|
------- stderr -------
|
|
Config error: Invalid type or value for fix.tools.my-tool-missing-command
|
|
Caused by: missing field `command`
|
|
|
|
Hint: Check the config file: $TEST_ENV/config/config0002.toml
|
|
For help, see https://docs.jj-vcs.dev/latest/config/ or use `jj help -k config`.
|
|
[EOF]
|
|
[exit status: 1]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
foo
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_tables_empty_patterns_list() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.my-tool-empty-patterns]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = []
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("foo", "foo\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
foo
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_filesets() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.my-tool-match-one]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ['glob:"a*"']
|
|
|
|
[fix.tools.my-tool-match-two]
|
|
command = [{formatter}, "--reverse"]
|
|
patterns = ['glob:"b*"']
|
|
|
|
[fix.tools.my-tool-match-none]
|
|
command = [{formatter}, "--append", "SHOULD NOT APPEAR"]
|
|
patterns = ['glob:"this-doesnt-match-anything-*"']
|
|
"###,
|
|
));
|
|
|
|
work_dir.write_file("a1", "a1\n");
|
|
work_dir.write_file("b1", "b1\n");
|
|
work_dir.write_file("b2", "b2\n");
|
|
|
|
work_dir.run_jj(["fix"]).success();
|
|
|
|
let output = work_dir.run_jj(["file", "show", "a1", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
A1
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b1", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
|
|
1b[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b2", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
|
|
2b[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_relative_paths() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool]
|
|
command = [{formatter}, "--stdout", "Fixed!"]
|
|
patterns = ['glob:"foo*"']
|
|
"###,
|
|
));
|
|
|
|
let sub_dir = work_dir.create_dir("dir");
|
|
work_dir.write_file("foo1", "unfixed\n");
|
|
work_dir.write_file("foo2", "unfixed\n");
|
|
work_dir.write_file("dir/foo3", "unfixed\n");
|
|
|
|
// Positional arguments are cwd-relative, but the configured patterns are
|
|
// repo-relative, so this command fixes the empty intersection of those
|
|
// filesets.
|
|
sub_dir.run_jj(["fix", "foo3"]).success();
|
|
let output = work_dir.run_jj(["file", "show", "foo1", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "foo2", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "dir/foo3", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
|
|
// Positional arguments can specify a subset of the configured fileset.
|
|
sub_dir.run_jj(["fix", "../foo1"]).success();
|
|
let output = work_dir.run_jj(["file", "show", "foo1", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"Fixed![EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "foo2", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "dir/foo3", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
|
|
// The current directory does not change the interpretation of the config, so
|
|
// foo2 is fixed but not dir/foo3.
|
|
sub_dir.run_jj(["fix"]).success();
|
|
let output = work_dir.run_jj(["file", "show", "foo1", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"Fixed![EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "foo2", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"Fixed![EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "dir/foo3", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
unfixed
|
|
[EOF]
|
|
");
|
|
|
|
// The output filtered to a non-existent file should display a warning.
|
|
let output = work_dir.run_jj(["fix", "nonexistent"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Warning: No matching entries for paths: nonexistent
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_relative_tool_path_from_subdirectory() -> TestResult {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
|
|
// Copy the fake-formatter into the workspace as a relative tool
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
let formatter_name = formatter_path.file_name().unwrap().to_str().unwrap();
|
|
let tool_dir = work_dir.create_dir("tools");
|
|
let workspace_formatter_path = tool_dir.root().join(formatter_name);
|
|
std::fs::copy(formatter_path, &workspace_formatter_path)?;
|
|
work_dir.write_file(".gitignore", "tools/\n");
|
|
let formatter_relative_path = PathBuf::from_iter(["$root", "tools", formatter_name]);
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.a]
|
|
command = [{path}, "--uppercase"]
|
|
patterns = ['glob:"**/*.txt"']
|
|
"###,
|
|
path = to_toml_value(formatter_relative_path.to_str().unwrap())
|
|
));
|
|
|
|
// Create a test file and subdirectory
|
|
work_dir.write_file("test.txt", "hello world\n");
|
|
let sub_dir = work_dir.create_dir("subdir");
|
|
work_dir.write_file("subdir/nested.txt", "nested content\n");
|
|
work_dir.run_jj(["debug", "snapshot"]).success();
|
|
let setup_opid = work_dir.current_operation_id();
|
|
|
|
// Run fix from workspace root
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 57a27b36 (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 2 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "test.txt", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
HELLO WORLD
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "subdir/nested.txt", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
NESTED CONTENT
|
|
[EOF]
|
|
");
|
|
|
|
// Reset so the fix tools should have an effect again
|
|
work_dir.run_jj(["op", "restore", &setup_opid]).success();
|
|
|
|
// Run fix from the subdirectory
|
|
let output = sub_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 05404d5b (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 2 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "test.txt", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
HELLO WORLD
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "subdir/nested.txt", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
NESTED CONTENT
|
|
[EOF]
|
|
");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_empty_commit() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_leaf_commit() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "unaffected");
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "affected");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: rlvkpnrz f5c11961 (no description set)
|
|
Parent commit (@-) : qpvuntsm b37955c0 (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"unaffected[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"AFFECTED[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_parent_commit() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
// Using one file name for all commits adds coverage of some possible bugs.
|
|
work_dir.write_file("file", "parent");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "parent"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "child1");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child1"])
|
|
.success();
|
|
work_dir.run_jj(["new", "-r", "parent"]).success();
|
|
work_dir.write_file("file", "child2");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child2"])
|
|
.success();
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "parent"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 3 commits of 3 checked.
|
|
Working copy (@) now at: mzvwutvl e7ba6d31 child2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm 49f1ddd5 parent | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "parent"]);
|
|
insta::assert_snapshot!(output, @"PARENT[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "child1"]);
|
|
insta::assert_snapshot!(output, @"CHILD1[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "child2"]);
|
|
insta::assert_snapshot!(output, @"CHILD2[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_sibling_commit() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "parent");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "parent"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "child1");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child1"])
|
|
.success();
|
|
work_dir.run_jj(["new", "-r", "parent"]).success();
|
|
work_dir.write_file("file", "child2");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child2"])
|
|
.success();
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "child1"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "parent"]);
|
|
insta::assert_snapshot!(output, @"parent[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "child1"]);
|
|
insta::assert_snapshot!(output, @"CHILD1[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "child2"]);
|
|
insta::assert_snapshot!(output, @"child2[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_descendant_commits() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("parent", "parent");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "parent"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("child1", "child1");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child1"])
|
|
.success();
|
|
work_dir.run_jj(["new", "-r", "parent"]).success();
|
|
work_dir.write_file("child2", "child2");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "child2"])
|
|
.success();
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "parent", "child1", "child2", "nonexistent"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Warning: No matching entries for paths: nonexistent
|
|
Fixed 2 commits of 3 checked.
|
|
Working copy (@) now at: mzvwutvl afe0ade0 child2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm c9cb6288 parent | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "parent", "-r", "parent"]);
|
|
insta::assert_snapshot!(output, @"parent[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "child1", "-r", "child1"]);
|
|
insta::assert_snapshot!(output, @"CHILD1[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "child2", "-r", "child2"]);
|
|
insta::assert_snapshot!(output, @"CHILD2[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_default_revset() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "trunk1");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "trunk1"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "trunk2");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "trunk2"])
|
|
.success();
|
|
work_dir.run_jj(["new", "trunk1"]).success();
|
|
work_dir.write_file("file", "foo");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "foo"])
|
|
.success();
|
|
work_dir.run_jj(["new", "trunk1"]).success();
|
|
work_dir.write_file("file", "bar1");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "bar1"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "bar2");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "bar2"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "bar3");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "bar3"])
|
|
.success();
|
|
work_dir.run_jj(["edit", "bar2"]).success();
|
|
|
|
// 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 output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 3 commits of 3 checked.
|
|
Working copy (@) now at: yostqsxw 932b950d bar2 | (no description set)
|
|
Parent commit (@-) : yqosqzyt 8a37ed67 bar1 | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "trunk1"]);
|
|
insta::assert_snapshot!(output, @"trunk1[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "trunk2"]);
|
|
insta::assert_snapshot!(output, @"trunk2[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "foo"]);
|
|
insta::assert_snapshot!(output, @"foo[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "bar1"]);
|
|
insta::assert_snapshot!(output, @"BAR1[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "bar2"]);
|
|
insta::assert_snapshot!(output, @"BAR2[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "bar3"]);
|
|
insta::assert_snapshot!(output, @"BAR3[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_custom_default_revset() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
|
|
work_dir.write_file("file", "foo");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "foo"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "bar");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "bar"])
|
|
.success();
|
|
|
|
// Check out a different commit so that the schema default `reachable(@,
|
|
// mutable())` would behave differently from our customized default.
|
|
work_dir.run_jj(["new", "-r", "foo"]).success();
|
|
test_env.add_config(r#"revsets.fix = "bar""#);
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "foo"]);
|
|
insta::assert_snapshot!(output, @"foo[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "bar"]);
|
|
insta::assert_snapshot!(output, @"BAR[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_immutable_commit() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "immutable");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "immutable"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "mutable");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "mutable"])
|
|
.success();
|
|
test_env.add_config(r#"revset-aliases."immutable_heads()" = "immutable""#);
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "immutable"]);
|
|
insta::assert_snapshot!(output, @r#"
|
|
------- stderr -------
|
|
Error: Commit a86b2eccaaab is immutable
|
|
Hint: Could not modify commit: qpvuntsm a86b2ecc immutable | (no description set)
|
|
Hint: Immutable commits are used to protect shared history.
|
|
Hint: For more information, see:
|
|
- https://docs.jj-vcs.dev/latest/config/#set-of-immutable-commits
|
|
- `jj help -k config`, "Set of immutable commits"
|
|
Hint: This operation would rewrite 1 immutable commits.
|
|
[EOF]
|
|
[exit status: 1]
|
|
"#);
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "immutable"]);
|
|
insta::assert_snapshot!(output, @"immutable[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "mutable"]);
|
|
insta::assert_snapshot!(output, @"mutable[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_empty_file() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_large_file() {
|
|
let mut test_env = TestEnvironment::default();
|
|
// Set to byte mode, so that fake-formatter doesn't read from the stdin all at
|
|
// once.
|
|
set_up_fake_formatter(&mut test_env, &["--lowercase", "--byte-mode"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
// 512KB should be larger than most default page size, so that the stdin pipe
|
|
// will be blocked if fake-formatter doesn't read, and the stdout pipe will
|
|
// be blocked if jj doesn't read.
|
|
let mut large_contents = b"A".repeat(0x800_000);
|
|
large_contents.push(b'\n');
|
|
work_dir.write_file("file", &large_contents);
|
|
|
|
work_dir
|
|
.run_jj([
|
|
"config",
|
|
"set",
|
|
"--repo",
|
|
"snapshot.max-new-file-size",
|
|
&large_contents.len().to_string(),
|
|
])
|
|
.success();
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm d500c021 (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
let stdout_bytes = output.stdout.raw().as_bytes();
|
|
assert_eq!(stdout_bytes.len(), large_contents.len());
|
|
assert!(stdout_bytes == large_contents.to_ascii_lowercase());
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_some_paths() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file1", "foo");
|
|
work_dir.write_file("file2", "bar");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@", "file1"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 0279baba (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file1"]);
|
|
insta::assert_snapshot!(output, @"FOO[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file2"]);
|
|
insta::assert_snapshot!(output, @"bar[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_cyclic() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--reverse"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "content\n");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm ce361156 (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
|
|
tnetnoc[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 547f589b (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
|
|
content[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_deduplication() {
|
|
// Append all fixed content to a log file. Note that fix tools are always run
|
|
// from the workspace root, so this will always write to $root/$path-fixlog.
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase", "--tee", "$path-fixlog"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
|
|
// There are at least two interesting cases: the content is repeated immediately
|
|
// in the child commit, or later in another descendant.
|
|
work_dir.write_file("file", "foo\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "a"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "bar\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "b"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "bar\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c"])
|
|
.success();
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("file", "foo\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "d"])
|
|
.success();
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "a"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 4 commits of 4 checked.
|
|
Working copy (@) now at: yqosqzyt 9849a250 d | (no description set)
|
|
Parent commit (@-) : mzvwutvl 9544f381 c | (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "a"]);
|
|
insta::assert_snapshot!(output, @"
|
|
FOO
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "b"]);
|
|
insta::assert_snapshot!(output, @"
|
|
BAR
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "c"]);
|
|
insta::assert_snapshot!(output, @"
|
|
BAR
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "d"]);
|
|
insta::assert_snapshot!(output, @"
|
|
FOO
|
|
[EOF]
|
|
");
|
|
|
|
// Each new content string only appears once in the log, because all the other
|
|
// inputs (like file name) were identical, and so the results were reused. We
|
|
// sort the log because the order of execution inside `jj fix` is undefined.
|
|
insta::assert_snapshot!(sorted_lines(work_dir.root().join("file-fixlog")), @"
|
|
BAR
|
|
FOO
|
|
");
|
|
}
|
|
|
|
fn sorted_lines(path: PathBuf) -> String {
|
|
let mut log: Vec<_> = std::fs::read_to_string(path.as_os_str())
|
|
.unwrap()
|
|
.lines()
|
|
.map(String::from)
|
|
.collect();
|
|
log.sort();
|
|
log.join("\n")
|
|
}
|
|
|
|
#[test]
|
|
fn test_executed_but_nothing_changed() {
|
|
// Show that the tool ran by causing a side effect with --tee, and test that we
|
|
// do the right thing when the tool's output is exactly equal to its input.
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--tee", "$path-copy"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "content\n");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
content
|
|
[EOF]
|
|
");
|
|
let copy_content = work_dir.read_file("file-copy");
|
|
insta::assert_snapshot!(copy_content, @"content");
|
|
|
|
// fix tools are always run from the workspace root, regardless of working
|
|
// directory at time of invocation.
|
|
let sub_dir = work_dir.create_dir("dir");
|
|
let output = sub_dir.run_jj(["fix"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
|
|
let copy_content = work_dir.read_file("file-copy");
|
|
insta::assert_snapshot!(copy_content, @"
|
|
content
|
|
content
|
|
");
|
|
assert!(!sub_dir.root().join("file-copy").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_failure() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--fail"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "content");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Warning: Fix tool `$FAKE_FORMATTER_PATH` exited with non-zero exit code for `file`
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"content[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_stderr_success() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(
|
|
&mut test_env,
|
|
&["--stderr", "error", "--stdout", "new content"],
|
|
);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "old content");
|
|
|
|
// TODO: Associate the stderr lines with the relevant tool/file/commit instead
|
|
// of passing it through directly.
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
file:
|
|
error
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm cb75cbcb (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"new content[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_stderr_failure() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(
|
|
&mut test_env,
|
|
&["--stderr", "error", "--stdout", "new content", "--fail"],
|
|
);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "old content");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
file:
|
|
error
|
|
Warning: Fix tool `$FAKE_FORMATTER_PATH` exited with non-zero exit code for `file`
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"old content[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_command() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
test_env.add_config(indoc! {"
|
|
[fix.tools.bad-tool]
|
|
command = ['this_executable_shouldnt_exist']
|
|
patterns = ['all()']
|
|
"});
|
|
// TODO: We should display a warning about invalid tool configurations. When we
|
|
// support multiple tools, we should also keep going to see if any of the other
|
|
// executions succeed.
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_file_types() -> TestResult {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "content");
|
|
work_dir.create_dir("dir");
|
|
symlink_file("file", work_dir.root().join("link"))?;
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 0184b215 (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CONTENT[EOF]");
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn test_fix_executable() -> TestResult {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let path = work_dir.root().join("file");
|
|
work_dir.write_file("file", "content");
|
|
let mut permissions = std::fs::metadata(&path)?.permissions();
|
|
permissions.set_mode(permissions.mode() | 0o111);
|
|
std::fs::set_permissions(&path, permissions)?;
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: qpvuntsm 5293bf26 (no description set)
|
|
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CONTENT[EOF]");
|
|
let executable = std::fs::metadata(&path)?.permissions().mode() & 0o111;
|
|
assert_eq!(executable, 0o111);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_trivial_merge_commit() {
|
|
// All the changes are attributable to a parent, so none are fixed (in the same
|
|
// way that none would be shown in `jj diff -r @`).
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file_a", "content a");
|
|
work_dir.write_file("file_c", "content c");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "a"])
|
|
.success();
|
|
work_dir.run_jj(["new", "@-"]).success();
|
|
work_dir.write_file("file_b", "content b");
|
|
work_dir.write_file("file_c", "content c");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "b"])
|
|
.success();
|
|
work_dir.run_jj(["new", "a", "b"]).success();
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file_a", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"content a[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file_b", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"content b[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file_c", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"content c[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_adding_merge_commit() {
|
|
// None of the changes are attributable to a parent, so they are all fixed (in
|
|
// the same way that they would be shown in `jj diff -r @`).
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file_a", "content a");
|
|
work_dir.write_file("file_c", "content c");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "a"])
|
|
.success();
|
|
work_dir.run_jj(["new", "@-"]).success();
|
|
work_dir.write_file("file_b", "content b");
|
|
work_dir.write_file("file_c", "content c");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "b"])
|
|
.success();
|
|
work_dir.run_jj(["new", "a", "b"]).success();
|
|
work_dir.write_file("file_a", "change a");
|
|
work_dir.write_file("file_b", "change b");
|
|
work_dir.write_file("file_c", "change c");
|
|
work_dir.write_file("file_d", "change d");
|
|
|
|
let output = work_dir.run_jj(["fix", "-s", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: mzvwutvl 9f580aac (no description set)
|
|
Parent commit (@-) : qpvuntsm 93f04460 a | (no description set)
|
|
Parent commit (@-) : kkmpptxz ad4fc36c b | (no description set)
|
|
Added 0 files, modified 4 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file_a", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CHANGE A[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file_b", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CHANGE B[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file_c", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CHANGE C[EOF]");
|
|
let output = work_dir.run_jj(["file", "show", "file_d", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"CHANGE D[EOF]");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_both_sides_of_conflict() {
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "content a\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "a"])
|
|
.success();
|
|
work_dir.run_jj(["new", "@-"]).success();
|
|
work_dir.write_file("file", "content b\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "b"])
|
|
.success();
|
|
work_dir.run_jj(["new", "a", "b"]).success();
|
|
|
|
// The conflicts are not different from the merged parent, so they would not be
|
|
// fixed if we didn't fix the parents also.
|
|
let output = work_dir.run_jj(["fix", "-s", "a", "-s", "b"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 3 commits of 3 checked.
|
|
Working copy (@) now at: mzvwutvl 1bfa5dc3 (conflict) (empty) (no description set)
|
|
Parent commit (@-) : qpvuntsm 0eae0dae a | (no description set)
|
|
Parent commit (@-) : kkmpptxz eb61ba8d b | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
Warning: There are unresolved conflicts at these paths:
|
|
file 2-sided conflict
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "a"]);
|
|
insta::assert_snapshot!(output, @"
|
|
CONTENT A
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "b"]);
|
|
insta::assert_snapshot!(output, @"
|
|
CONTENT B
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
<<<<<<< conflict 1 of 1
|
|
%%%%%%% diff from: zzzzzzzz 00000000
|
|
\\\\\\\ to: qpvuntsm c8346c1c
|
|
+CONTENT A
|
|
+++++++ kkmpptxz 7c4518cb
|
|
CONTENT B
|
|
>>>>>>> conflict 1 of 1 ends
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_resolve_conflict() {
|
|
// If both sides of the conflict look the same after being fixed, the conflict
|
|
// will be resolved.
|
|
let mut test_env = TestEnvironment::default();
|
|
set_up_fake_formatter(&mut test_env, &["--uppercase"]);
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir.write_file("file", "Content\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "a"])
|
|
.success();
|
|
work_dir.run_jj(["new", "@-"]).success();
|
|
work_dir.write_file("file", "cOnTeNt\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "b"])
|
|
.success();
|
|
work_dir.run_jj(["new", "a", "b"]).success();
|
|
|
|
// The conflicts are not different from the merged parent, so they would not be
|
|
// fixed if we didn't fix the parents also.
|
|
let output = work_dir.run_jj(["fix", "-s", "a", "-s", "b"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 3 commits of 3 checked.
|
|
Working copy (@) now at: mzvwutvl c4e4665e (empty) (no description set)
|
|
Parent commit (@-) : qpvuntsm 7a0dbb95 a | (no description set)
|
|
Parent commit (@-) : kkmpptxz 5d9510ab b | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "file", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
CONTENT
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_files() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
|
|
// Consider a few cases:
|
|
// File A: in patterns, changed in child
|
|
// File B: in patterns, NOT changed in child
|
|
// File C: NOT in patterns, NOT changed in child
|
|
// File D: NOT in patterns, changed in child
|
|
// Some files will be in subdirectories to make sure we're covering that aspect
|
|
// of matching.
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool]
|
|
command = [{formatter}, "--append", "fixed"]
|
|
patterns = ["a/a", "b/b"]
|
|
"###,
|
|
));
|
|
|
|
work_dir.create_dir("a");
|
|
work_dir.create_dir("b");
|
|
work_dir.create_dir("c");
|
|
work_dir.write_file("a/a", "parent aaa\n");
|
|
work_dir.write_file("b/b", "parent bbb\n");
|
|
work_dir.write_file("c/c", "parent ccc\n");
|
|
work_dir.write_file("ddd", "parent ddd\n");
|
|
work_dir.run_jj(["commit", "-m", "parent"]).success();
|
|
|
|
work_dir.write_file("a/a", "child aaa\n");
|
|
work_dir.write_file("ddd", "child ddd\n");
|
|
work_dir.run_jj(["describe", "-m", "child"]).success();
|
|
|
|
// Specifying files means exactly those files will be fixed in each revision,
|
|
// although some like file C won't have any tools configured to make changes to
|
|
// them. Specified but unfixed files are silently skipped, whether they lack
|
|
// configuration, are ignored, don't exist, aren't normal files, etc.
|
|
let output = work_dir.run_jj([
|
|
"fix",
|
|
"--include-unchanged-files",
|
|
"b/b",
|
|
"c/c",
|
|
"does_not.exist",
|
|
]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Warning: No matching entries for paths: does_not.exist
|
|
Fixed 2 commits of 2 checked.
|
|
Working copy (@) now at: rlvkpnrz d8503fee child
|
|
Parent commit (@-) : qpvuntsm 62c6ee98 parent
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "a/a", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent aaa
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b/b", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent bbb
|
|
fixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "c/c", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ccc
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "ddd", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ddd
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "a/a", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
child aaa
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b/b", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent bbb
|
|
fixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "c/c", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ccc
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "ddd", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
child ddd
|
|
[EOF]
|
|
");
|
|
|
|
// Not specifying files means all files will be fixed in each revision.
|
|
let output = work_dir.run_jj(["fix", "--include-unchanged-files"]);
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 2 commits of 2 checked.
|
|
Working copy (@) now at: rlvkpnrz 16aeb14c child
|
|
Parent commit (@-) : qpvuntsm 5257b8ec parent
|
|
Added 0 files, modified 2 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "a/a", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent aaa
|
|
fixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b/b", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent bbb
|
|
fixedfixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "c/c", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ccc
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "ddd", "-r", "@-"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ddd
|
|
[EOF]
|
|
");
|
|
|
|
let output = work_dir.run_jj(["file", "show", "a/a", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
child aaa
|
|
fixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "b/b", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent bbb
|
|
fixedfixed[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "c/c", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
parent ccc
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "ddd", "-r", "@"]);
|
|
insta::assert_snapshot!(output, @"
|
|
child ddd
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_with_line_ranges() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo", "baz", "qux"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar", "baz"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
"###,
|
|
));
|
|
|
|
// Initial commit.
|
|
work_dir.write_file("foo", "Foo1\nFoo2\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1\nBar2\nBar3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c1"])
|
|
.success();
|
|
|
|
// Create new commit modifying foo and bar.
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("foo", "Foo1\nFoo2-Mod\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1-Mod\nBar2\nBar3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c2"])
|
|
.success();
|
|
|
|
// Commit c3 doing more modifications to foo and bar.
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("foo", "Foo1\nFoo2\nFoo3-Mod\n");
|
|
work_dir.remove_file("bar");
|
|
work_dir.write_file("baz", "");
|
|
work_dir.write_file("qux", "newfile\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c3"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the second commit.
|
|
let output = work_dir.run_jj(["fix", "-s", "c2"]).success();
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 2 commits of 2 checked.
|
|
Working copy (@) now at: mzvwutvl 7d52a6e8 c3 | (no description set)
|
|
Parent commit (@-) : kkmpptxz a8e4c2e8 c2 | (no description set)
|
|
Added 0 files, modified 2 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the first commit.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
Foo2
|
|
Foo3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Bar1
|
|
Bar2
|
|
Bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was applied to the second commit.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
FOO2-MOD
|
|
Foo3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
bar1-mod
|
|
Bar2
|
|
Bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was applied to the third commit but in different
|
|
// line ranges.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c3"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
Foo2
|
|
FOO3-MOD
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c3"]);
|
|
insta::assert_snapshot!(output, @"");
|
|
let output = work_dir.run_jj(["file", "show", "qux", "-r", "c3"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
NEWFILE
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_with_run_tool_if_zero_line_ranges() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo", "baz"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
run-tool-if-zero-line-ranges = true
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
run-tool-if-zero-line-ranges = false
|
|
|
|
[fix.tools.tool-3]
|
|
command = [{formatter}, "--uppercase", "--split-even-length-lines"]
|
|
patterns = ["qux"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
run-tool-if-zero-line-ranges = true
|
|
"###,
|
|
));
|
|
|
|
// Initial commit.
|
|
work_dir.write_file("foo", "Foo1\nFoo2\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1\nBar2\nBar3\n");
|
|
work_dir.write_file("qux", "Qux1\nQux2\nQux3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c1"])
|
|
.success();
|
|
|
|
// Create a new commit doing a deletion of a line in foo and bar (which should
|
|
// produce empty line ranges).
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("foo", "Foo1\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1\nBar3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c2"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the second commit.
|
|
let output = work_dir.run_jj(["fix", "-s", "c2"]).success();
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: kkmpptxz 4613d090 c2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm 074c5dba c1 | (no description set)
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the first commit.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
Foo2
|
|
Foo3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Bar1
|
|
Bar2
|
|
Bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was applied to the second commit where
|
|
// run-tool-if-zero-line-ranges is true.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
FOO1
|
|
FOO3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Bar1
|
|
Bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
|
|
// Create a new commit doing a deletion of a line in qux. This commit's parent
|
|
// is c1.
|
|
work_dir.run_jj(["new", "-r", "c1"]).success();
|
|
work_dir.write_file("qux", "Qux1\nQux3\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c3"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the third commit. This command should pass, but the
|
|
// fake-formatter should fail to format qux because `--split-even-length-lines`
|
|
// requires line-ranges to be passed into the fake formatter and qux should
|
|
// not have any line ranges passed due to run-tool-if-zero-line-ranges.
|
|
// This should result in no changes to qux.
|
|
let output = work_dir.run_jj(["fix", "-s", "c3"]).success();
|
|
let output = output.normalize_stderr_with(|s| {
|
|
s.replace(formatter_path.to_str().unwrap(), "fake-formatter")
|
|
.replace(".exe", "")
|
|
});
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
qux:
|
|
error: the following required arguments were not provided:
|
|
--line-ranges <LINE_RANGES>
|
|
|
|
Usage: fake-formatter --line-ranges <LINE_RANGES> --uppercase --split-even-length-lines
|
|
|
|
For more information, try '--help'.
|
|
|
|
Warning: Fix tool `fake-formatter` exited with non-zero exit code for `qux`
|
|
Fixed 0 commits of 1 checked.
|
|
Nothing changed.
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the third commit.
|
|
let output = work_dir.run_jj(["file", "show", "qux", "-r", "c3"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Qux1
|
|
Qux3
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_with_all_lines_arg() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo", "baz", "qux"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--lowercase"]
|
|
patterns = ["bar"]
|
|
"###,
|
|
));
|
|
|
|
// Initial commit.
|
|
work_dir.write_file("foo", "Foo1\nFoo2\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1\nBar2\nBar3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c1"])
|
|
.success();
|
|
|
|
// Create new commit modifying foo and bar.
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("foo", "Foo1\nFoo2-Mod\nFoo3\n");
|
|
work_dir.write_file("bar", "Bar1\nBar2-Mod\nBar3\n");
|
|
work_dir.write_file("baz", "unmodified\n");
|
|
work_dir.write_file("qux", "newfile\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c2"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the second commit with --all-lines arg.
|
|
let output = work_dir
|
|
.run_jj(["fix", "--all-lines", "-s", "c2"])
|
|
.success();
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: kkmpptxz d875fd1f c2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm 81234495 c1 | (no description set)
|
|
Added 0 files, modified 3 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the first commit.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
Foo2
|
|
Foo3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Bar1
|
|
Bar2
|
|
Bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was applied to the second commit. And that the
|
|
// whole-file was formatted for changed files. And whole file was formatted
|
|
// for formatters that don't support line ranges.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
FOO1
|
|
FOO2-MOD
|
|
FOO3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "bar", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
bar1
|
|
bar2-mod
|
|
bar3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "baz", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
unmodified
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "qux", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
NEWFILE
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_with_line_ranges_multiple_formatters() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--split-even-length-lines"]
|
|
patterns = ["foo", "boo"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
|
|
[fix.tools.tool-2]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["foo", "boo"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
"###,
|
|
));
|
|
|
|
// Initial commit.
|
|
work_dir.write_file("foo", "ab\ncd\nefg\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c1"])
|
|
.success();
|
|
|
|
// Create new commit to deal with split even lines and uppercase.
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("foo", "abcd\nefgh\n");
|
|
work_dir.write_file("boo", "abcd\nefgh\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c2"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the second commit.
|
|
let output = work_dir.run_jj(["fix", "-s", "c2"]).success();
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: kkmpptxz 4c5f12f5 c2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm 37f1e5c6 c1 | (no description set)
|
|
Added 0 files, modified 2 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the first commit.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
ab
|
|
cd
|
|
efg
|
|
[EOF]
|
|
");
|
|
|
|
// Check that split lines was applied first and then upper was only applied to
|
|
// the second half.
|
|
let output = work_dir.run_jj(["file", "show", "foo", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
ab
|
|
cd
|
|
EF
|
|
GH
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "boo", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
AB
|
|
CD
|
|
EF
|
|
GH
|
|
[EOF]
|
|
");
|
|
}
|
|
|
|
#[test]
|
|
fn test_fix_with_line_ranges_and_include_unchanged_files_all_lines() {
|
|
let test_env = TestEnvironment::default();
|
|
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let formatter_path = assert_cmd::cargo::cargo_bin!("fake-formatter");
|
|
assert!(formatter_path.is_file());
|
|
let formatter = to_toml_value(formatter_path.to_str().unwrap());
|
|
test_env.add_config(format!(
|
|
r###"
|
|
[fix.tools.tool-1]
|
|
command = [{formatter}, "--uppercase"]
|
|
patterns = ["all()"]
|
|
line-range-arg = "--line-ranges=$first-$last"
|
|
"###,
|
|
));
|
|
|
|
// Initial commit.
|
|
work_dir.write_file("changed.txt", "Foo1\nFoo2\nFoo3\n");
|
|
work_dir.write_file("unchanged.txt", "baz1\nbaz2\nbaz3\n");
|
|
work_dir.write_file("empty.txt", "content\n");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c1"])
|
|
.success();
|
|
|
|
// Create new commit modifying changed.txt.
|
|
work_dir.run_jj(["new"]).success();
|
|
work_dir.write_file("changed.txt", "Foo1\nFoo2-Mod\nFoo3\n");
|
|
work_dir.write_file("unchanged.txt", "baz1\nbaz2\nbaz3\n");
|
|
work_dir.write_file("newfile.txt", "new\n");
|
|
work_dir.write_file("empty.txt", "");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "-r@", "c2"])
|
|
.success();
|
|
|
|
// Run `jj fix` on the second commit, including unchanged files.
|
|
let output = work_dir
|
|
.run_jj([
|
|
"fix",
|
|
"--include-unchanged-files",
|
|
"--all-lines",
|
|
"-s",
|
|
"c2",
|
|
])
|
|
.success();
|
|
insta::assert_snapshot!(output, @"
|
|
------- stderr -------
|
|
Fixed 1 commits of 1 checked.
|
|
Working copy (@) now at: kkmpptxz 1fb2bf8f c2 | (no description set)
|
|
Parent commit (@-) : qpvuntsm 42db21c2 c1 | (no description set)
|
|
Added 0 files, modified 3 files, removed 0 files
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was not applied to the first commit.
|
|
let output = work_dir.run_jj(["file", "show", "changed.txt", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
Foo1
|
|
Foo2
|
|
Foo3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "unchanged.txt", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
baz1
|
|
baz2
|
|
baz3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "empty.txt", "-r", "c1"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
content
|
|
[EOF]
|
|
");
|
|
|
|
// Check that the formatter was applied to the second commit and all lines were
|
|
// affected.
|
|
let output = work_dir.run_jj(["file", "show", "changed.txt", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @"
|
|
FOO1
|
|
FOO2-MOD
|
|
FOO3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "unchanged.txt", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
BAZ1
|
|
BAZ2
|
|
BAZ3
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "newfile.txt", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"
|
|
NEW
|
|
[EOF]
|
|
");
|
|
let output = work_dir.run_jj(["file", "show", "empty.txt", "-r", "c2"]);
|
|
insta::assert_snapshot!(output, @r"");
|
|
}
|