Skip to content

Commit 2245ab5

Browse files
authored
Merge pull request #9470 from gitbutlerapp/branch-rename-and-commit-enhancements
feat(reword): return new commit ID and message on commit
2 parents 6d9ad4a + 11232b8 commit 2245ab5

File tree

4 files changed

+73
-36
lines changed

4 files changed

+73
-36
lines changed

crates/but-action/src/generate.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub struct StructuredOutput {
9696
pub async fn branch_name(
9797
client: &Client<OpenAIConfig>,
9898
commit_messages: &[String],
99+
diffs: &[String],
99100
existing_branch_names: &[String],
100101
) -> anyhow::Result<String> {
101102
let system_message =
@@ -104,18 +105,27 @@ pub async fn branch_name(
104105
"Generate a concise and descriptive branch name based on the provided commit messages.
105106
Keep the branch name short, ideally under 50 characters. Only user lowercase letters, numbers, and hyphens.
106107
Don't use other special characters or spaces.
107-
The branche name should reflect the main content of the commit messages.
108-
109-
Try to make the branch name unique and noticeably different from existing branch names.
108+
<important_notes>
109+
The branch name should reflect the main content of the commit messages and, if available, change diffs.
110+
Try to make the branch name unique and noticeably different from existing branch names.
111+
Do not use any of the existing branch names.
112+
</important_notes>
110113
111-
Here are the existing branch names:
114+
<exisiting_branch_names>
112115
{}
116+
</existing_branch_names>
113117
114-
Here are the commit messages:
115-
116-
{}",
118+
<commit_messages>
119+
{}
120+
</commit_messages>
121+
122+
<diffs>
123+
{}
124+
</diffs>
125+
",
117126
existing_branch_names.join(",\n"),
118-
commit_messages.join("\n==================\n")
127+
commit_messages.join("\n==================\n"),
128+
diffs.join("\n==================\n")
119129
);
120130

121131
let schema = schema_for!(GenerateBranchNameOutput);

crates/but-action/src/rename_branch.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
use std::vec;
2+
13
use async_openai::{Client, config::OpenAIConfig};
24
use but_workspace::StackId;
35
use gitbutler_command_context::CommandContext;
4-
use gix::bstr::BString;
56

67
use crate::workflow::{self, Workflow};
78

89
pub struct RenameBranchParams {
910
pub commit_id: gix::ObjectId,
10-
pub commit_message: BString,
11+
pub commit_message: String,
1112
pub stack_id: StackId,
1213
pub current_branch_name: String,
1314
pub existing_branch_names: Vec<String>,
@@ -26,9 +27,16 @@ pub async fn rename_branch(
2627
current_branch_name,
2728
existing_branch_names,
2829
} = parameters;
29-
let commit_messages = vec![commit_message.to_string()];
30+
31+
let repo = &ctx.gix_repo_for_merging_non_persisting()?;
32+
let changes = but_core::diff::ui::commit_changes_by_worktree_dir(repo, commit_id)?;
33+
let diff = changes.try_as_unidiff_string(repo, ctx.app_settings().context_lines)?;
34+
let diffs = vec![diff];
35+
36+
let commit_messages = vec![commit_message];
3037
let branch_name =
31-
crate::generate::branch_name(client, &commit_messages, &existing_branch_names).await?;
38+
crate::generate::branch_name(client, &commit_messages, &diffs, &existing_branch_names)
39+
.await?;
3240
let normalized_branch_name = gitbutler_reference::normalize_branch_name(&branch_name)?;
3341

3442
let update = gitbutler_branch_actions::stack::update_branch_name(
@@ -52,8 +60,8 @@ pub async fn rename_branch(
5260
}),
5361
workflow::Trigger::Snapshot(trigger_id),
5462
status,
55-
vec![commit_id],
56-
vec![commit_id],
63+
vec![],
64+
vec![],
5765
None,
5866
)
5967
.persist(ctx)

crates/but-action/src/reword.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ pub struct CommitEvent {
2020
pub trigger: Uuid,
2121
}
2222

23-
pub async fn commit(client: &Client<OpenAIConfig>, event: CommitEvent) -> anyhow::Result<()> {
23+
pub async fn commit(
24+
client: &Client<OpenAIConfig>,
25+
event: CommitEvent,
26+
) -> anyhow::Result<Option<(gix::ObjectId, String)>> {
2427
let ctx = &mut CommandContext::open(&event.project, event.app_settings)?;
2528
let repo = &ctx.gix_repo_for_merging_non_persisting()?;
2629
let changes = but_core::diff::ui::commit_changes_by_worktree_dir(repo, event.commit_id)?;
@@ -49,10 +52,8 @@ pub async fn commit(client: &Client<OpenAIConfig>, event: CommitEvent) -> anyhow
4952
Err(e) => workflow::Status::Failed(e.to_string()),
5053
};
5154

52-
let output_commits = match &result {
53-
Ok(new_commit_id) => vec![new_commit_id.to_gix()],
54-
Err(_) => vec![],
55-
};
55+
let new_commit_id = result.map(|id| id.to_gix()).ok();
56+
let output_commits = new_commit_id.map(|id| vec![id]).unwrap_or_default();
5657

5758
Workflow::new(
5859
workflow::Kind::Reword(Some(workflow::RewordOutcome {
@@ -73,7 +74,7 @@ pub async fn commit(client: &Client<OpenAIConfig>, event: CommitEvent) -> anyhow
7374
.persist(ctx)
7475
.ok();
7576

76-
Ok(())
77+
Ok(new_commit_id.map(|id| (id, message)))
7778
}
7879

7980
fn stacks(ctx: &CommandContext) -> anyhow::Result<Vec<StackEntry>> {

crates/but/src/command/claude.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use std::io::{self, Read};
23
use std::str::FromStr;
34

@@ -7,7 +8,6 @@ use but_action::{ActionHandler, OpenAiProvider, Source, reword::CommitEvent};
78
use but_db::ClaudeCodeSession;
89
use but_hunk_assignment::HunkAssignmentRequest;
910
use but_settings::AppSettings;
10-
use but_workspace::ui::Commit;
1111
use but_workspace::{HunkHeader, StackId};
1212
use gitbutler_branch::BranchCreateRequest;
1313
use gitbutler_command_context::CommandContext;
@@ -120,10 +120,13 @@ pub(crate) async fn handle_stop() -> anyhow::Result<ClaudeHookOutput> {
120120
// Trigger commit message generation for newly created commits
121121
// TODO: Maybe this can be done in the main app process i.e. the GitButler GUI, if avaialbe
122122
// Alternatively, and probably better - we could spawn a new process to do this
123+
123124
if let Some(openai_client) =
124125
OpenAiProvider::with(None).and_then(|provider| provider.client().ok())
125126
{
126127
for branch in &outcome.updated_branches {
128+
let mut commit_message_mapping = HashMap::new();
129+
127130
let elegibility = is_branch_eligible_for_rename(&defer, &stacks, branch)?;
128131

129132
for commit in &branch.new_commits {
@@ -137,24 +140,39 @@ pub(crate) async fn handle_stop() -> anyhow::Result<ClaudeHookOutput> {
137140
app_settings: defer.ctx.app_settings().clone(),
138141
trigger: id,
139142
};
140-
but_action::reword::commit(&openai_client, commit_event)
143+
let reword_result = but_action::reword::commit(&openai_client, commit_event)
141144
.await
142-
.ok();
145+
.ok()
146+
.unwrap_or_default();
147+
148+
// Update the commit mapping with the new commit ID
149+
if let Some(reword_result) = reword_result {
150+
commit_message_mapping.insert(commit_id, reword_result);
151+
}
143152
}
144153
}
145154

146155
match elegibility {
147-
RenameEligibility::Eligible(commit) => {
148-
let params = RenameBranchParams {
149-
commit_id: commit.id,
150-
commit_message: commit.message,
151-
stack_id: branch.stack_id,
152-
current_branch_name: branch.branch_name.clone(),
153-
existing_branch_names: existing_branch_names.clone(),
154-
};
155-
but_action::rename_branch::rename_branch(defer.ctx, &openai_client, params, id)
156+
RenameEligibility::Eligible { commit_id } => {
157+
let reword_result = commit_message_mapping.get(&commit_id).cloned();
158+
159+
if let Some((commit_id, commit_message)) = reword_result {
160+
let params = RenameBranchParams {
161+
commit_id,
162+
commit_message,
163+
stack_id: branch.stack_id,
164+
current_branch_name: branch.branch_name.clone(),
165+
existing_branch_names: existing_branch_names.clone(),
166+
};
167+
but_action::rename_branch::rename_branch(
168+
defer.ctx,
169+
&openai_client,
170+
params,
171+
id,
172+
)
156173
.await
157174
.ok();
175+
}
158176
}
159177
RenameEligibility::NotEligible => {
160178
// Do nothing, branch is not eligible for renaming
@@ -172,7 +190,7 @@ pub(crate) async fn handle_stop() -> anyhow::Result<ClaudeHookOutput> {
172190
}
173191

174192
enum RenameEligibility {
175-
Eligible(Box<Commit>),
193+
Eligible { commit_id: gix::ObjectId },
176194
NotEligible,
177195
}
178196

@@ -231,9 +249,9 @@ fn is_branch_eligible_for_rename(
231249
but_workspace::ui::PushStatus::CompletelyUnpushed
232250
)
233251
{
234-
Ok(RenameEligibility::Eligible(Box::new(
235-
branch_details.commits[0].clone(),
236-
)))
252+
Ok(RenameEligibility::Eligible {
253+
commit_id: branch_head.tip,
254+
})
237255
} else {
238256
Ok(RenameEligibility::NotEligible)
239257
}

0 commit comments

Comments
 (0)