Git on main thread (#26573)

This moves spawning of the git subprocess to the main thread. We're not
yet
sure why, but when we spawn a process using GCD's background queues,
sub-processes like git-credential-manager fail to open windows.

This seems to be fixable either by using the main thread, or by using a
standard background thread,
but for now we use the main thread.


Release Notes:

- Git: Fix git-credential-manager

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
Conrad Irwin 2025-03-12 13:39:30 -06:00 committed by GitHub
parent 5268e74315
commit 7bca15704b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 926 additions and 713 deletions

View file

@ -401,7 +401,7 @@ impl GitStore {
if let Some((repo, path)) = this.repository_and_path_for_buffer_id(buffer_id, cx) {
let recv = repo.update(cx, |repo, cx| {
repo.set_index_text(
&path,
path,
new_index_text.as_ref().map(|rope| rope.to_string()),
cx,
)
@ -715,7 +715,7 @@ impl GitStore {
repository_handle
.update(&mut cx, |repository_handle, cx| {
repository_handle.set_index_text(
&RepoPath::from_str(&envelope.payload.path),
RepoPath::from_str(&envelope.payload.path),
envelope.payload.text,
cx,
)
@ -808,7 +808,7 @@ impl GitStore {
repository_handle
.update(&mut cx, |repository_handle, _| {
repository_handle.create_branch(&branch_name)
repository_handle.create_branch(branch_name)
})?
.await??;
@ -828,7 +828,7 @@ impl GitStore {
repository_handle
.update(&mut cx, |repository_handle, _| {
repository_handle.change_branch(&branch_name)
repository_handle.change_branch(branch_name)
})?
.await??;
@ -847,7 +847,7 @@ impl GitStore {
let commit = repository_handle
.update(&mut cx, |repository_handle, _| {
repository_handle.show(&envelope.payload.commit)
repository_handle.show(envelope.payload.commit)
})?
.await??;
Ok(proto::GitCommitDetails {
@ -876,7 +876,7 @@ impl GitStore {
repository_handle
.update(&mut cx, |repository_handle, cx| {
repository_handle.reset(&envelope.payload.commit, mode, cx)
repository_handle.reset(envelope.payload.commit, mode, cx)
})?
.await??;
Ok(proto::Ack {})
@ -1081,8 +1081,8 @@ impl Repository {
fn send_job<F, Fut, R>(&self, job: F) -> oneshot::Receiver<R>
where
F: FnOnce(GitRepo) -> Fut + 'static,
Fut: Future<Output = R> + Send + 'static,
F: FnOnce(GitRepo, AsyncApp) -> Fut + 'static,
Fut: Future<Output = R> + 'static,
R: Send + 'static,
{
self.send_keyed_job(None, job)
@ -1090,8 +1090,8 @@ impl Repository {
fn send_keyed_job<F, Fut, R>(&self, key: Option<GitJobKey>, job: F) -> oneshot::Receiver<R>
where
F: FnOnce(GitRepo) -> Fut + 'static,
Fut: Future<Output = R> + Send + 'static,
F: FnOnce(GitRepo, AsyncApp) -> Fut + 'static,
Fut: Future<Output = R> + 'static,
R: Send + 'static,
{
let (result_tx, result_rx) = futures::channel::oneshot::channel();
@ -1100,8 +1100,8 @@ impl Repository {
.unbounded_send(GitJob {
key,
job: Box::new(|cx: &mut AsyncApp| {
let job = job(git_repo);
cx.background_spawn(async move {
let job = job(git_repo, cx.clone());
cx.spawn(|_| async move {
let result = job.await;
result_tx.send(result).ok();
})
@ -1292,9 +1292,9 @@ impl Repository {
let commit = commit.to_string();
let env = self.worktree_environment(cx);
self.send_job(|git_repo| async move {
self.send_job(|git_repo, _| async move {
match git_repo {
GitRepo::Local(repo) => repo.checkout_files(&commit, &paths, &env.await),
GitRepo::Local(repo) => repo.checkout_files(commit, paths, env.await).await,
GitRepo::Remote {
project_id,
client,
@ -1322,17 +1322,17 @@ impl Repository {
pub fn reset(
&self,
commit: &str,
commit: String,
reset_mode: ResetMode,
cx: &mut App,
) -> oneshot::Receiver<Result<()>> {
let commit = commit.to_string();
let env = self.worktree_environment(cx);
self.send_job(|git_repo| async move {
self.send_job(|git_repo, _| async move {
match git_repo {
GitRepo::Local(git_repo) => {
let env = env.await;
git_repo.reset(&commit, reset_mode, &env)
git_repo.reset(commit, reset_mode, env).await
}
GitRepo::Remote {
project_id,
@ -1359,11 +1359,10 @@ impl Repository {
})
}
pub fn show(&self, commit: &str) -> oneshot::Receiver<Result<CommitDetails>> {
let commit = commit.to_string();
self.send_job(|git_repo| async move {
pub fn show(&self, commit: String) -> oneshot::Receiver<Result<CommitDetails>> {
self.send_job(|git_repo, cx| async move {
match git_repo {
GitRepo::Local(git_repository) => git_repository.show(&commit),
GitRepo::Local(git_repository) => git_repository.show(commit, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1433,9 +1432,9 @@ impl Repository {
let env = env.await;
this.update(&mut cx, |this, _| {
this.send_job(|git_repo| async move {
this.send_job(|git_repo, cx| async move {
match git_repo {
GitRepo::Local(repo) => repo.stage_paths(&entries, &env),
GitRepo::Local(repo) => repo.stage_paths(entries, env, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1504,9 +1503,9 @@ impl Repository {
let env = env.await;
this.update(&mut cx, |this, _| {
this.send_job(|git_repo| async move {
this.send_job(|git_repo, cx| async move {
match git_repo {
GitRepo::Local(repo) => repo.unstage_paths(&entries, &env),
GitRepo::Local(repo) => repo.unstage_paths(entries, env, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1587,17 +1586,11 @@ impl Repository {
cx: &mut App,
) -> oneshot::Receiver<Result<()>> {
let env = self.worktree_environment(cx);
self.send_job(|git_repo| async move {
self.send_job(|git_repo, cx| async move {
match git_repo {
GitRepo::Local(repo) => {
let env = env.await;
repo.commit(
message.as_ref(),
name_and_email
.as_ref()
.map(|(name, email)| (name.as_ref(), email.as_ref())),
&env,
)
repo.commit(message, name_and_email, env, cx).await
}
GitRepo::Remote {
project_id,
@ -1634,12 +1627,12 @@ impl Repository {
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
let env = self.worktree_environment(cx);
self.send_job(move |git_repo| async move {
self.send_job(move |git_repo, cx| async move {
match git_repo {
GitRepo::Local(git_repository) => {
let askpass = AskPassSession::new(&executor, askpass).await?;
let env = env.await;
git_repository.fetch(askpass, &env)
git_repository.fetch(askpass, env, cx).await
}
GitRepo::Remote {
project_id,
@ -1685,12 +1678,21 @@ impl Repository {
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
let env = self.worktree_environment(cx);
self.send_job(move |git_repo| async move {
self.send_job(move |git_repo, cx| async move {
match git_repo {
GitRepo::Local(git_repository) => {
let env = env.await;
let askpass = AskPassSession::new(&executor, askpass).await?;
git_repository.push(&branch, &remote, options, askpass, &env)
git_repository
.push(
branch.to_string(),
remote.to_string(),
options,
askpass,
env,
cx,
)
.await
}
GitRepo::Remote {
project_id,
@ -1740,12 +1742,14 @@ impl Repository {
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
let env = self.worktree_environment(cx);
self.send_job(move |git_repo| async move {
self.send_job(move |git_repo, cx| async move {
match git_repo {
GitRepo::Local(git_repository) => {
let askpass = AskPassSession::new(&executor, askpass).await?;
let env = env.await;
git_repository.pull(&branch, &remote, askpass, &env)
git_repository
.pull(branch.to_string(), remote.to_string(), askpass, env, cx)
.await
}
GitRepo::Remote {
project_id,
@ -1781,18 +1785,17 @@ impl Repository {
fn set_index_text(
&self,
path: &RepoPath,
path: RepoPath,
content: Option<String>,
cx: &mut App,
) -> oneshot::Receiver<anyhow::Result<()>> {
let path = path.clone();
let env = self.worktree_environment(cx);
self.send_keyed_job(
Some(GitJobKey::WriteIndex(path.clone())),
|git_repo| async move {
|git_repo, cx| async move {
match git_repo {
GitRepo::Local(repo) => repo.set_index_text(&path, content, &env.await),
GitRepo::Local(repo) => repo.set_index_text(path, content, env.await, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1819,11 +1822,9 @@ impl Repository {
&self,
branch_name: Option<String>,
) -> oneshot::Receiver<Result<Vec<Remote>>> {
self.send_job(|repo| async move {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => {
git_repository.get_remotes(branch_name.as_deref())
}
GitRepo::Local(git_repository) => git_repository.get_remotes(branch_name, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1854,9 +1855,13 @@ impl Repository {
}
pub fn branches(&self) -> oneshot::Receiver<Result<Vec<Branch>>> {
self.send_job(|repo| async move {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => git_repository.branches(),
GitRepo::Local(git_repository) => {
let git_repository = git_repository.clone();
cx.background_spawn(async move { git_repository.branches().await })
.await
}
GitRepo::Remote {
project_id,
client,
@ -1884,9 +1889,9 @@ impl Repository {
}
pub fn diff(&self, diff_type: DiffType, _cx: &App) -> oneshot::Receiver<Result<String>> {
self.send_job(|repo| async move {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => git_repository.diff(diff_type),
GitRepo::Local(git_repository) => git_repository.diff(diff_type, cx).await,
GitRepo::Remote {
project_id,
client,
@ -1916,11 +1921,12 @@ impl Repository {
})
}
pub fn create_branch(&self, branch_name: &str) -> oneshot::Receiver<Result<()>> {
let branch_name = branch_name.to_owned();
self.send_job(|repo| async move {
pub fn create_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => git_repository.create_branch(&branch_name),
GitRepo::Local(git_repository) => {
git_repository.create_branch(branch_name, cx).await
}
GitRepo::Remote {
project_id,
client,
@ -1942,11 +1948,12 @@ impl Repository {
})
}
pub fn change_branch(&self, branch_name: &str) -> oneshot::Receiver<Result<()>> {
let branch_name = branch_name.to_owned();
self.send_job(|repo| async move {
pub fn change_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => git_repository.change_branch(&branch_name),
GitRepo::Local(git_repository) => {
git_repository.change_branch(branch_name, cx).await
}
GitRepo::Remote {
project_id,
client,
@ -1969,9 +1976,9 @@ impl Repository {
}
pub fn check_for_pushed_commits(&self) -> oneshot::Receiver<Result<Vec<SharedString>>> {
self.send_job(|repo| async move {
self.send_job(|repo, cx| async move {
match repo {
GitRepo::Local(git_repository) => git_repository.check_for_pushed_commit(),
GitRepo::Local(git_repository) => git_repository.check_for_pushed_commit(cx).await,
GitRepo::Remote {
project_id,
client,