git_ui: Add support for generating commit messages with an LLM (#26227)

This PR finishes up the support for generating commit messages using an
LLM.

We're shelling out to `git diff` to get the diff text, as it seemed more
efficient than attempting to reconstruct the diff ourselves from our
internal Git state.


https://github.com/user-attachments/assets/9bcf30a7-7a08-4f49-a753-72a5d954bddd

Release Notes:

- Git Beta: Added support for generating commit messages using a
language model.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Marshall Bowers 2025-03-06 14:47:52 -05:00 committed by GitHub
parent d1cec209d4
commit b8a8b9c699
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 233 additions and 52 deletions

View file

@ -49,7 +49,8 @@ actions!(
Pull,
Fetch,
Commit,
ExpandCommitEditor
ExpandCommitEditor,
GenerateCommitMessage
]
);
action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);

View file

@ -217,6 +217,14 @@ pub trait GitRepository: Send + Sync {
/// returns a list of remote branches that contain HEAD
fn check_for_pushed_commit(&self) -> Result<Vec<SharedString>>;
/// Run git diff
fn diff(&self, diff: DiffType) -> Result<String>;
}
pub enum DiffType {
HeadToIndex,
HeadToWorktree,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)]
@ -577,6 +585,28 @@ impl GitRepository for RealGitRepository {
)
}
fn diff(&self, diff: DiffType) -> Result<String> {
let working_directory = self.working_directory()?;
let args = match diff {
DiffType::HeadToIndex => Some("--staged"),
DiffType::HeadToWorktree => None,
};
let output = new_std_command(&self.git_binary_path)
.current_dir(&working_directory)
.args(["diff"])
.args(args)
.output()?;
if !output.status.success() {
return Err(anyhow!(
"Failed to run git diff:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
fn stage_paths(&self, paths: &[RepoPath]) -> Result<()> {
let working_directory = self.working_directory()?;
@ -1048,6 +1078,10 @@ impl GitRepository for FakeGitRepository {
fn check_for_pushed_commit(&self) -> Result<Vec<SharedString>> {
unimplemented!()
}
fn diff(&self, _diff: DiffType) -> Result<String> {
unimplemented!()
}
}
fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {