mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
Add command to use ai to generate commit msg
This commit is contained in:
parent
51d8f96348
commit
c98ca240a6
7 changed files with 91 additions and 2 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -765,6 +765,7 @@ dependencies = [
|
|||
"filetreelist",
|
||||
"fuzzy-matcher",
|
||||
"gh-emoji",
|
||||
"git2-summarize",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"log",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ keywords = ["git", "gui", "cli", "terminal", "ui"]
|
|||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
asyncgit = { path = "./asyncgit", version = "0.24", default-features = false }
|
||||
git2-summarize = { path = "./git2-summarize", version = "0.1"}
|
||||
backtrace = "0.3"
|
||||
bitflags = "1.3"
|
||||
bugreport = "0.5"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
error::Error,
|
||||
error::Result,
|
||||
hash,
|
||||
sync::{get_stashes, repository::repo},
|
||||
sync::{get_stashes, repository::repo, utils::bytes2string},
|
||||
};
|
||||
use easy_cast::Conv;
|
||||
use git2::{
|
||||
|
|
@ -398,6 +398,35 @@ fn raw_diff_to_file_diff(
|
|||
Ok(res.into_inner())
|
||||
}
|
||||
|
||||
///
|
||||
pub fn unified_stage_diff(repo_path: &RepoPath) -> Result<String> {
|
||||
scope_time!("unified_stage_diff");
|
||||
|
||||
let repo = repo(repo_path)?;
|
||||
|
||||
let diff = get_diff_raw(&repo, "*", true, false, None)?;
|
||||
|
||||
let mut output = String::with_capacity(32);
|
||||
|
||||
diff.print(DiffFormat::Patch, |_delta, _hunk, line| {
|
||||
let prefix = match line.origin_value() {
|
||||
git2::DiffLineType::Addition => "+",
|
||||
git2::DiffLineType::Deletion => "-",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
output.push_str(&format!(
|
||||
"{}{}",
|
||||
prefix,
|
||||
bytes2string(line.content()).unwrap()
|
||||
));
|
||||
|
||||
true
|
||||
})?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
const fn is_newline(c: char) -> bool {
|
||||
c == '\n' || c == '\r'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub use config::{
|
|||
get_config_string, untracked_files_config,
|
||||
ShowUntrackedFilesConfig,
|
||||
};
|
||||
pub use diff::get_diff_commit;
|
||||
pub use diff::{get_diff_commit, unified_stage_diff};
|
||||
pub use git2::BranchType;
|
||||
pub use hooks::{
|
||||
hooks_commit_msg, hooks_post_commit, hooks_pre_commit, HookResult,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ pub struct CommitComponent {
|
|||
commit_msg_history_idx: usize,
|
||||
options: SharedOptions,
|
||||
verify: bool,
|
||||
open_ai_token: Option<String>,
|
||||
}
|
||||
|
||||
const FIRST_LINE_LIMIT: usize = 50;
|
||||
|
|
@ -90,6 +91,7 @@ impl CommitComponent {
|
|||
commit_msg_history_idx: 0,
|
||||
options,
|
||||
verify: true,
|
||||
open_ai_token: std::env::var("OPENAI_API_KEY").ok(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -352,6 +354,30 @@ impl CommitComponent {
|
|||
self.input.set_text(signed_msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_summarize(&mut self) -> Result<()> {
|
||||
use std::result::Result::Ok;
|
||||
|
||||
if let Some(api_key) = self.open_ai_token.as_ref() {
|
||||
let mut unified_diff =
|
||||
sync::unified_stage_diff(&self.repo.borrow())?;
|
||||
|
||||
while unified_diff.len() > 3500 {
|
||||
unified_diff.pop();
|
||||
}
|
||||
|
||||
match git2_summarize::git_diff_summarize(
|
||||
&api_key,
|
||||
&unified_diff,
|
||||
FIRST_LINE_LIMIT,
|
||||
) {
|
||||
Ok(msg) => self.input.set_text(msg),
|
||||
Err(e) => bail!(e),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn toggle_verify(&mut self) {
|
||||
self.verify = !self.verify;
|
||||
}
|
||||
|
|
@ -539,6 +565,14 @@ impl Component for CommitComponent {
|
|||
self.options.borrow().has_commit_msg_history(),
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::commit_msg_summarize(
|
||||
&self.key_config,
|
||||
),
|
||||
self.open_ai_token.is_some(),
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
visibility_blocking(self)
|
||||
|
|
@ -596,6 +630,15 @@ impl Component for CommitComponent {
|
|||
self.key_config.keys.toggle_signoff,
|
||||
) {
|
||||
self.signoff_commit();
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.commit_msg_summarize,
|
||||
) {
|
||||
try_or_popup!(
|
||||
self,
|
||||
"commit summary error:",
|
||||
self.commit_summarize()
|
||||
);
|
||||
}
|
||||
// stop key event propagation
|
||||
return Ok(EventState::Consumed);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ pub struct KeysList {
|
|||
pub view_submodule_parent: GituiKeyEvent,
|
||||
pub update_submodule: GituiKeyEvent,
|
||||
pub commit_history_next: GituiKeyEvent,
|
||||
pub commit_msg_summarize: GituiKeyEvent,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -209,6 +210,7 @@ impl Default for KeysList {
|
|||
view_submodule_parent: GituiKeyEvent::new(KeyCode::Char('p'), KeyModifiers::empty()),
|
||||
update_submodule: GituiKeyEvent::new(KeyCode::Char('u'), KeyModifiers::empty()),
|
||||
commit_history_next: GituiKeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL),
|
||||
commit_msg_summarize: GituiKeyEvent::new(KeyCode::Char('g'), KeyModifiers::CONTROL),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -965,6 +965,19 @@ pub mod commands {
|
|||
CMD_GROUP_COMMIT_POPUP,
|
||||
)
|
||||
}
|
||||
pub fn commit_msg_summarize(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Summarize [{}]",
|
||||
key_config
|
||||
.get_hint(key_config.keys.commit_msg_summarize),
|
||||
),
|
||||
"use openai chat-gpt to generate commit message",
|
||||
CMD_GROUP_COMMIT_POPUP,
|
||||
)
|
||||
}
|
||||
pub fn commit_enter(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue