ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: suggest root:"<path>" if cwd-relative path is not in workspace

Closes #3216
This commit is contained in:
Yuya Nishihara 2024-04-08 20:52:45 +09:00
parent d45cf30250
commit 18f94bbb8b
3 changed files with 71 additions and 9 deletions

View file

@ -19,14 +19,14 @@ use std::{error, io, iter, str};
use itertools::Itertools as _;
use jj_lib::backend::BackendError;
use jj_lib::fileset::{FilesetParseError, FilesetParseErrorKind};
use jj_lib::fileset::{FilePatternParseError, FilesetParseError, FilesetParseErrorKind};
use jj_lib::git::{GitConfigParseError, GitExportError, GitImportError, GitRemoteManagementError};
use jj_lib::gitignore::GitIgnoreError;
use jj_lib::op_heads_store::OpHeadResolutionError;
use jj_lib::op_store::OpStoreError;
use jj_lib::op_walk::OpsetEvaluationError;
use jj_lib::repo::{CheckOutCommitError, EditCommitError, RepoLoaderError, RewriteRootCommit};
use jj_lib::repo_path::FsPathParseError;
use jj_lib::repo_path::{FsPathParseError, RepoPathBuf};
use jj_lib::revset::{
RevsetEvaluationError, RevsetParseError, RevsetParseErrorKind, RevsetResolutionError,
};
@ -539,15 +539,27 @@ impl From<GitIgnoreError> for CommandError {
fn find_source_parse_error_hint(err: &dyn error::Error) -> Option<String> {
let source = err.source()?;
// TODO: For FilePatternParseError, suggest "root:<path>" if the user
// input looks like repo-relative path #3216.
if let Some(source) = source.downcast_ref() {
file_pattern_parse_error_hint(source)
} else if let Some(source) = source.downcast_ref() {
string_pattern_parse_error_hint(source)
} else {
None
}
}
fn file_pattern_parse_error_hint(err: &FilePatternParseError) -> Option<String> {
match err {
FilePatternParseError::InvalidKind(_) => None,
// Suggest root:"<path>" if input can be parsed as repo-relative path
FilePatternParseError::FsPath(e) => RepoPathBuf::from_relative_path(&e.input)
.ok()
.map(|path| format!(r#"Consider using root:{path:?} to specify repo-relative path"#)),
FilePatternParseError::RelativePath(_) => None,
FilePatternParseError::GlobPattern(_) => None,
}
}
fn string_pattern_parse_error_hint(err: &StringPatternParseError) -> Option<String> {
match err {
StringPatternParseError::InvalidKind(_) => {

View file

@ -230,13 +230,60 @@ fn test_bad_path() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
let subdir = repo_path.join("dir");
std::fs::create_dir_all(&subdir).unwrap();
test_env.add_config("ui.allow-filesets = true");
// cwd == workspace_root
let stderr = test_env.jj_cmd_failure(&repo_path, &["cat", "../out"]);
insta::assert_snapshot!(stderr.replace('\\', "/"), @r###"
Error: Path "../out" is not in the repo "."
Caused by: Invalid component ".." in repo-relative path "../out"
Error: Failed to parse fileset: Invalid file pattern
Caused by:
1: --> 1:1
|
1 | ../out
| ^----^
|
= Invalid file pattern
2: Path "../out" is not in the repo "."
3: Invalid component ".." in repo-relative path "../out"
"###);
// cwd != workspace_root, can't be parsed as repo-relative path
let stderr = test_env.jj_cmd_failure(&subdir, &["cat", "../.."]);
insta::assert_snapshot!(stderr.replace('\\', "/"), @r###"
Error: Failed to parse fileset: Invalid file pattern
Caused by:
1: --> 1:1
|
1 | ../..
| ^---^
|
= Invalid file pattern
2: Path "../.." is not in the repo "../"
3: Invalid component ".." in repo-relative path "../"
"###);
// cwd != workspace_root, can be parsed as repo-relative path
let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["cat", "-Rrepo", "out"]);
insta::assert_snapshot!(stderr.replace('\\', "/"), @r###"
Error: Failed to parse fileset: Invalid file pattern
Caused by:
1: --> 1:1
|
1 | out
| ^-^
|
= Invalid file pattern
2: Path "out" is not in the repo "repo"
3: Invalid component ".." in repo-relative path "../out"
Hint: Consider using root:"out" to specify repo-relative path
"###);
test_env.add_config("ui.allow-filesets = false");
// If fileset/pattern syntax is disabled, no hint should be generated
let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["cat", "-Rrepo", "out"]);
insta::assert_snapshot!(stderr.replace('\\', "/"), @r###"
Error: Path "out" is not in the repo "repo"

View file

@ -452,9 +452,12 @@ pub enum RelativePathParseError {
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[error(r#"Path "{input}" is not in the repo "{base}""#)]
pub struct FsPathParseError {
base: Box<Path>,
input: Box<Path>,
source: RelativePathParseError,
/// Repository or workspace root path relative to the `cwd`.
pub base: Box<Path>,
/// Input path without normalization.
pub input: Box<Path>,
/// Source error.
pub source: RelativePathParseError,
}
fn is_valid_repo_path_component_str(value: &str) -> bool {