forked from mirrors/jj
cli: default to log when no subcommand is provided
This is a convenience optimization to improve the default user experience, since `jj log` is a frequently run command. Accessing the help information explicitly still follows normal CLI conventions, and instructions are displayed appropriately if the user happens to make a mistake. Discoverability should not be adversely harmed. Note that this behavior mirrors what Sapling does [2], where `sl` will display the smartlog by default. [1] https://github.com/clap-rs/clap/issues/975 [2] https://sapling-scm.com/docs/overview/smartlog
This commit is contained in:
parent
e056474fe8
commit
6c627fb30d
7 changed files with 107 additions and 13 deletions
|
@ -79,6 +79,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* `jj git fetch` and `jj git push` will now use the single defined remote even if it is not named "origin".
|
||||
|
||||
* `jj` with no subcommand now defaults to `jj log` instead of showing help. This
|
||||
command can be overridden by setting `ui.default-command`.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
* Modify/delete conflicts now include context lines
|
||||
|
|
|
@ -124,6 +124,16 @@ Which elements can be colored is not yet documented, but see
|
|||
the [default color configuration](https://github.com/martinvonz/jj/blob/main/src/config/colors.toml)
|
||||
for some examples of what's possible.
|
||||
|
||||
### Default command
|
||||
|
||||
When `jj` is run with no explicit subcommand, the value of the
|
||||
`ui.default-command` setting will used instead. Possible values are any valid
|
||||
subcommand name, subcommand alias, or user-defined alias (defaults to `"log"`).
|
||||
|
||||
```toml
|
||||
ui.default-command = "log"
|
||||
```
|
||||
|
||||
### Diff format
|
||||
|
||||
```toml
|
||||
|
|
|
@ -2028,6 +2028,49 @@ impl ValueParserFactory for RevisionArg {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_default_command(
|
||||
ui: &mut Ui,
|
||||
config: &config::Config,
|
||||
app: &Command,
|
||||
string_args: &mut Vec<String>,
|
||||
) -> Result<(), CommandError> {
|
||||
const PRIORITY_FLAGS: &[&str] = &["help", "--help", "-h", "--version", "-V"];
|
||||
|
||||
let has_priority_flag = string_args
|
||||
.iter()
|
||||
.any(|arg| PRIORITY_FLAGS.contains(&arg.as_str()));
|
||||
if has_priority_flag {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let app_clone = app
|
||||
.clone()
|
||||
.allow_external_subcommands(true)
|
||||
.ignore_errors(true);
|
||||
let matches = app_clone.try_get_matches_from(string_args.clone()).ok();
|
||||
|
||||
if let Some(matches) = matches {
|
||||
if matches.subcommand_name().is_none() {
|
||||
if config.get_string("ui.default-command").is_err() {
|
||||
writeln!(
|
||||
ui.hint(),
|
||||
"Hint: Use `jj -h` for a list of available commands."
|
||||
)?;
|
||||
writeln!(
|
||||
ui.hint(),
|
||||
"Set the config `ui.default-command = \"log\"` to disable this message."
|
||||
)?;
|
||||
}
|
||||
let default_command = config
|
||||
.get_string("ui.default-command")
|
||||
.unwrap_or("log".to_string());
|
||||
// Insert the default command directly after the path to the binary.
|
||||
string_args.insert(1, default_command);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_aliases(
|
||||
config: &config::Config,
|
||||
app: &Command,
|
||||
|
@ -2119,6 +2162,7 @@ fn handle_early_args(
|
|||
}
|
||||
|
||||
pub fn expand_args(
|
||||
ui: &mut Ui,
|
||||
app: &Command,
|
||||
args_os: ArgsOs,
|
||||
config: &config::Config,
|
||||
|
@ -2132,6 +2176,7 @@ pub fn expand_args(
|
|||
}
|
||||
}
|
||||
|
||||
resolve_default_command(ui, config, app, &mut string_args)?;
|
||||
resolve_aliases(config, app, &string_args)
|
||||
}
|
||||
|
||||
|
@ -2299,7 +2344,7 @@ impl CliRunner {
|
|||
layered_configs.read_user_config()?;
|
||||
let config = layered_configs.merge();
|
||||
ui.reset(&config)?;
|
||||
let string_args = expand_args(&self.app, std::env::args_os(), &config)?;
|
||||
let string_args = expand_args(ui, &self.app, std::env::args_os(), &config)?;
|
||||
let (matches, args) = parse_args(
|
||||
ui,
|
||||
&self.app,
|
||||
|
|
|
@ -3474,9 +3474,7 @@ fn cmd_sparse(ui: &mut Ui, command: &CommandHelper, args: &SparseArgs) -> Result
|
|||
|
||||
pub fn default_app() -> Command {
|
||||
let app: Command = Commands::augment_subcommands(Args::command());
|
||||
app.arg_required_else_help(true)
|
||||
.subcommand_required(true)
|
||||
.version(env!("JJ_VERSION"))
|
||||
app.version(env!("JJ_VERSION"))
|
||||
}
|
||||
|
||||
pub fn run_command(
|
||||
|
|
|
@ -51,6 +51,11 @@
|
|||
"description": "Whether to allow initializing a repo with the native backend",
|
||||
"default": false
|
||||
},
|
||||
"default-command": {
|
||||
"type": "string",
|
||||
"description": "Default command to run when no explicit command is given",
|
||||
"default": "log"
|
||||
},
|
||||
"default-revset": {
|
||||
"type": "string",
|
||||
"description": "Default set of revisions to show when no explicit revset is given for jj log and similar commands",
|
||||
|
|
|
@ -69,7 +69,7 @@ fn test_alias_bad_name() {
|
|||
insta::assert_snapshot!(stderr, @r###"
|
||||
error: unrecognized subcommand 'foo.'
|
||||
|
||||
Usage: jj [OPTIONS] <COMMAND>
|
||||
Usage: jj [OPTIONS] [COMMAND]
|
||||
|
||||
For more information, try '--help'.
|
||||
"###);
|
||||
|
@ -86,7 +86,7 @@ fn test_alias_calls_unknown_command() {
|
|||
insta::assert_snapshot!(stderr, @r###"
|
||||
error: unrecognized subcommand 'nonexistent'
|
||||
|
||||
Usage: jj [OPTIONS] <COMMAND>
|
||||
Usage: jj [OPTIONS] [COMMAND]
|
||||
|
||||
For more information, try '--help'.
|
||||
"###);
|
||||
|
@ -123,7 +123,7 @@ fn test_alias_calls_help() {
|
|||
|
||||
To get started, see the tutorial at https://github.com/martinvonz/jj/blob/main/docs/tutorial.md.
|
||||
|
||||
Usage: jj [OPTIONS] <COMMAND>
|
||||
Usage: jj [OPTIONS] [COMMAND]
|
||||
"###);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,25 @@ fn test_non_utf8_arg() {
|
|||
#[test]
|
||||
fn test_no_subcommand() {
|
||||
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");
|
||||
|
||||
let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &[]);
|
||||
insta::assert_snapshot!(stderr.lines().next().unwrap(), @"Jujutsu (An experimental VCS)");
|
||||
// Outside of a repo.
|
||||
let stderr = test_env.jj_cmd_failure(test_env.env_root(), &[]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Hint: Use `jj -h` for a list of available commands.
|
||||
Set the config `ui.default-command = "log"` to disable this message.
|
||||
Error: There is no jj repo in "."
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_cli_error(test_env.env_root(), &["-R."]);
|
||||
insta::assert_snapshot!(stderr.lines().next().unwrap(), @"error: 'jj' requires a subcommand but one was not provided");
|
||||
test_env.add_config(r#"ui.default-command="log""#);
|
||||
let stderr = test_env.jj_cmd_failure(test_env.env_root(), &[]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: There is no jj repo in "."
|
||||
"###);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--help"]);
|
||||
insta::assert_snapshot!(stdout.lines().next().unwrap(), @"Jujutsu (An experimental VCS)");
|
||||
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--version"]);
|
||||
let sanitized = stdout.replace(|c: char| c.is_ascii_hexdigit(), "?");
|
||||
|
@ -62,8 +75,28 @@ fn test_no_subcommand() {
|
|||
"{sanitized}"
|
||||
);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--help"]);
|
||||
insta::assert_snapshot!(stdout.lines().next().unwrap(), @"Jujutsu (An experimental VCS)");
|
||||
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["-R", "repo"]);
|
||||
assert_eq!(stdout, test_env.jj_cmd_success(&repo_path, &["log"]));
|
||||
|
||||
// Inside of a repo.
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &[]);
|
||||
assert_eq!(stdout, test_env.jj_cmd_success(&repo_path, &["log"]));
|
||||
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["-T", "show"]);
|
||||
let stdout = stdout.lines().skip(2).join("\n");
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
│ Author: Test User <test.user@example.com> (2001-02-03 04:05:07.000 +07:00)
|
||||
│ Committer: Test User <test.user@example.com> (2001-02-03 04:05:07.000 +07:00)
|
||||
│
|
||||
│ (no description set)
|
||||
│
|
||||
◉ Commit ID: 0000000000000000000000000000000000000000
|
||||
Change ID: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
Author: <> (1970-01-01 00:00:00.000 +00:00)
|
||||
Committer: <> (1970-01-01 00:00:00.000 +00:00)
|
||||
|
||||
(no description set)
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue