Git activity indicator (#28204)
Closes #26182 Release Notes: - Added an activity indicator for long-running git commands. --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
4f9f443452
commit
e3830d2ef5
11 changed files with 625 additions and 487 deletions
|
@ -11,13 +11,22 @@ use language::{BinaryStatus, LanguageRegistry, LanguageServerId};
|
||||||
use project::{
|
use project::{
|
||||||
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
|
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
|
||||||
ProjectEnvironmentEvent,
|
ProjectEnvironmentEvent,
|
||||||
|
git_store::{GitStoreEvent, Repository},
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp::Reverse, fmt::Write, path::Path, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
cmp::Reverse,
|
||||||
|
fmt::Write,
|
||||||
|
path::Path,
|
||||||
|
sync::Arc,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
use ui::{ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
|
use ui::{ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
|
||||||
use util::truncate_and_trailoff;
|
use util::truncate_and_trailoff;
|
||||||
use workspace::{StatusItemView, Workspace, item::ItemHandle};
|
use workspace::{StatusItemView, Workspace, item::ItemHandle};
|
||||||
|
|
||||||
|
const GIT_OPERATION_DELAY: Duration = Duration::from_millis(0);
|
||||||
|
|
||||||
actions!(activity_indicator, [ShowErrorMessage]);
|
actions!(activity_indicator, [ShowErrorMessage]);
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
@ -105,6 +114,15 @@ impl ActivityIndicator {
|
||||||
)
|
)
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
cx.subscribe(
|
||||||
|
&project.read(cx).git_store().clone(),
|
||||||
|
|_, _, event: &GitStoreEvent, cx| match event {
|
||||||
|
project::git_store::GitStoreEvent::JobsUpdated => cx.notify(),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
|
||||||
if let Some(auto_updater) = auto_updater.as_ref() {
|
if let Some(auto_updater) = auto_updater.as_ref() {
|
||||||
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
||||||
}
|
}
|
||||||
|
@ -285,6 +303,34 @@ impl ActivityIndicator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_job = self
|
||||||
|
.project
|
||||||
|
.read(cx)
|
||||||
|
.active_repository(cx)
|
||||||
|
.map(|r| r.read(cx))
|
||||||
|
.and_then(Repository::current_job);
|
||||||
|
// Show any long-running git command
|
||||||
|
if let Some(job_info) = current_job {
|
||||||
|
if Instant::now() - job_info.start >= GIT_OPERATION_DELAY {
|
||||||
|
return Some(Content {
|
||||||
|
icon: Some(
|
||||||
|
Icon::new(IconName::ArrowCircle)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.with_animation(
|
||||||
|
"arrow-circle",
|
||||||
|
Animation::new(Duration::from_secs(2)).repeat(),
|
||||||
|
|icon, delta| {
|
||||||
|
icon.transform(Transformation::rotate(percentage(delta)))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
message: job_info.message.into(),
|
||||||
|
on_click: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show any language server installation info.
|
// Show any language server installation info.
|
||||||
let mut downloading = SmallVec::<[_; 3]>::new();
|
let mut downloading = SmallVec::<[_; 3]>::new();
|
||||||
let mut checking_for_update = SmallVec::<[_; 3]>::new();
|
let mut checking_for_update = SmallVec::<[_; 3]>::new();
|
||||||
|
|
|
@ -226,7 +226,8 @@ impl MessageEditor {
|
||||||
|
|
||||||
let thread = self.thread.clone();
|
let thread = self.thread.clone();
|
||||||
let context_store = self.context_store.clone();
|
let context_store = self.context_store.clone();
|
||||||
let checkpoint = self.project.read(cx).git_store().read(cx).checkpoint(cx);
|
let git_store = self.project.read(cx).git_store().clone();
|
||||||
|
let checkpoint = git_store.update(cx, |git_store, cx| git_store.checkpoint(cx));
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let checkpoint = checkpoint.await.ok();
|
let checkpoint = checkpoint.await.ok();
|
||||||
|
|
|
@ -471,11 +471,11 @@ impl Thread {
|
||||||
cx.emit(ThreadEvent::CheckpointChanged);
|
cx.emit(ThreadEvent::CheckpointChanged);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let project = self.project.read(cx);
|
let git_store = self.project().read(cx).git_store().clone();
|
||||||
let restore = project
|
let restore = git_store.update(cx, |git_store, cx| {
|
||||||
.git_store()
|
git_store.restore_checkpoint(checkpoint.git_checkpoint.clone(), cx)
|
||||||
.read(cx)
|
});
|
||||||
.restore_checkpoint(checkpoint.git_checkpoint.clone(), cx);
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let result = restore.await;
|
let result = restore.await;
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
|
@ -506,11 +506,11 @@ impl Thread {
|
||||||
};
|
};
|
||||||
|
|
||||||
let git_store = self.project.read(cx).git_store().clone();
|
let git_store = self.project.read(cx).git_store().clone();
|
||||||
let final_checkpoint = git_store.read(cx).checkpoint(cx);
|
let final_checkpoint = git_store.update(cx, |git_store, cx| git_store.checkpoint(cx));
|
||||||
cx.spawn(async move |this, cx| match final_checkpoint.await {
|
cx.spawn(async move |this, cx| match final_checkpoint.await {
|
||||||
Ok(final_checkpoint) => {
|
Ok(final_checkpoint) => {
|
||||||
let equal = git_store
|
let equal = git_store
|
||||||
.read_with(cx, |store, cx| {
|
.update(cx, |store, cx| {
|
||||||
store.compare_checkpoints(
|
store.compare_checkpoints(
|
||||||
pending_checkpoint.git_checkpoint.clone(),
|
pending_checkpoint.git_checkpoint.clone(),
|
||||||
final_checkpoint.clone(),
|
final_checkpoint.clone(),
|
||||||
|
@ -522,7 +522,7 @@ impl Thread {
|
||||||
|
|
||||||
if equal {
|
if equal {
|
||||||
git_store
|
git_store
|
||||||
.read_with(cx, |store, cx| {
|
.update(cx, |store, cx| {
|
||||||
store.delete_checkpoint(pending_checkpoint.git_checkpoint, cx)
|
store.delete_checkpoint(pending_checkpoint.git_checkpoint, cx)
|
||||||
})?
|
})?
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -533,7 +533,7 @@ impl Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
git_store
|
git_store
|
||||||
.read_with(cx, |store, cx| {
|
.update(cx, |store, cx| {
|
||||||
store.delete_checkpoint(final_checkpoint, cx)
|
store.delete_checkpoint(final_checkpoint, cx)
|
||||||
})?
|
})?
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -1650,10 +1650,10 @@ impl Thread {
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|repo| {
|
.map(|repo| {
|
||||||
repo.read_with(cx, |repo, _| {
|
repo.update(cx, |repo, _| {
|
||||||
let current_branch =
|
let current_branch =
|
||||||
repo.branch.as_ref().map(|branch| branch.name.to_string());
|
repo.branch.as_ref().map(|branch| branch.name.to_string());
|
||||||
repo.send_job(|state, _| async move {
|
repo.send_job(None, |state, _| async move {
|
||||||
let RepositoryState::Local { backend, .. } = state else {
|
let RepositoryState::Local { backend, .. } = state else {
|
||||||
return GitState {
|
return GitState {
|
||||||
remote_url: None,
|
remote_url: None,
|
||||||
|
|
|
@ -6866,7 +6866,11 @@ async fn test_remote_git_branches(
|
||||||
|
|
||||||
assert_eq!(branches_b, branches_set);
|
assert_eq!(branches_b, branches_set);
|
||||||
|
|
||||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
cx_b.update(|cx| {
|
||||||
|
repo_b.update(cx, |repository, _cx| {
|
||||||
|
repository.change_branch(new_branch.to_string())
|
||||||
|
})
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -6892,18 +6896,18 @@ async fn test_remote_git_branches(
|
||||||
|
|
||||||
// Also try creating a new branch
|
// Also try creating a new branch
|
||||||
cx_b.update(|cx| {
|
cx_b.update(|cx| {
|
||||||
repo_b
|
repo_b.update(cx, |repository, _cx| {
|
||||||
.read(cx)
|
repository.create_branch("totally-new-branch".to_string())
|
||||||
.create_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx_b.update(|cx| {
|
cx_b.update(|cx| {
|
||||||
repo_b
|
repo_b.update(cx, |repository, _cx| {
|
||||||
.read(cx)
|
repository.change_branch("totally-new-branch".to_string())
|
||||||
.change_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -283,7 +283,7 @@ async fn test_ssh_collaboration_git_branches(
|
||||||
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
||||||
|
|
||||||
let branches_b = cx_b
|
let branches_b = cx_b
|
||||||
.update(|cx| repo_b.read(cx).branches())
|
.update(|cx| repo_b.update(cx, |repo_b, _cx| repo_b.branches()))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -297,7 +297,11 @@ async fn test_ssh_collaboration_git_branches(
|
||||||
|
|
||||||
assert_eq!(&branches_b, &branches_set);
|
assert_eq!(&branches_b, &branches_set);
|
||||||
|
|
||||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
cx_b.update(|cx| {
|
||||||
|
repo_b.update(cx, |repo_b, _cx| {
|
||||||
|
repo_b.change_branch(new_branch.to_string())
|
||||||
|
})
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -325,18 +329,18 @@ async fn test_ssh_collaboration_git_branches(
|
||||||
|
|
||||||
// Also try creating a new branch
|
// Also try creating a new branch
|
||||||
cx_b.update(|cx| {
|
cx_b.update(|cx| {
|
||||||
repo_b
|
repo_b.update(cx, |repo_b, _cx| {
|
||||||
.read(cx)
|
repo_b.create_branch("totally-new-branch".to_string())
|
||||||
.create_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx_b.update(|cx| {
|
cx_b.update(|cx| {
|
||||||
repo_b
|
repo_b.update(cx, |repo_b, _cx| {
|
||||||
.read(cx)
|
repo_b.change_branch("totally-new-branch".to_string())
|
||||||
.change_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -436,7 +436,9 @@ impl GitBlame {
|
||||||
}
|
}
|
||||||
let buffer_edits = self.buffer.update(cx, |buffer, _| buffer.subscribe());
|
let buffer_edits = self.buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
let snapshot = self.buffer.read(cx).snapshot();
|
let snapshot = self.buffer.read(cx).snapshot();
|
||||||
let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx);
|
let blame = self.project.update(cx, |project, cx| {
|
||||||
|
project.blame_buffer(&self.buffer, None, cx)
|
||||||
|
});
|
||||||
let provider_registry = GitHostingProviderRegistry::default_global(cx);
|
let provider_registry = GitHostingProviderRegistry::default_global(cx);
|
||||||
|
|
||||||
self.task = cx.spawn(async move |this, cx| {
|
self.task = cx.spawn(async move |this, cx| {
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl BranchList {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let all_branches_request = repository
|
let all_branches_request = repository
|
||||||
.clone()
|
.clone()
|
||||||
.map(|repository| repository.read(cx).branches());
|
.map(|repository| repository.update(cx, |repository, _| repository.branches()));
|
||||||
|
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let mut all_branches = all_branches_request
|
let mut all_branches = all_branches_request
|
||||||
|
@ -202,10 +202,15 @@ impl BranchListDelegate {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
cx.spawn(async move |_, cx| {
|
cx.spawn(async move |_, cx| {
|
||||||
cx.update(|cx| repo.read(cx).create_branch(new_branch_name.to_string()))?
|
repo.update(cx, |repo, _| {
|
||||||
|
repo.create_branch(new_branch_name.to_string())
|
||||||
|
})?
|
||||||
.await??;
|
.await??;
|
||||||
cx.update(|cx| repo.read(cx).change_branch(new_branch_name.to_string()))?
|
repo.update(cx, |repo, _| {
|
||||||
|
repo.change_branch(new_branch_name.to_string())
|
||||||
|
})?
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.detach_and_prompt_err("Failed to create branch", window, cx, |e, _, _| {
|
.detach_and_prompt_err("Failed to create branch", window, cx, |e, _, _| {
|
||||||
|
@ -359,10 +364,12 @@ impl PickerDelegate for BranchListDelegate {
|
||||||
.ok_or_else(|| anyhow!("No active repository"))?
|
.ok_or_else(|| anyhow!("No active repository"))?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let cx = cx.to_async();
|
let mut cx = cx.to_async();
|
||||||
|
|
||||||
anyhow::Ok(async move {
|
anyhow::Ok(async move {
|
||||||
cx.update(|cx| repo.read(cx).change_branch(branch.name.to_string()))?
|
repo.update(&mut cx, |repo, _| {
|
||||||
|
repo.change_branch(branch.name.to_string())
|
||||||
|
})?
|
||||||
.await?
|
.await?
|
||||||
})
|
})
|
||||||
})??;
|
})??;
|
||||||
|
|
|
@ -53,10 +53,8 @@ use project::{
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings as _, SettingsStore};
|
use settings::{Settings as _, SettingsStore};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::{collections::HashSet, sync::Arc, time::Duration, usize};
|
use std::{collections::HashSet, sync::Arc, time::Duration, usize};
|
||||||
use strum::{IntoEnumIterator, VariantNames};
|
use strum::{IntoEnumIterator, VariantNames};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@ -64,7 +62,7 @@ use ui::{
|
||||||
Checkbox, ContextMenu, ElevationIndex, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
|
Checkbox, ContextMenu, ElevationIndex, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt, maybe, post_inc};
|
use util::{ResultExt, TryFutureExt, maybe};
|
||||||
use workspace::AppState;
|
use workspace::AppState;
|
||||||
|
|
||||||
use notifications::status_toast::{StatusToast, ToastIcon};
|
use notifications::status_toast::{StatusToast, ToastIcon};
|
||||||
|
@ -232,8 +230,6 @@ struct PendingOperation {
|
||||||
op_id: usize,
|
op_id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteOperations = Rc<RefCell<HashSet<u32>>>;
|
|
||||||
|
|
||||||
// computed state related to how to render scrollbars
|
// computed state related to how to render scrollbars
|
||||||
// one per axis
|
// one per axis
|
||||||
// on render we just read this off the panel
|
// on render we just read this off the panel
|
||||||
|
@ -290,8 +286,6 @@ impl ScrollbarProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GitPanel {
|
pub struct GitPanel {
|
||||||
remote_operation_id: u32,
|
|
||||||
pending_remote_operations: RemoteOperations,
|
|
||||||
pub(crate) active_repository: Option<Entity<Repository>>,
|
pub(crate) active_repository: Option<Entity<Repository>>,
|
||||||
pub(crate) commit_editor: Entity<Editor>,
|
pub(crate) commit_editor: Entity<Editor>,
|
||||||
conflicted_count: usize,
|
conflicted_count: usize,
|
||||||
|
@ -327,17 +321,6 @@ pub struct GitPanel {
|
||||||
_settings_subscription: Subscription,
|
_settings_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RemoteOperationGuard {
|
|
||||||
id: u32,
|
|
||||||
pending_remote_operations: RemoteOperations,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for RemoteOperationGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.pending_remote_operations.borrow_mut().remove(&self.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_PANEL_EDITOR_LINES: usize = 6;
|
const MAX_PANEL_EDITOR_LINES: usize = 6;
|
||||||
|
|
||||||
pub(crate) fn commit_message_editor(
|
pub(crate) fn commit_message_editor(
|
||||||
|
@ -416,7 +399,7 @@ impl GitPanel {
|
||||||
) => {
|
) => {
|
||||||
this.schedule_update(*full_scan, window, cx);
|
this.schedule_update(*full_scan, window, cx);
|
||||||
}
|
}
|
||||||
GitStoreEvent::RepositoryUpdated(_, _, _) => {}
|
|
||||||
GitStoreEvent::RepositoryAdded(_) | GitStoreEvent::RepositoryRemoved(_) => {
|
GitStoreEvent::RepositoryAdded(_) | GitStoreEvent::RepositoryRemoved(_) => {
|
||||||
this.schedule_update(false, window, cx);
|
this.schedule_update(false, window, cx);
|
||||||
}
|
}
|
||||||
|
@ -427,6 +410,8 @@ impl GitPanel {
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
GitStoreEvent::RepositoryUpdated(_, _, _) => {}
|
||||||
|
GitStoreEvent::JobsUpdated => {}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -458,8 +443,6 @@ impl GitPanel {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut git_panel = Self {
|
let mut git_panel = Self {
|
||||||
pending_remote_operations: Default::default(),
|
|
||||||
remote_operation_id: 0,
|
|
||||||
active_repository,
|
active_repository,
|
||||||
commit_editor,
|
commit_editor,
|
||||||
conflicted_count: 0,
|
conflicted_count: 0,
|
||||||
|
@ -671,16 +654,6 @@ impl GitPanel {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_remote_operation(&mut self) -> RemoteOperationGuard {
|
|
||||||
let id = post_inc(&mut self.remote_operation_id);
|
|
||||||
self.pending_remote_operations.borrow_mut().insert(id);
|
|
||||||
|
|
||||||
RemoteOperationGuard {
|
|
||||||
id,
|
|
||||||
pending_remote_operations: self.pending_remote_operations.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize(&mut self, cx: &mut Context<Self>) {
|
fn serialize(&mut self, cx: &mut Context<Self>) {
|
||||||
let width = self.width;
|
let width = self.width;
|
||||||
self.pending_serialization = cx.background_spawn(
|
self.pending_serialization = cx.background_spawn(
|
||||||
|
@ -1743,7 +1716,6 @@ impl GitPanel {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
telemetry::event!("Git Fetched");
|
telemetry::event!("Git Fetched");
|
||||||
let guard = self.start_remote_operation();
|
|
||||||
let askpass = self.askpass_delegate("git fetch", window, cx);
|
let askpass = self.askpass_delegate("git fetch", window, cx);
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
window
|
window
|
||||||
|
@ -1751,7 +1723,6 @@ impl GitPanel {
|
||||||
let fetch = repo.update(cx, |repo, cx| repo.fetch(askpass, cx))?;
|
let fetch = repo.update(cx, |repo, cx| repo.fetch(askpass, cx))?;
|
||||||
|
|
||||||
let remote_message = fetch.await?;
|
let remote_message = fetch.await?;
|
||||||
drop(guard);
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
let action = RemoteAction::Fetch;
|
let action = RemoteAction::Fetch;
|
||||||
match remote_message {
|
match remote_message {
|
||||||
|
@ -1883,16 +1854,11 @@ impl GitPanel {
|
||||||
this.askpass_delegate(format!("git pull {}", remote.name), window, cx)
|
this.askpass_delegate(format!("git pull {}", remote.name), window, cx)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let guard = this
|
|
||||||
.update(cx, |this, _| this.start_remote_operation())
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let pull = repo.update(cx, |repo, cx| {
|
let pull = repo.update(cx, |repo, cx| {
|
||||||
repo.pull(branch.name.clone(), remote.name.clone(), askpass, cx)
|
repo.pull(branch.name.clone(), remote.name.clone(), askpass, cx)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let remote_message = pull.await?;
|
let remote_message = pull.await?;
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
let action = RemoteAction::Pull(remote);
|
let action = RemoteAction::Pull(remote);
|
||||||
this.update(cx, |this, cx| match remote_message {
|
this.update(cx, |this, cx| match remote_message {
|
||||||
|
@ -1954,10 +1920,6 @@ impl GitPanel {
|
||||||
this.askpass_delegate(format!("git push {}", remote.name), window, cx)
|
this.askpass_delegate(format!("git push {}", remote.name), window, cx)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let guard = this
|
|
||||||
.update(cx, |this, _| this.start_remote_operation())
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let push = repo.update(cx, |repo, cx| {
|
let push = repo.update(cx, |repo, cx| {
|
||||||
repo.push(
|
repo.push(
|
||||||
branch.name.clone(),
|
branch.name.clone(),
|
||||||
|
@ -1969,7 +1931,6 @@ impl GitPanel {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let remote_output = push.await?;
|
let remote_output = push.await?;
|
||||||
drop(guard);
|
|
||||||
|
|
||||||
let action = RemoteAction::Push(branch.name, remote);
|
let action = RemoteAction::Push(branch.name, remote);
|
||||||
this.update(cx, |this, cx| match remote_output {
|
this.update(cx, |this, cx| match remote_output {
|
||||||
|
@ -2590,20 +2551,6 @@ impl GitPanel {
|
||||||
workspace.add_item_to_center(Box::new(editor), window, cx);
|
workspace.add_item_to_center(Box::new(editor), window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_spinner(&self) -> Option<impl IntoElement> {
|
|
||||||
(!self.pending_remote_operations.borrow().is_empty()).then(|| {
|
|
||||||
Icon::new(IconName::ArrowCircle)
|
|
||||||
.size(IconSize::XSmall)
|
|
||||||
.color(Color::Info)
|
|
||||||
.with_animation(
|
|
||||||
"arrow-circle",
|
|
||||||
Animation::new(Duration::from_secs(2)).repeat(),
|
|
||||||
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_commit(&self) -> bool {
|
pub fn can_commit(&self) -> bool {
|
||||||
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
|
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
|
||||||
}
|
}
|
||||||
|
@ -2832,12 +2779,10 @@ impl GitPanel {
|
||||||
if !self.can_push_and_pull(cx) {
|
if !self.can_push_and_pull(cx) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let spinner = self.render_spinner();
|
|
||||||
Some(
|
Some(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
.children(spinner)
|
|
||||||
.when_some(branch, |this, branch| {
|
.when_some(branch, |this, branch| {
|
||||||
let focus_handle = Some(self.focus_handle(cx));
|
let focus_handle = Some(self.focus_handle(cx));
|
||||||
|
|
||||||
|
@ -4129,17 +4074,17 @@ impl RenderOnce for PanelRepoFooter {
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.tooltip(Tooltip::for_action_title(
|
.tooltip(Tooltip::for_action_title(
|
||||||
"Switch Branch",
|
"Switch Branch",
|
||||||
&zed_actions::git::Branch,
|
&zed_actions::git::Switch,
|
||||||
))
|
))
|
||||||
.on_click(|_, window, cx| {
|
.on_click(|_, window, cx| {
|
||||||
window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx);
|
window.dispatch_action(zed_actions::git::Switch.boxed_clone(), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let branch_selector = PopoverMenu::new("popover-button")
|
let branch_selector = PopoverMenu::new("popover-button")
|
||||||
.menu(move |window, cx| Some(branch_picker::popover(repo.clone(), window, cx)))
|
.menu(move |window, cx| Some(branch_picker::popover(repo.clone(), window, cx)))
|
||||||
.trigger_with_tooltip(
|
.trigger_with_tooltip(
|
||||||
branch_selector_button,
|
branch_selector_button,
|
||||||
Tooltip::for_action_title("Switch Branch", &zed_actions::git::Branch),
|
Tooltip::for_action_title("Switch Branch", &zed_actions::git::Switch),
|
||||||
)
|
)
|
||||||
.anchor(Corner::BottomLeft)
|
.anchor(Corner::BottomLeft)
|
||||||
.offset(gpui::Point {
|
.offset(gpui::Point {
|
||||||
|
|
|
@ -54,10 +54,11 @@ use std::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{self, AtomicU64},
|
atomic::{self, AtomicU64},
|
||||||
},
|
},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
use sum_tree::{Edit, SumTree, TreeSet};
|
use sum_tree::{Edit, SumTree, TreeSet};
|
||||||
use text::{Bias, BufferId};
|
use text::{Bias, BufferId};
|
||||||
use util::{ResultExt, debug_panic};
|
use util::{ResultExt, debug_panic, post_inc};
|
||||||
use worktree::{
|
use worktree::{
|
||||||
File, PathKey, PathProgress, PathSummary, PathTarget, UpdatedGitRepositoriesSet, Worktree,
|
File, PathKey, PathProgress, PathSummary, PathTarget, UpdatedGitRepositoriesSet, Worktree,
|
||||||
};
|
};
|
||||||
|
@ -224,7 +225,16 @@ pub struct RepositorySnapshot {
|
||||||
pub scan_id: u64,
|
pub scan_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JobId = u64;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct JobInfo {
|
||||||
|
pub start: Instant,
|
||||||
|
pub message: SharedString,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Repository {
|
pub struct Repository {
|
||||||
|
this: WeakEntity<Self>,
|
||||||
snapshot: RepositorySnapshot,
|
snapshot: RepositorySnapshot,
|
||||||
commit_message_buffer: Option<Entity<Buffer>>,
|
commit_message_buffer: Option<Entity<Buffer>>,
|
||||||
git_store: WeakEntity<GitStore>,
|
git_store: WeakEntity<GitStore>,
|
||||||
|
@ -232,6 +242,8 @@ pub struct Repository {
|
||||||
// and that should be examined during the next status scan.
|
// and that should be examined during the next status scan.
|
||||||
paths_needing_status_update: BTreeSet<RepoPath>,
|
paths_needing_status_update: BTreeSet<RepoPath>,
|
||||||
job_sender: mpsc::UnboundedSender<GitJob>,
|
job_sender: mpsc::UnboundedSender<GitJob>,
|
||||||
|
active_jobs: HashMap<JobId, JobInfo>,
|
||||||
|
job_id: JobId,
|
||||||
askpass_delegates: Arc<Mutex<HashMap<u64, AskPassDelegate>>>,
|
askpass_delegates: Arc<Mutex<HashMap<u64, AskPassDelegate>>>,
|
||||||
latest_askpass_id: u64,
|
latest_askpass_id: u64,
|
||||||
}
|
}
|
||||||
|
@ -262,6 +274,9 @@ pub enum RepositoryEvent {
|
||||||
MergeHeadsChanged,
|
MergeHeadsChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct JobsUpdated;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GitStoreEvent {
|
pub enum GitStoreEvent {
|
||||||
ActiveRepositoryChanged(Option<RepositoryId>),
|
ActiveRepositoryChanged(Option<RepositoryId>),
|
||||||
|
@ -269,12 +284,14 @@ pub enum GitStoreEvent {
|
||||||
RepositoryAdded(RepositoryId),
|
RepositoryAdded(RepositoryId),
|
||||||
RepositoryRemoved(RepositoryId),
|
RepositoryRemoved(RepositoryId),
|
||||||
IndexWriteError(anyhow::Error),
|
IndexWriteError(anyhow::Error),
|
||||||
|
JobsUpdated,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<RepositoryEvent> for Repository {}
|
impl EventEmitter<RepositoryEvent> for Repository {}
|
||||||
|
impl EventEmitter<JobsUpdated> for Repository {}
|
||||||
impl EventEmitter<GitStoreEvent> for GitStore {}
|
impl EventEmitter<GitStoreEvent> for GitStore {}
|
||||||
|
|
||||||
struct GitJob {
|
pub struct GitJob {
|
||||||
job: Box<dyn FnOnce(RepositoryState, &mut AsyncApp) -> Task<()>>,
|
job: Box<dyn FnOnce(RepositoryState, &mut AsyncApp) -> Task<()>>,
|
||||||
key: Option<GitJobKey>,
|
key: Option<GitJobKey>,
|
||||||
}
|
}
|
||||||
|
@ -552,7 +569,9 @@ impl GitStore {
|
||||||
.loading_diffs
|
.loading_diffs
|
||||||
.entry((buffer_id, DiffKind::Unstaged))
|
.entry((buffer_id, DiffKind::Unstaged))
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let staged_text = repo.read(cx).load_staged_text(buffer_id, repo_path, cx);
|
let staged_text = repo.update(cx, |repo, cx| {
|
||||||
|
repo.load_staged_text(buffer_id, repo_path, cx)
|
||||||
|
});
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
Self::open_diff_internal(
|
Self::open_diff_internal(
|
||||||
this,
|
this,
|
||||||
|
@ -607,7 +626,10 @@ impl GitStore {
|
||||||
.loading_diffs
|
.loading_diffs
|
||||||
.entry((buffer_id, DiffKind::Uncommitted))
|
.entry((buffer_id, DiffKind::Uncommitted))
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let changes = repo.read(cx).load_committed_text(buffer_id, repo_path, cx);
|
let changes = repo.update(cx, |repo, cx| {
|
||||||
|
repo.load_committed_text(buffer_id, repo_path, cx)
|
||||||
|
});
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
Self::open_diff_internal(this, DiffKind::Uncommitted, changes.await, buffer, cx)
|
Self::open_diff_internal(this, DiffKind::Uncommitted, changes.await, buffer, cx)
|
||||||
.await
|
.await
|
||||||
|
@ -709,13 +731,14 @@ impl GitStore {
|
||||||
Some(repo.read(cx).status_for_path(&repo_path)?.status)
|
Some(repo.read(cx).status_for_path(&repo_path)?.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkpoint(&self, cx: &App) -> Task<Result<GitStoreCheckpoint>> {
|
pub fn checkpoint(&self, cx: &mut App) -> Task<Result<GitStoreCheckpoint>> {
|
||||||
let mut work_directory_abs_paths = Vec::new();
|
let mut work_directory_abs_paths = Vec::new();
|
||||||
let mut checkpoints = Vec::new();
|
let mut checkpoints = Vec::new();
|
||||||
for repository in self.repositories.values() {
|
for repository in self.repositories.values() {
|
||||||
let repository = repository.read(cx);
|
repository.update(cx, |repository, _| {
|
||||||
work_directory_abs_paths.push(repository.snapshot.work_directory_abs_path.clone());
|
work_directory_abs_paths.push(repository.snapshot.work_directory_abs_path.clone());
|
||||||
checkpoints.push(repository.checkpoint().map(|checkpoint| checkpoint?));
|
checkpoints.push(repository.checkpoint().map(|checkpoint| checkpoint?));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
|
@ -729,7 +752,11 @@ impl GitStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_checkpoint(&self, checkpoint: GitStoreCheckpoint, cx: &App) -> Task<Result<()>> {
|
pub fn restore_checkpoint(
|
||||||
|
&self,
|
||||||
|
checkpoint: GitStoreCheckpoint,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
let repositories_by_work_dir_abs_path = self
|
let repositories_by_work_dir_abs_path = self
|
||||||
.repositories
|
.repositories
|
||||||
.values()
|
.values()
|
||||||
|
@ -739,7 +766,9 @@ impl GitStore {
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
for (work_dir_abs_path, checkpoint) in checkpoint.checkpoints_by_work_dir_abs_path {
|
for (work_dir_abs_path, checkpoint) in checkpoint.checkpoints_by_work_dir_abs_path {
|
||||||
if let Some(repository) = repositories_by_work_dir_abs_path.get(&work_dir_abs_path) {
|
if let Some(repository) = repositories_by_work_dir_abs_path.get(&work_dir_abs_path) {
|
||||||
let restore = repository.read(cx).restore_checkpoint(checkpoint);
|
let restore = repository.update(cx, |repository, _| {
|
||||||
|
repository.restore_checkpoint(checkpoint)
|
||||||
|
});
|
||||||
tasks.push(async move { restore.await? });
|
tasks.push(async move { restore.await? });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -754,7 +783,7 @@ impl GitStore {
|
||||||
&self,
|
&self,
|
||||||
left: GitStoreCheckpoint,
|
left: GitStoreCheckpoint,
|
||||||
mut right: GitStoreCheckpoint,
|
mut right: GitStoreCheckpoint,
|
||||||
cx: &App,
|
cx: &mut App,
|
||||||
) -> Task<Result<bool>> {
|
) -> Task<Result<bool>> {
|
||||||
let repositories_by_work_dir_abs_path = self
|
let repositories_by_work_dir_abs_path = self
|
||||||
.repositories
|
.repositories
|
||||||
|
@ -770,9 +799,10 @@ impl GitStore {
|
||||||
{
|
{
|
||||||
if let Some(repository) = repositories_by_work_dir_abs_path.get(&work_dir_abs_path)
|
if let Some(repository) = repositories_by_work_dir_abs_path.get(&work_dir_abs_path)
|
||||||
{
|
{
|
||||||
let compare = repository
|
let compare = repository.update(cx, |repository, _| {
|
||||||
.read(cx)
|
repository.compare_checkpoints(left_checkpoint, right_checkpoint)
|
||||||
.compare_checkpoints(left_checkpoint, right_checkpoint);
|
});
|
||||||
|
|
||||||
tasks.push(async move { compare.await? });
|
tasks.push(async move { compare.await? });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -787,7 +817,11 @@ impl GitStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_checkpoint(&self, checkpoint: GitStoreCheckpoint, cx: &App) -> Task<Result<()>> {
|
pub fn delete_checkpoint(
|
||||||
|
&self,
|
||||||
|
checkpoint: GitStoreCheckpoint,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
let repositories_by_work_directory_abs_path = self
|
let repositories_by_work_directory_abs_path = self
|
||||||
.repositories
|
.repositories
|
||||||
.values()
|
.values()
|
||||||
|
@ -799,7 +833,7 @@ impl GitStore {
|
||||||
if let Some(repository) =
|
if let Some(repository) =
|
||||||
repositories_by_work_directory_abs_path.get(&work_dir_abs_path)
|
repositories_by_work_directory_abs_path.get(&work_dir_abs_path)
|
||||||
{
|
{
|
||||||
let delete = repository.read(cx).delete_checkpoint(checkpoint);
|
let delete = repository.update(cx, |this, _| this.delete_checkpoint(checkpoint));
|
||||||
tasks.push(async move { delete.await? });
|
tasks.push(async move { delete.await? });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -814,7 +848,7 @@ impl GitStore {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
version: Option<clock::Global>,
|
version: Option<clock::Global>,
|
||||||
cx: &App,
|
cx: &mut App,
|
||||||
) -> Task<Result<Option<Blame>>> {
|
) -> Task<Result<Option<Blame>>> {
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
let Some((repo, repo_path)) =
|
let Some((repo, repo_path)) =
|
||||||
|
@ -829,7 +863,8 @@ impl GitStore {
|
||||||
let version = version.unwrap_or(buffer.version());
|
let version = version.unwrap_or(buffer.version());
|
||||||
let buffer_id = buffer.remote_id();
|
let buffer_id = buffer.remote_id();
|
||||||
|
|
||||||
let rx = repo.read(cx).send_job(move |state, _| async move {
|
let rx = repo.update(cx, |repo, _| {
|
||||||
|
repo.send_job(None, move |state, _| async move {
|
||||||
match state {
|
match state {
|
||||||
RepositoryState::Local { backend, .. } => backend
|
RepositoryState::Local { backend, .. } => backend
|
||||||
.blame(repo_path.clone(), content)
|
.blame(repo_path.clone(), content)
|
||||||
|
@ -847,6 +882,7 @@ impl GitStore {
|
||||||
Ok(deserialize_blame_buffer_response(response))
|
Ok(deserialize_blame_buffer_response(response))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
||||||
|
@ -856,7 +892,7 @@ impl GitStore {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
selection: Range<u32>,
|
selection: Range<u32>,
|
||||||
cx: &App,
|
cx: &mut App,
|
||||||
) -> Task<Result<url::Url>> {
|
) -> Task<Result<url::Url>> {
|
||||||
let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
|
let Some(file) = File::from_dyn(buffer.read(cx).file()) else {
|
||||||
return Task::ready(Err(anyhow!("buffer has no file")));
|
return Task::ready(Err(anyhow!("buffer has no file")));
|
||||||
|
@ -897,7 +933,9 @@ impl GitStore {
|
||||||
.and_then(|b| b.remote_name())
|
.and_then(|b| b.remote_name())
|
||||||
.unwrap_or("origin")
|
.unwrap_or("origin")
|
||||||
.to_string();
|
.to_string();
|
||||||
let rx = repo.read(cx).send_job(move |state, cx| async move {
|
|
||||||
|
let rx = repo.update(cx, |repo, _| {
|
||||||
|
repo.send_job(None, move |state, cx| async move {
|
||||||
match state {
|
match state {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
let origin_url = backend
|
let origin_url = backend
|
||||||
|
@ -943,6 +981,7 @@ impl GitStore {
|
||||||
url::Url::parse(&response.permalink).context("failed to parse permalink")
|
url::Url::parse(&response.permalink).context("failed to parse permalink")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
||||||
}
|
}
|
||||||
|
@ -1058,6 +1097,10 @@ impl GitStore {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_jobs_updated(&mut self, _: Entity<Repository>, _: &JobsUpdated, cx: &mut Context<Self>) {
|
||||||
|
cx.emit(GitStoreEvent::JobsUpdated)
|
||||||
|
}
|
||||||
|
|
||||||
/// Update our list of repositories and schedule git scans in response to a notification from a worktree,
|
/// Update our list of repositories and schedule git scans in response to a notification from a worktree,
|
||||||
fn update_repositories_from_worktree(
|
fn update_repositories_from_worktree(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1110,6 +1153,8 @@ impl GitStore {
|
||||||
});
|
});
|
||||||
self._subscriptions
|
self._subscriptions
|
||||||
.push(cx.subscribe(&repo, Self::on_repository_event));
|
.push(cx.subscribe(&repo, Self::on_repository_event));
|
||||||
|
self._subscriptions
|
||||||
|
.push(cx.subscribe(&repo, Self::on_jobs_updated));
|
||||||
self.repositories.insert(id, repo);
|
self.repositories.insert(id, repo);
|
||||||
cx.emit(GitStoreEvent::RepositoryAdded(id));
|
cx.emit(GitStoreEvent::RepositoryAdded(id));
|
||||||
self.active_repo_id.get_or_insert_with(|| {
|
self.active_repo_id.get_or_insert_with(|| {
|
||||||
|
@ -1291,8 +1336,10 @@ impl GitStore {
|
||||||
for (repo, repo_diff_state_updates) in diff_state_updates.into_iter() {
|
for (repo, repo_diff_state_updates) in diff_state_updates.into_iter() {
|
||||||
let git_store = cx.weak_entity();
|
let git_store = cx.weak_entity();
|
||||||
|
|
||||||
let _ = repo.read(cx).send_keyed_job(
|
let _ = repo.update(cx, |repo, _| {
|
||||||
|
repo.send_keyed_job(
|
||||||
Some(GitJobKey::BatchReadIndex),
|
Some(GitJobKey::BatchReadIndex),
|
||||||
|
None,
|
||||||
|state, mut cx| async move {
|
|state, mut cx| async move {
|
||||||
let RepositoryState::Local { backend, .. } = state else {
|
let RepositoryState::Local { backend, .. } = state else {
|
||||||
log::error!("tried to recompute diffs for a non-local repository");
|
log::error!("tried to recompute diffs for a non-local repository");
|
||||||
|
@ -1408,7 +1455,8 @@ impl GitStore {
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2610,6 +2658,7 @@ impl Repository {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let snapshot = RepositorySnapshot::empty(id, work_directory_abs_path.clone());
|
let snapshot = RepositorySnapshot::empty(id, work_directory_abs_path.clone());
|
||||||
Repository {
|
Repository {
|
||||||
|
this: cx.weak_entity(),
|
||||||
git_store,
|
git_store,
|
||||||
snapshot,
|
snapshot,
|
||||||
commit_message_buffer: None,
|
commit_message_buffer: None,
|
||||||
|
@ -2623,6 +2672,8 @@ impl Repository {
|
||||||
fs,
|
fs,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
|
job_id: 0,
|
||||||
|
active_jobs: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2636,6 +2687,7 @@ impl Repository {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let snapshot = RepositorySnapshot::empty(id, work_directory_abs_path);
|
let snapshot = RepositorySnapshot::empty(id, work_directory_abs_path);
|
||||||
Self {
|
Self {
|
||||||
|
this: cx.weak_entity(),
|
||||||
snapshot,
|
snapshot,
|
||||||
commit_message_buffer: None,
|
commit_message_buffer: None,
|
||||||
git_store,
|
git_store,
|
||||||
|
@ -2643,6 +2695,8 @@ impl Repository {
|
||||||
job_sender: Self::spawn_remote_git_worker(project_id, client, cx),
|
job_sender: Self::spawn_remote_git_worker(project_id, client, cx),
|
||||||
askpass_delegates: Default::default(),
|
askpass_delegates: Default::default(),
|
||||||
latest_askpass_id: 0,
|
latest_askpass_id: 0,
|
||||||
|
active_jobs: Default::default(),
|
||||||
|
job_id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2650,29 +2704,61 @@ impl Repository {
|
||||||
self.git_store.upgrade()
|
self.git_store.upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_job<F, Fut, R>(&self, job: F) -> oneshot::Receiver<R>
|
pub fn send_job<F, Fut, R>(
|
||||||
|
&mut self,
|
||||||
|
status: Option<SharedString>,
|
||||||
|
job: F,
|
||||||
|
) -> oneshot::Receiver<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(RepositoryState, AsyncApp) -> Fut + 'static,
|
F: FnOnce(RepositoryState, AsyncApp) -> Fut + 'static,
|
||||||
Fut: Future<Output = R> + 'static,
|
Fut: Future<Output = R> + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
self.send_keyed_job(None, job)
|
self.send_keyed_job(None, status, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_keyed_job<F, Fut, R>(&self, key: Option<GitJobKey>, job: F) -> oneshot::Receiver<R>
|
fn send_keyed_job<F, Fut, R>(
|
||||||
|
&mut self,
|
||||||
|
key: Option<GitJobKey>,
|
||||||
|
status: Option<SharedString>,
|
||||||
|
job: F,
|
||||||
|
) -> oneshot::Receiver<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(RepositoryState, AsyncApp) -> Fut + 'static,
|
F: FnOnce(RepositoryState, AsyncApp) -> Fut + 'static,
|
||||||
Fut: Future<Output = R> + 'static,
|
Fut: Future<Output = R> + 'static,
|
||||||
R: Send + 'static,
|
R: Send + 'static,
|
||||||
{
|
{
|
||||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||||
|
let job_id = post_inc(&mut self.job_id);
|
||||||
|
let this = self.this.clone();
|
||||||
self.job_sender
|
self.job_sender
|
||||||
.unbounded_send(GitJob {
|
.unbounded_send(GitJob {
|
||||||
key,
|
key,
|
||||||
job: Box::new(|state, cx: &mut AsyncApp| {
|
job: Box::new(move |state, cx: &mut AsyncApp| {
|
||||||
let job = job(state, cx.clone());
|
let job = job(state, cx.clone());
|
||||||
cx.spawn(async move |_| {
|
cx.spawn(async move |cx| {
|
||||||
|
if let Some(s) = status.clone() {
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.active_jobs.insert(
|
||||||
|
job_id,
|
||||||
|
JobInfo {
|
||||||
|
start: Instant::now(),
|
||||||
|
message: s.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
let result = job.await;
|
let result = job.await;
|
||||||
|
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.active_jobs.remove(&job_id);
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
result_tx.send(result).ok();
|
result_tx.send(result).ok();
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
@ -2741,7 +2827,7 @@ impl Repository {
|
||||||
}
|
}
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
|
|
||||||
let rx = self.send_job(move |state, mut cx| async move {
|
let rx = self.send_job(None, move |state, mut cx| async move {
|
||||||
let Some(this) = this.upgrade() else {
|
let Some(this) = this.upgrade() else {
|
||||||
bail!("git store was dropped");
|
bail!("git store was dropped");
|
||||||
};
|
};
|
||||||
|
@ -2807,7 +2893,7 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkout_files(
|
pub fn checkout_files(
|
||||||
&self,
|
&mut self,
|
||||||
commit: &str,
|
commit: &str,
|
||||||
paths: Vec<RepoPath>,
|
paths: Vec<RepoPath>,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
|
@ -2815,7 +2901,9 @@ impl Repository {
|
||||||
let commit = commit.to_string();
|
let commit = commit.to_string();
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, _| async move {
|
self.send_job(
|
||||||
|
Some(format!("git checkout {}", commit).into()),
|
||||||
|
move |git_repo, _| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -2842,11 +2930,12 @@ impl Repository {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(
|
pub fn reset(
|
||||||
&self,
|
&mut self,
|
||||||
commit: String,
|
commit: String,
|
||||||
reset_mode: ResetMode,
|
reset_mode: ResetMode,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
|
@ -2854,7 +2943,7 @@ impl Repository {
|
||||||
let commit = commit.to_string();
|
let commit = commit.to_string();
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, _| async move {
|
self.send_job(None, move |git_repo, _| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -2880,9 +2969,9 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&self, commit: String) -> oneshot::Receiver<Result<CommitDetails>> {
|
pub fn show(&mut self, commit: String) -> oneshot::Receiver<Result<CommitDetails>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |git_repo, _cx| async move {
|
self.send_job(None, move |git_repo, _cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.show(commit).await,
|
RepositoryState::Local { backend, .. } => backend.show(commit).await,
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
|
@ -2906,9 +2995,9 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_commit_diff(&self, commit: String) -> oneshot::Receiver<Result<CommitDiff>> {
|
pub fn load_commit_diff(&mut self, commit: String) -> oneshot::Receiver<Result<CommitDiff>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |git_repo, cx| async move {
|
self.send_job(None, move |git_repo, cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.load_commit(commit, cx).await,
|
RepositoryState::Local { backend, .. } => backend.load_commit(commit, cx).await,
|
||||||
RepositoryState::Remote {
|
RepositoryState::Remote {
|
||||||
|
@ -2977,7 +3066,7 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, _| {
|
||||||
this.send_job(move |git_repo, _cx| async move {
|
this.send_job(None, move |git_repo, _cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3044,7 +3133,7 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, _| {
|
||||||
this.send_job(move |git_repo, _cx| async move {
|
this.send_job(None, move |git_repo, _cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3094,14 +3183,14 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(
|
pub fn commit(
|
||||||
&self,
|
&mut self,
|
||||||
message: SharedString,
|
message: SharedString,
|
||||||
name_and_email: Option<(SharedString, SharedString)>,
|
name_and_email: Option<(SharedString, SharedString)>,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
) -> oneshot::Receiver<Result<()>> {
|
) -> oneshot::Receiver<Result<()>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, _cx| async move {
|
self.send_job(Some("git commit".into()), move |git_repo, _cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3136,7 +3225,7 @@ impl Repository {
|
||||||
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, cx| async move {
|
self.send_job(Some("git fetch".into()), move |git_repo, cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3180,7 +3269,16 @@ impl Repository {
|
||||||
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, cx| async move {
|
let args = options
|
||||||
|
.map(|option| match option {
|
||||||
|
PushOptions::SetUpstream => " --set-upstream",
|
||||||
|
PushOptions::Force => " --force",
|
||||||
|
})
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
|
self.send_job(
|
||||||
|
Some(format!("git push{} {} {}", args, branch, remote).into()),
|
||||||
|
move |git_repo, cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3213,8 +3311,11 @@ impl Repository {
|
||||||
remote_name: remote.to_string(),
|
remote_name: remote.to_string(),
|
||||||
options: options.map(|options| match options {
|
options: options.map(|options| match options {
|
||||||
PushOptions::Force => proto::push::PushOptions::Force,
|
PushOptions::Force => proto::push::PushOptions::Force,
|
||||||
PushOptions::SetUpstream => proto::push::PushOptions::SetUpstream,
|
PushOptions::SetUpstream => {
|
||||||
} as i32),
|
proto::push::PushOptions::SetUpstream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
as i32),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.context("sending push request")?;
|
.context("sending push request")?;
|
||||||
|
@ -3225,7 +3326,8 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pull(
|
pub fn pull(
|
||||||
|
@ -3239,7 +3341,9 @@ impl Repository {
|
||||||
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
self.send_job(move |git_repo, cx| async move {
|
self.send_job(
|
||||||
|
Some(format!("git pull {} {}", remote, branch).into()),
|
||||||
|
move |git_repo, cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
backend,
|
backend,
|
||||||
|
@ -3279,11 +3383,12 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_set_index_text_job(
|
fn spawn_set_index_text_job(
|
||||||
&self,
|
&mut self,
|
||||||
path: RepoPath,
|
path: RepoPath,
|
||||||
content: Option<String>,
|
content: Option<String>,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
|
@ -3292,6 +3397,7 @@ impl Repository {
|
||||||
|
|
||||||
self.send_keyed_job(
|
self.send_keyed_job(
|
||||||
Some(GitJobKey::WriteIndex(path.clone())),
|
Some(GitJobKey::WriteIndex(path.clone())),
|
||||||
|
None,
|
||||||
move |git_repo, _cx| async move {
|
move |git_repo, _cx| async move {
|
||||||
match git_repo {
|
match git_repo {
|
||||||
RepositoryState::Local {
|
RepositoryState::Local {
|
||||||
|
@ -3320,11 +3426,11 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_remotes(
|
pub fn get_remotes(
|
||||||
&self,
|
&mut self,
|
||||||
branch_name: Option<String>,
|
branch_name: Option<String>,
|
||||||
) -> oneshot::Receiver<Result<Vec<Remote>>> {
|
) -> oneshot::Receiver<Result<Vec<Remote>>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.get_remotes(branch_name).await,
|
RepositoryState::Local { backend, .. } => backend.get_remotes(branch_name).await,
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
|
@ -3350,9 +3456,9 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branches(&self) -> oneshot::Receiver<Result<Vec<Branch>>> {
|
pub fn branches(&mut self) -> oneshot::Receiver<Result<Vec<Branch>>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, cx| async move {
|
self.send_job(None, move |repo, cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
let backend = backend.clone();
|
let backend = backend.clone();
|
||||||
|
@ -3379,9 +3485,9 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff(&self, diff_type: DiffType, _cx: &App) -> oneshot::Receiver<Result<String>> {
|
pub fn diff(&mut self, diff_type: DiffType, _cx: &App) -> oneshot::Receiver<Result<String>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.diff(diff_type).await,
|
RepositoryState::Local { backend, .. } => backend.diff(diff_type).await,
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
|
@ -3406,11 +3512,15 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
pub fn create_branch(&mut self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(
|
||||||
|
Some(format!("git switch -c {branch_name}").into()),
|
||||||
|
move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.create_branch(branch_name).await,
|
RepositoryState::Local { backend, .. } => {
|
||||||
|
backend.create_branch(branch_name).await
|
||||||
|
}
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
client
|
client
|
||||||
.request(proto::GitCreateBranch {
|
.request(proto::GitCreateBranch {
|
||||||
|
@ -3423,14 +3533,19 @@ impl Repository {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_branch(&self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
pub fn change_branch(&mut self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(
|
||||||
|
Some(format!("git switch {branch_name}").into()),
|
||||||
|
move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.change_branch(branch_name).await,
|
RepositoryState::Local { backend, .. } => {
|
||||||
|
backend.change_branch(branch_name).await
|
||||||
|
}
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
client
|
client
|
||||||
.request(proto::GitChangeBranch {
|
.request(proto::GitChangeBranch {
|
||||||
|
@ -3443,12 +3558,13 @@ impl Repository {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_for_pushed_commits(&self) -> oneshot::Receiver<Result<Vec<SharedString>>> {
|
pub fn check_for_pushed_commits(&mut self) -> oneshot::Receiver<Result<Vec<SharedString>>> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.check_for_pushed_commit().await,
|
RepositoryState::Local { backend, .. } => backend.check_for_pushed_commit().await,
|
||||||
RepositoryState::Remote { project_id, client } => {
|
RepositoryState::Remote { project_id, client } => {
|
||||||
|
@ -3467,8 +3583,8 @@ impl Repository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkpoint(&self) -> oneshot::Receiver<Result<GitRepositoryCheckpoint>> {
|
pub fn checkpoint(&mut self) -> oneshot::Receiver<Result<GitRepositoryCheckpoint>> {
|
||||||
self.send_job(|repo, _cx| async move {
|
self.send_job(None, |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => backend.checkpoint().await,
|
RepositoryState::Local { backend, .. } => backend.checkpoint().await,
|
||||||
RepositoryState::Remote { .. } => Err(anyhow!("not implemented yet")),
|
RepositoryState::Remote { .. } => Err(anyhow!("not implemented yet")),
|
||||||
|
@ -3477,10 +3593,10 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_checkpoint(
|
pub fn restore_checkpoint(
|
||||||
&self,
|
&mut self,
|
||||||
checkpoint: GitRepositoryCheckpoint,
|
checkpoint: GitRepositoryCheckpoint,
|
||||||
) -> oneshot::Receiver<Result<()>> {
|
) -> oneshot::Receiver<Result<()>> {
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
backend.restore_checkpoint(checkpoint).await
|
backend.restore_checkpoint(checkpoint).await
|
||||||
|
@ -3526,11 +3642,11 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_checkpoints(
|
pub fn compare_checkpoints(
|
||||||
&self,
|
&mut self,
|
||||||
left: GitRepositoryCheckpoint,
|
left: GitRepositoryCheckpoint,
|
||||||
right: GitRepositoryCheckpoint,
|
right: GitRepositoryCheckpoint,
|
||||||
) -> oneshot::Receiver<Result<bool>> {
|
) -> oneshot::Receiver<Result<bool>> {
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
backend.compare_checkpoints(left, right).await
|
backend.compare_checkpoints(left, right).await
|
||||||
|
@ -3541,10 +3657,10 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_checkpoint(
|
pub fn delete_checkpoint(
|
||||||
&self,
|
&mut self,
|
||||||
checkpoint: GitRepositoryCheckpoint,
|
checkpoint: GitRepositoryCheckpoint,
|
||||||
) -> oneshot::Receiver<Result<()>> {
|
) -> oneshot::Receiver<Result<()>> {
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
backend.delete_checkpoint(checkpoint).await
|
backend.delete_checkpoint(checkpoint).await
|
||||||
|
@ -3555,11 +3671,11 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_checkpoints(
|
pub fn diff_checkpoints(
|
||||||
&self,
|
&mut self,
|
||||||
base_checkpoint: GitRepositoryCheckpoint,
|
base_checkpoint: GitRepositoryCheckpoint,
|
||||||
target_checkpoint: GitRepositoryCheckpoint,
|
target_checkpoint: GitRepositoryCheckpoint,
|
||||||
) -> oneshot::Receiver<Result<String>> {
|
) -> oneshot::Receiver<Result<String>> {
|
||||||
self.send_job(move |repo, _cx| async move {
|
self.send_job(None, move |repo, _cx| async move {
|
||||||
match repo {
|
match repo {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
backend
|
backend
|
||||||
|
@ -3585,6 +3701,7 @@ impl Repository {
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
let _ = self.send_keyed_job(
|
let _ = self.send_keyed_job(
|
||||||
Some(GitJobKey::ReloadGitState),
|
Some(GitJobKey::ReloadGitState),
|
||||||
|
None,
|
||||||
|state, mut cx| async move {
|
|state, mut cx| async move {
|
||||||
let Some(this) = this.upgrade() else {
|
let Some(this) = this.upgrade() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -3727,12 +3844,12 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_staged_text(
|
fn load_staged_text(
|
||||||
&self,
|
&mut self,
|
||||||
buffer_id: BufferId,
|
buffer_id: BufferId,
|
||||||
repo_path: RepoPath,
|
repo_path: RepoPath,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Task<Result<Option<String>>> {
|
) -> Task<Result<Option<String>>> {
|
||||||
let rx = self.send_job(move |state, _| async move {
|
let rx = self.send_job(None, move |state, _| async move {
|
||||||
match state {
|
match state {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
anyhow::Ok(backend.load_index_text(repo_path).await)
|
anyhow::Ok(backend.load_index_text(repo_path).await)
|
||||||
|
@ -3752,12 +3869,12 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_committed_text(
|
fn load_committed_text(
|
||||||
&self,
|
&mut self,
|
||||||
buffer_id: BufferId,
|
buffer_id: BufferId,
|
||||||
repo_path: RepoPath,
|
repo_path: RepoPath,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Task<Result<DiffBasesChange>> {
|
) -> Task<Result<DiffBasesChange>> {
|
||||||
let rx = self.send_job(move |state, _| async move {
|
let rx = self.send_job(None, move |state, _| async move {
|
||||||
match state {
|
match state {
|
||||||
RepositoryState::Local { backend, .. } => {
|
RepositoryState::Local { backend, .. } => {
|
||||||
let committed_text = backend.load_committed_text(repo_path.clone()).await;
|
let committed_text = backend.load_committed_text(repo_path.clone()).await;
|
||||||
|
@ -3809,6 +3926,7 @@ impl Repository {
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
let _ = self.send_keyed_job(
|
let _ = self.send_keyed_job(
|
||||||
Some(GitJobKey::RefreshStatuses),
|
Some(GitJobKey::RefreshStatuses),
|
||||||
|
None,
|
||||||
|state, mut cx| async move {
|
|state, mut cx| async move {
|
||||||
let (prev_snapshot, mut changed_paths) = this.update(&mut cx, |this, _| {
|
let (prev_snapshot, mut changed_paths) = this.update(&mut cx, |this, _| {
|
||||||
(
|
(
|
||||||
|
@ -3871,6 +3989,11 @@ impl Repository {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// currently running git command and when it started
|
||||||
|
pub fn current_job(&self) -> Option<JobInfo> {
|
||||||
|
self.active_jobs.values().next().cloned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_permalink_in_rust_registry_src(
|
fn get_permalink_in_rust_registry_src(
|
||||||
|
|
|
@ -4135,20 +4135,22 @@ impl Project {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
version: Option<clock::Global>,
|
version: Option<clock::Global>,
|
||||||
cx: &App,
|
cx: &mut App,
|
||||||
) -> Task<Result<Option<Blame>>> {
|
) -> Task<Result<Option<Blame>>> {
|
||||||
self.git_store.read(cx).blame_buffer(buffer, version, cx)
|
self.git_store.update(cx, |git_store, cx| {
|
||||||
|
git_store.blame_buffer(buffer, version, cx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_permalink_to_line(
|
pub fn get_permalink_to_line(
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
selection: Range<u32>,
|
selection: Range<u32>,
|
||||||
cx: &App,
|
cx: &mut App,
|
||||||
) -> Task<Result<url::Url>> {
|
) -> Task<Result<url::Url>> {
|
||||||
self.git_store
|
self.git_store.update(cx, |git_store, cx| {
|
||||||
.read(cx)
|
git_store.get_permalink_to_line(buffer, selection, cx)
|
||||||
.get_permalink_to_line(buffer, selection, cx)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPC message handlers
|
// RPC message handlers
|
||||||
|
|
|
@ -1366,7 +1366,11 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
|
||||||
|
|
||||||
assert_eq!(&remote_branches, &branches_set);
|
assert_eq!(&remote_branches, &branches_set);
|
||||||
|
|
||||||
cx.update(|cx| repository.read(cx).change_branch(new_branch.to_string()))
|
cx.update(|cx| {
|
||||||
|
repository.update(cx, |repository, _cx| {
|
||||||
|
repository.change_branch(new_branch.to_string())
|
||||||
|
})
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1394,18 +1398,18 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
|
||||||
|
|
||||||
// Also try creating a new branch
|
// Also try creating a new branch
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
repository
|
repository.update(cx, |repo, _cx| {
|
||||||
.read(cx)
|
repo.create_branch("totally-new-branch".to_string())
|
||||||
.create_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
repository
|
repository.update(cx, |repo, _cx| {
|
||||||
.read(cx)
|
repo.change_branch("totally-new-branch".to_string())
|
||||||
.change_branch("totally-new-branch".to_string())
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue