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

cli: add kind:pattern syntax to branch delete/forget commands

The parse rule is lax compared to revset. We could require the pattern to be
quoted, but that would mean glob patterns have to be quoted like 'glob:"foo*"'.
This commit is contained in:
Yuya Nishihara 2023-10-20 05:17:58 +09:00
parent 65c033e5d5
commit c3f167e6cc
4 changed files with 57 additions and 34 deletions

View file

@ -56,6 +56,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `branches()`/`remote_branches()`/`author()`/`committer()`/`description()`
revsets now support glob matching.
* `jj branch delete`/`forget` now support [string pattern
syntax](docs/revsets.md#string-patterns).
### Fixed bugs
* Updating the working copy to a commit where a file that's currently ignored

View file

@ -10,7 +10,7 @@ use jj_lib::git;
use jj_lib::op_store::RefTarget;
use jj_lib::repo::Repo;
use jj_lib::revset::{self, RevsetExpression};
use jj_lib::str_util::StringPattern;
use jj_lib::str_util::{StringPattern, StringPatternParseError};
use jj_lib::view::View;
use crate::cli_util::{user_error, user_error_with_hint, CommandError, CommandHelper, RevisionArg};
@ -54,9 +54,13 @@ pub struct BranchCreateArgs {
/// next push.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchDeleteArgs {
/// The branches to delete.
#[arg(required_unless_present_any(& ["glob"]))]
names: Vec<String>,
/// The branches to delete
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// https://github.com/martinvonz/jj/blob/main/docs/revsets.md#string-patterns.
#[arg(required_unless_present_any(&["glob"]), value_parser = parse_name_pattern)]
pub names: Vec<StringPattern>,
/// A glob pattern indicating branches to delete.
#[arg(long, value_parser = StringPattern::glob)]
@ -95,9 +99,13 @@ pub struct BranchListArgs {
/// recreated on future pulls if it still exists in the remote.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchForgetArgs {
/// The branches to forget.
#[arg(required_unless_present_any(& ["glob"]))]
pub names: Vec<String>,
/// The branches to forget
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// https://github.com/martinvonz/jj/blob/main/docs/revsets.md#string-patterns.
#[arg(required_unless_present_any(&["glob"]), value_parser = parse_name_pattern)]
pub names: Vec<StringPattern>,
/// A glob pattern indicating branches to forget.
#[arg(long, value_parser = StringPattern::glob)]
@ -276,6 +284,14 @@ fn cmd_branch_set(
Ok(())
}
fn parse_name_pattern(src: &str) -> Result<StringPattern, StringPatternParseError> {
if let Some((kind, pat)) = src.split_once(':') {
StringPattern::from_str_kind(pat, kind)
} else {
Ok(StringPattern::exact(src))
}
}
fn find_local_branches(
view: &View,
name_patterns: &[StringPattern],
@ -331,11 +347,7 @@ fn cmd_branch_delete(
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let name_patterns = itertools::chain(
args.names.iter().map(StringPattern::exact),
args.glob.iter().cloned(),
)
.collect_vec();
let name_patterns = [&args.names[..], &args.glob[..]].concat();
let names = find_local_branches(view, &name_patterns)?;
let mut tx =
workspace_command.start_transaction(&format!("delete {}", make_branch_term(&names)));
@ -357,11 +369,7 @@ fn cmd_branch_forget(
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let name_patterns = itertools::chain(
args.names.iter().map(StringPattern::exact),
args.glob.iter().cloned(),
)
.collect_vec();
let name_patterns = [&args.names[..], &args.glob[..]].concat();
let names = find_forgettable_branches(view, &name_patterns)?;
let mut tx =
workspace_command.start_transaction(&format!("forget {}", make_branch_term(&names)));

View file

@ -313,6 +313,8 @@ fn cmd_git_fetch(
"fetch from git remote(s) {}",
remotes.iter().join(",")
));
// TODO: maybe this should error out if the pattern contained meta
// characters and is not prefixed with "glob:".
let branches = args.branch.iter().map(|b| b.as_str()).collect_vec();
for remote in remotes {
let stats = with_remote_callbacks(ui, |cb| {

View file

@ -101,6 +101,12 @@ fn test_branch_forget_glob() {
insta::assert_snapshot!(stderr, @r###"
Forgot 2 branches.
"###);
test_env.jj_cmd_ok(&repo_path, &["undo"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "forget", "glob:foo-[1-3]"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Forgot 2 branches.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ bar-2 foo-4 230dd059e1b0
000000000000
@ -110,9 +116,7 @@ fn test_branch_forget_glob() {
// multiple glob patterns, shouldn't produce an error.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&[
"branch", "forget", "foo-4", "--glob", "foo-*", "--glob", "foo-*",
],
&["branch", "forget", "foo-4", "--glob", "foo-*", "glob:foo-*"],
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
@ -122,9 +126,9 @@ fn test_branch_forget_glob() {
"###);
// Malformed glob
let stderr = test_env.jj_cmd_cli_error(&repo_path, &["branch", "forget", "--glob", "foo-[1-3"]);
let stderr = test_env.jj_cmd_cli_error(&repo_path, &["branch", "forget", "glob:foo-[1-3"]);
insta::assert_snapshot!(stderr, @r###"
error: invalid value 'foo-[1-3' for '--glob <GLOB>': Pattern syntax error near position 4: invalid range pattern
error: invalid value 'glob:foo-[1-3' for '[NAMES]...': Pattern syntax error near position 4: invalid range pattern
For more information, try '--help'.
"###);
@ -132,13 +136,7 @@ fn test_branch_forget_glob() {
// We get an error if none of the globs match anything
let stderr = test_env.jj_cmd_failure(
&repo_path,
&[
"branch",
"forget",
"--glob=bar*",
"--glob=baz*",
"--glob=boom*",
],
&["branch", "forget", "glob:bar*", "glob:baz*", "--glob=boom*"],
);
insta::assert_snapshot!(stderr, @r###"
Error: No matching branches for patterns: baz*, boom*
@ -181,6 +179,12 @@ fn test_branch_delete_glob() {
insta::assert_snapshot!(stderr, @r###"
Deleted 2 branches.
"###);
test_env.jj_cmd_ok(&repo_path, &["undo"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "delete", "glob:foo-[1-3]"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Deleted 2 branches.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ bar-2 foo-1@origin foo-3@origin foo-4 6fbf398c2d59
000000000000
@ -197,9 +201,7 @@ fn test_branch_delete_glob() {
// multiple glob patterns, shouldn't produce an error.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&[
"branch", "delete", "foo-4", "--glob", "foo-*", "--glob", "foo-*",
],
&["branch", "delete", "foo-4", "--glob", "foo-*", "glob:foo-*"],
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
@ -227,9 +229,17 @@ fn test_branch_delete_glob() {
"###);
// Malformed glob
let stderr = test_env.jj_cmd_cli_error(&repo_path, &["branch", "delete", "--glob", "foo-[1-3"]);
let stderr = test_env.jj_cmd_cli_error(&repo_path, &["branch", "delete", "glob:foo-[1-3"]);
insta::assert_snapshot!(stderr, @r###"
error: invalid value 'foo-[1-3' for '--glob <GLOB>': Pattern syntax error near position 4: invalid range pattern
error: invalid value 'glob:foo-[1-3' for '[NAMES]...': Pattern syntax error near position 4: invalid range pattern
For more information, try '--help'.
"###);
// Unknown pattern kind
let stderr = test_env.jj_cmd_cli_error(&repo_path, &["branch", "forget", "whatever:branch"]);
insta::assert_snapshot!(stderr, @r###"
error: invalid value 'whatever:branch' for '[NAMES]...': Invalid string pattern kind "whatever"
For more information, try '--help'.
"###);