cli: add basic support for 'jj log PATH'

In the current implementation, tree is diffed twice if both PATH and -p
are specified. If this adds significant cost, we'll need to reimplement
it without using a revset abstraction (or maybe adjust revset/graph API.)
This commit is contained in:
Yuya Nishihara 2022-09-13 16:26:23 +09:00
parent 873b5ba771
commit c9c3735faf
4 changed files with 56 additions and 5 deletions

View File

@ -138,6 +138,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj log` now accepts a `--reversed` option, which will show older commits
first.
* `jj log` now accepts file paths.
* `jj obslog` now accepts `-p`/`--patch` option, which will show the diff
compared to the previous version of the change.

View File

@ -123,7 +123,7 @@ commit to any other commit using `jj move`.
## Status
The tool is quite feature-complete, but some important features like (the
equivalent of) `git blame` and `git log <paths>` are not yet supported. There
equivalent of) `git blame` are not yet supported. There
are also several performance bugs. It's also likely that workflows and setups
different from what I personally use are not well supported. For example,
pull-request workflows currently require too many manual steps.

View File

@ -1319,6 +1319,8 @@ struct LogArgs {
default_value = "remote_branches().. | (remote_branches()..)-"
)]
revisions: String,
/// Show commits modifying the given paths
paths: Vec<String>,
/// Show revisions in the opposite order (older revisions first)
#[clap(long)]
reversed: bool,
@ -3114,8 +3116,12 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C
let repo = workspace_command.repo();
let workspace_id = workspace_command.workspace_id();
let checkout_id = repo.view().get_checkout(&workspace_id);
let matcher = EverythingMatcher;
let revset = revset_expression.evaluate(repo.as_repo_ref(), Some(&workspace_id))?;
let matcher = matcher_from_values(ui, workspace_command.workspace_root(), &args.paths)?;
let mut revset = revset_expression.evaluate(repo.as_repo_ref(), Some(&workspace_id))?;
if !args.paths.is_empty() {
revset = revset::filter_by_diff(repo.as_repo_ref(), matcher.as_ref(), revset);
}
let store = repo.store();
let diff_format = (args.patch || args.diff_format.git || args.diff_format.summary)
.then(|| diff_format_for(ui, &args.diff_format));
@ -3190,7 +3196,7 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C
formatter.as_mut(),
&workspace_command,
&commit,
&matcher,
matcher.as_ref(),
diff_format,
)?;
}
@ -3216,7 +3222,7 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C
formatter,
&workspace_command,
&commit,
&matcher,
matcher.as_ref(),
diff_format,
)?;
}

View File

@ -155,3 +155,46 @@ fn test_log_reversed() {
second
"###);
}
#[test]
fn test_log_filtered_by_path() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "first"]);
test_env.jj_cmd_success(&repo_path, &["new", "-m", "second"]);
std::fs::write(repo_path.join("file1"), "foo\nbar\n").unwrap();
std::fs::write(repo_path.join("file2"), "baz\n").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "description", "file1"]);
insta::assert_snapshot!(stdout, @r###"
@ second
o first
~
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "description", "file2"]);
insta::assert_snapshot!(stdout, @r###"
@ second
~
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "description", "-s", "file1"]);
insta::assert_snapshot!(stdout, @r###"
@ second
| M file1
o first
~ A file1
"###);
let stdout = test_env.jj_cmd_success(
&repo_path,
&["log", "-T", "description", "-s", "file2", "--no-graph"],
);
insta::assert_snapshot!(stdout, @r###"
second
A file2
"###);
}