Enable collaborating editing of the commit message input inside the git panel (#24130)

https://github.com/user-attachments/assets/200b88b8-249a-4841-97cd-fda8365efd00

Now all users in the collab/ssh session can edit the commit input
collaboratively, observing each others' changes live.

A real `.git/COMMIT_EDITMSG` file is opened, which automatically enables
its syntax highlight, but its original context is never used or saved on
disk — this way we avoid stale commit messages from previous commits
that git places there.

A caveat: previous version put some effort into preserving unfinished
commit messages on repo swtiches, but this version would not do that
— instead, it will be blank on startup, and use whatever
`.git/COMMIT_EDITMSG` contents on repo switch

Release Notes:

- N/A

---------

Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Kirill Bulatov 2025-02-03 18:11:13 +02:00 committed by GitHub
parent 6b48a6e690
commit a864168c27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 595 additions and 364 deletions

View file

@ -1,6 +1,6 @@
use crate::status::FileStatus;
use crate::GitHostingProviderRegistry;
use crate::{blame::Blame, status::GitStatus};
use crate::{GitHostingProviderRegistry, COMMIT_MESSAGE};
use anyhow::{anyhow, Context as _, Result};
use collections::{HashMap, HashSet};
use git2::BranchType;
@ -62,7 +62,7 @@ pub trait GitRepository: Send + Sync {
/// If any of the paths were previously staged but do not exist in HEAD, they will be removed from the index.
fn unstage_paths(&self, paths: &[RepoPath]) -> Result<()>;
fn commit(&self, message: &str, name_and_email: Option<(&str, &str)>) -> Result<()>;
fn commit(&self, name_and_email: Option<(&str, &str)>) -> Result<()>;
}
impl std::fmt::Debug for dyn GitRepository {
@ -283,14 +283,22 @@ impl GitRepository for RealGitRepository {
Ok(())
}
fn commit(&self, message: &str, name_and_email: Option<(&str, &str)>) -> Result<()> {
fn commit(&self, name_and_email: Option<(&str, &str)>) -> Result<()> {
let working_directory = self
.repository
.lock()
.workdir()
.context("failed to read git work directory")?
.to_path_buf();
let mut args = vec!["commit", "--quiet", "-m", message];
let commit_file = self.dot_git_dir().join(*COMMIT_MESSAGE);
let commit_file_path = commit_file.to_string_lossy();
let mut args = vec![
"commit",
"--quiet",
"-F",
commit_file_path.as_ref(),
"--cleanup=strip",
];
let author = name_and_email.map(|(name, email)| format!("{name} <{email}>"));
if let Some(author) = author.as_deref() {
args.push("--author");
@ -450,7 +458,7 @@ impl GitRepository for FakeGitRepository {
unimplemented!()
}
fn commit(&self, _message: &str, _name_and_email: Option<(&str, &str)>) -> Result<()> {
fn commit(&self, _name_and_email: Option<(&str, &str)>) -> Result<()> {
unimplemented!()
}
}