feat: set message for multiple revsets

Allows to set the revision message of multiple revisions by passing
them as arguments. This only supports simple revisions, such as `@`,
`@-`, `xs`, and so on. Comple revisions such as `@..@-` are not
supported.

Fixes: #5
This commit is contained in:
2026-04-05 23:02:14 +02:00
parent 3da214ae4c
commit 8e01d435b1
17 changed files with 438 additions and 168 deletions

View File

@@ -54,10 +54,12 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
/// - User cancels the workflow
/// - Repository operation fails
/// - Message validation fails
pub async fn run(&self) -> Result<(), Error> {
pub async fn run_for_revset(&self, revset: &str) -> Result<(), Error> {
if !self.executor.is_repository().await? {
return Err(Error::NotARepository);
}
// For future reference
let _existing_desc = self.executor.get_description(revset).await.ok();
let commit_type = self.type_selection()?;
loop {
let scope = self.scope_input()?;
@@ -67,7 +69,7 @@ impl<J: JjExecutor, P: Prompter> CommitWorkflow<J, P> {
match self.preview_and_confirm(commit_type, scope, description, breaking_change, body) {
Ok(conventional_commit) => {
self.executor
.describe(&conventional_commit.to_string())
.describe(revset, &conventional_commit.to_string())
.await?;
return Ok(());
}
@@ -205,7 +207,7 @@ mod tests {
async fn workflow_returns_not_a_repository() {
let mock = MockJjExecutor::new().with_is_repo_response(Ok(false));
let workflow = CommitWorkflow::new(mock);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::NotARepository));
}
@@ -215,7 +217,7 @@ mod tests {
async fn workflow_returns_repository_error() {
let mock = MockJjExecutor::new().with_is_repo_response(Err(Error::NotARepository));
let workflow = CommitWorkflow::new(mock);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::NotARepository));
}
@@ -285,7 +287,7 @@ mod tests {
// Verify the mock behaves as expected
assert!(mock.is_repository().await.is_ok());
assert!(mock.describe("test").await.is_err());
assert!(mock.describe("@", "test").await.is_err());
// Also test with a working mock
let working_mock = MockJjExecutor::new();
@@ -323,7 +325,7 @@ mod tests {
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
// Run the workflow - should succeed
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_ok());
}
@@ -334,7 +336,7 @@ mod tests {
let mock_prompts = MockPrompts::new().with_error(Error::Cancelled);
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Cancelled));
@@ -353,7 +355,7 @@ mod tests {
.with_confirm(false); // User cancels at confirmation
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Cancelled));
@@ -384,7 +386,7 @@ mod tests {
// Clone before moving into workflow so we can inspect emitted messages after
let mock_prompts_handle = mock_prompts.clone();
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
// Should succeed after the retry
assert!(
@@ -421,7 +423,7 @@ mod tests {
));
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::InvalidScope(_)));
@@ -440,7 +442,7 @@ mod tests {
));
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::InvalidDescription(_)));
@@ -479,7 +481,7 @@ mod tests {
MockJjExecutor::new().with_is_repo_response(Ok(true)),
mock_prompts,
);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_ok(), "Failed for commit type: {:?}", commit_type);
}
}
@@ -503,7 +505,7 @@ mod tests {
mock_prompts,
);
{
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_ok());
}
@@ -521,7 +523,7 @@ mod tests {
mock_prompts,
);
{
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(result.is_ok());
}
}
@@ -621,7 +623,7 @@ mod tests {
.with_confirm(true);
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(
result.is_ok(),
@@ -716,7 +718,7 @@ mod tests {
.with_confirm(true);
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(
result.is_ok(),
@@ -748,7 +750,7 @@ mod tests {
.with_confirm(true);
let workflow = CommitWorkflow::with_prompts(mock_executor, mock_prompts);
let result: Result<(), Error> = workflow.run().await;
let result: Result<(), Error> = workflow.run_for_revset("@").await;
assert!(
result.is_ok(),