Add an undo button to the git panel (#24593)
Also prep infrastructure for pushing a commit Release Notes: - N/A --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Nate Butler <iamnbutler@gmail.com>
This commit is contained in:
parent
df8adc8b11
commit
b014afa938
41 changed files with 1437 additions and 738 deletions
|
@ -2722,8 +2722,8 @@ fn serialize_blame_buffer_response(blame: Option<git::blame::Blame>) -> proto::B
|
|||
author_mail: entry.author_mail.clone(),
|
||||
author_time: entry.author_time,
|
||||
author_tz: entry.author_tz.clone(),
|
||||
committer: entry.committer.clone(),
|
||||
committer_mail: entry.committer_mail.clone(),
|
||||
committer: entry.committer_name.clone(),
|
||||
committer_mail: entry.committer_email.clone(),
|
||||
committer_time: entry.committer_time,
|
||||
committer_tz: entry.committer_tz.clone(),
|
||||
summary: entry.summary.clone(),
|
||||
|
@ -2772,10 +2772,10 @@ fn deserialize_blame_buffer_response(
|
|||
sha: git::Oid::from_bytes(&entry.sha).ok()?,
|
||||
range: entry.start_line..entry.end_line,
|
||||
original_line_number: entry.original_line_number,
|
||||
committer: entry.committer,
|
||||
committer_name: entry.committer,
|
||||
committer_time: entry.committer_time,
|
||||
committer_tz: entry.committer_tz,
|
||||
committer_mail: entry.committer_mail,
|
||||
committer_email: entry.committer_mail,
|
||||
author: entry.author,
|
||||
author_mail: entry.author_mail,
|
||||
author_time: entry.author_time,
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
use crate::buffer_store::BufferStore;
|
||||
use crate::worktree_store::{WorktreeStore, WorktreeStoreEvent};
|
||||
use crate::{Project, ProjectPath};
|
||||
use anyhow::Context as _;
|
||||
use anyhow::{Context as _, Result};
|
||||
use client::ProjectId;
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use futures::StreamExt as _;
|
||||
use git::repository::{Branch, CommitDetails, ResetMode};
|
||||
use git::{
|
||||
repository::{GitRepository, RepoPath},
|
||||
status::{GitSummary, TrackedSummary},
|
||||
};
|
||||
use gpui::{
|
||||
App, AppContext, Context, Entity, EventEmitter, SharedString, Subscription, Task, WeakEntity,
|
||||
App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
|
||||
WeakEntity,
|
||||
};
|
||||
use language::{Buffer, LanguageRegistry};
|
||||
use rpc::proto::ToProto;
|
||||
use rpc::{proto, AnyProtoClient};
|
||||
use rpc::proto::{git_reset, ToProto};
|
||||
use rpc::{proto, AnyProtoClient, TypedEnvelope};
|
||||
use settings::WorktreeId;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
@ -22,22 +24,23 @@ use text::BufferId;
|
|||
use util::{maybe, ResultExt};
|
||||
use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};
|
||||
|
||||
pub struct GitState {
|
||||
pub struct GitStore {
|
||||
pub(super) project_id: Option<ProjectId>,
|
||||
pub(super) client: Option<AnyProtoClient>,
|
||||
pub update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<anyhow::Result<()>>)>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
repositories: Vec<Entity<Repository>>,
|
||||
active_index: Option<usize>,
|
||||
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
pub struct Repository {
|
||||
commit_message_buffer: Option<Entity<Buffer>>,
|
||||
git_state: WeakEntity<GitState>,
|
||||
git_store: WeakEntity<GitStore>,
|
||||
pub worktree_id: WorktreeId,
|
||||
pub repository_entry: RepositoryEntry,
|
||||
pub git_repo: GitRepo,
|
||||
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<anyhow::Result<()>>)>,
|
||||
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -57,6 +60,11 @@ pub enum Message {
|
|||
message: SharedString,
|
||||
name_and_email: Option<(SharedString, SharedString)>,
|
||||
},
|
||||
Reset {
|
||||
repo: GitRepo,
|
||||
commit: SharedString,
|
||||
reset_mode: ResetMode,
|
||||
},
|
||||
Stage(GitRepo, Vec<RepoPath>),
|
||||
Unstage(GitRepo, Vec<RepoPath>),
|
||||
SetIndexText(GitRepo, RepoPath, Option<String>),
|
||||
|
@ -68,11 +76,12 @@ pub enum GitEvent {
|
|||
GitStateUpdated,
|
||||
}
|
||||
|
||||
impl EventEmitter<GitEvent> for GitState {}
|
||||
impl EventEmitter<GitEvent> for GitStore {}
|
||||
|
||||
impl GitState {
|
||||
impl GitStore {
|
||||
pub fn new(
|
||||
worktree_store: &Entity<WorktreeStore>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
client: Option<AnyProtoClient>,
|
||||
project_id: Option<ProjectId>,
|
||||
cx: &mut Context<'_, Self>,
|
||||
|
@ -80,9 +89,10 @@ impl GitState {
|
|||
let update_sender = Self::spawn_git_worker(cx);
|
||||
let _subscription = cx.subscribe(worktree_store, Self::on_worktree_store_event);
|
||||
|
||||
GitState {
|
||||
GitStore {
|
||||
project_id,
|
||||
client,
|
||||
buffer_store,
|
||||
repositories: Vec::new(),
|
||||
active_index: None,
|
||||
update_sender,
|
||||
|
@ -90,6 +100,16 @@ impl GitState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_request_handler(Self::handle_stage);
|
||||
client.add_entity_request_handler(Self::handle_unstage);
|
||||
client.add_entity_request_handler(Self::handle_commit);
|
||||
client.add_entity_request_handler(Self::handle_reset);
|
||||
client.add_entity_request_handler(Self::handle_show);
|
||||
client.add_entity_request_handler(Self::handle_open_commit_message_buffer);
|
||||
client.add_entity_request_handler(Self::handle_set_index_text);
|
||||
}
|
||||
|
||||
pub fn active_repository(&self) -> Option<Entity<Repository>> {
|
||||
self.active_index
|
||||
.map(|index| self.repositories[index].clone())
|
||||
|
@ -153,7 +173,7 @@ impl GitState {
|
|||
existing_handle
|
||||
} else {
|
||||
cx.new(|_| Repository {
|
||||
git_state: this.clone(),
|
||||
git_store: this.clone(),
|
||||
worktree_id,
|
||||
repository_entry: repo.clone(),
|
||||
git_repo,
|
||||
|
@ -189,10 +209,10 @@ impl GitState {
|
|||
}
|
||||
|
||||
fn spawn_git_worker(
|
||||
cx: &mut Context<'_, GitState>,
|
||||
) -> mpsc::UnboundedSender<(Message, oneshot::Sender<anyhow::Result<()>>)> {
|
||||
cx: &mut Context<'_, GitStore>,
|
||||
) -> mpsc::UnboundedSender<(Message, oneshot::Sender<Result<()>>)> {
|
||||
let (update_sender, mut update_receiver) =
|
||||
mpsc::unbounded::<(Message, oneshot::Sender<anyhow::Result<()>>)>();
|
||||
mpsc::unbounded::<(Message, oneshot::Sender<Result<()>>)>();
|
||||
cx.spawn(|_, cx| async move {
|
||||
while let Some((msg, respond)) = update_receiver.next().await {
|
||||
let result = cx
|
||||
|
@ -206,7 +226,7 @@ impl GitState {
|
|||
update_sender
|
||||
}
|
||||
|
||||
async fn process_git_msg(msg: Message) -> Result<(), anyhow::Error> {
|
||||
async fn process_git_msg(msg: Message) -> Result<()> {
|
||||
match msg {
|
||||
Message::Stage(repo, paths) => {
|
||||
match repo {
|
||||
|
@ -233,6 +253,35 @@ impl GitState {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Message::Reset {
|
||||
repo,
|
||||
commit,
|
||||
reset_mode,
|
||||
} => {
|
||||
match repo {
|
||||
GitRepo::Local(repo) => repo.reset(&commit, reset_mode)?,
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => {
|
||||
client
|
||||
.request(proto::GitReset {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
commit: commit.into(),
|
||||
mode: match reset_mode {
|
||||
ResetMode::Soft => git_reset::ResetMode::Soft.into(),
|
||||
ResetMode::Mixed => git_reset::ResetMode::Mixed.into(),
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Message::Unstage(repo, paths) => {
|
||||
match repo {
|
||||
GitRepo::Local(repo) => repo.unstage_paths(&paths)?,
|
||||
|
@ -309,20 +358,219 @@ impl GitState {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_stage(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Stage>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let entries = envelope
|
||||
.payload
|
||||
.paths
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.map(RepoPath::new)
|
||||
.collect();
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.stage_entries(entries)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_unstage(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Unstage>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let entries = envelope
|
||||
.payload
|
||||
.paths
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.map(RepoPath::new)
|
||||
.collect();
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.unstage_entries(entries)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_set_index_text(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::SetIndexText>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.set_index_text(
|
||||
&RepoPath::from_str(&envelope.payload.path),
|
||||
envelope.payload.text,
|
||||
)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_commit(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Commit>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let message = SharedString::from(envelope.payload.message);
|
||||
let name = envelope.payload.name.map(SharedString::from);
|
||||
let email = envelope.payload.email.map(SharedString::from);
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.commit(message, name.zip(email))
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_show(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitShow>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::GitCommitDetails> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let commit = repository_handle
|
||||
.update(&mut cx, |repository_handle, cx| {
|
||||
repository_handle.show(&envelope.payload.commit, cx)
|
||||
})?
|
||||
.await?;
|
||||
Ok(proto::GitCommitDetails {
|
||||
sha: commit.sha.into(),
|
||||
message: commit.message.into(),
|
||||
commit_timestamp: commit.commit_timestamp,
|
||||
committer_email: commit.committer_email.into(),
|
||||
committer_name: commit.committer_name.into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_reset(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitReset>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let mode = match envelope.payload.mode() {
|
||||
git_reset::ResetMode::Soft => ResetMode::Soft,
|
||||
git_reset::ResetMode::Mixed => ResetMode::Mixed,
|
||||
};
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.reset(&envelope.payload.commit, mode)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_open_commit_message_buffer(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::OpenCommitMessageBuffer>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::OpenBufferResponse> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
let buffer = repository
|
||||
.update(&mut cx, |repository, cx| {
|
||||
repository.open_commit_buffer(None, this.read(cx).buffer_store.clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let buffer_id = buffer.read_with(&cx, |buffer, _| buffer.remote_id())?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.buffer_store.update(cx, |buffer_store, cx| {
|
||||
buffer_store
|
||||
.create_buffer_for_peer(
|
||||
&buffer,
|
||||
envelope.original_sender_id.unwrap_or(envelope.sender_id),
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(proto::OpenBufferResponse {
|
||||
buffer_id: buffer_id.to_proto(),
|
||||
})
|
||||
}
|
||||
|
||||
fn repository_for_request(
|
||||
this: &Entity<Self>,
|
||||
worktree_id: WorktreeId,
|
||||
work_directory_id: ProjectEntryId,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Entity<Repository>> {
|
||||
this.update(cx, |this, cx| {
|
||||
let repository_handle = this
|
||||
.all_repositories()
|
||||
.into_iter()
|
||||
.find(|repository_handle| {
|
||||
repository_handle.read(cx).worktree_id == worktree_id
|
||||
&& repository_handle
|
||||
.read(cx)
|
||||
.repository_entry
|
||||
.work_directory_id()
|
||||
== work_directory_id
|
||||
})
|
||||
.context("missing repository handle")?;
|
||||
anyhow::Ok(repository_handle)
|
||||
})?
|
||||
}
|
||||
}
|
||||
|
||||
impl GitRepo {}
|
||||
|
||||
impl Repository {
|
||||
pub fn git_state(&self) -> Option<Entity<GitState>> {
|
||||
self.git_state.upgrade()
|
||||
pub fn git_store(&self) -> Option<Entity<GitStore>> {
|
||||
self.git_store.upgrade()
|
||||
}
|
||||
|
||||
fn id(&self) -> (WorktreeId, ProjectEntryId) {
|
||||
(self.worktree_id, self.repository_entry.work_directory_id())
|
||||
}
|
||||
|
||||
pub fn branch(&self) -> Option<Arc<str>> {
|
||||
pub fn branch(&self) -> Option<&Branch> {
|
||||
self.repository_entry.branch()
|
||||
}
|
||||
|
||||
|
@ -344,19 +592,19 @@ impl Repository {
|
|||
}
|
||||
|
||||
pub fn activate(&self, cx: &mut Context<Self>) {
|
||||
let Some(git_state) = self.git_state.upgrade() else {
|
||||
let Some(git_store) = self.git_store.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let entity = cx.entity();
|
||||
git_state.update(cx, |git_state, cx| {
|
||||
let Some(index) = git_state
|
||||
git_store.update(cx, |git_store, cx| {
|
||||
let Some(index) = git_store
|
||||
.repositories
|
||||
.iter()
|
||||
.position(|handle| *handle == entity)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
git_state.active_index = Some(index);
|
||||
git_store.active_index = Some(index);
|
||||
cx.emit(GitEvent::ActiveRepositoryChanged);
|
||||
});
|
||||
}
|
||||
|
@ -396,7 +644,7 @@ impl Repository {
|
|||
languages: Option<Arc<LanguageRegistry>>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<anyhow::Result<Entity<Buffer>>> {
|
||||
) -> Task<Result<Entity<Buffer>>> {
|
||||
if let Some(buffer) = self.commit_message_buffer.clone() {
|
||||
return Task::ready(Ok(buffer));
|
||||
}
|
||||
|
@ -444,7 +692,7 @@ impl Repository {
|
|||
language_registry: Option<Arc<LanguageRegistry>>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<anyhow::Result<Entity<Buffer>>> {
|
||||
) -> Task<Result<Entity<Buffer>>> {
|
||||
cx.spawn(|repository, mut cx| async move {
|
||||
let buffer = buffer_store
|
||||
.update(&mut cx, |buffer_store, cx| buffer_store.create_buffer(cx))?
|
||||
|
@ -464,7 +712,57 @@ impl Repository {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn stage_entries(&self, entries: Vec<RepoPath>) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
pub fn reset(&self, commit: &str, reset_mode: ResetMode) -> oneshot::Receiver<Result<()>> {
|
||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||
let commit = commit.to_string().into();
|
||||
self.update_sender
|
||||
.unbounded_send((
|
||||
Message::Reset {
|
||||
repo: self.git_repo.clone(),
|
||||
commit,
|
||||
reset_mode,
|
||||
},
|
||||
result_tx,
|
||||
))
|
||||
.ok();
|
||||
result_rx
|
||||
}
|
||||
|
||||
pub fn show(&self, commit: &str, cx: &Context<Self>) -> Task<Result<CommitDetails>> {
|
||||
let commit = commit.to_string();
|
||||
match self.git_repo.clone() {
|
||||
GitRepo::Local(git_repository) => {
|
||||
let commit = commit.to_string();
|
||||
cx.background_executor()
|
||||
.spawn(async move { git_repository.show(&commit) })
|
||||
}
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => cx.background_executor().spawn(async move {
|
||||
let resp = client
|
||||
.request(proto::GitShow {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
commit,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(CommitDetails {
|
||||
sha: resp.sha.into(),
|
||||
message: resp.message.into(),
|
||||
commit_timestamp: resp.commit_timestamp,
|
||||
committer_email: resp.committer_email.into(),
|
||||
committer_name: resp.committer_name.into(),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stage_entries(&self, entries: Vec<RepoPath>) -> oneshot::Receiver<Result<()>> {
|
||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||
if entries.is_empty() {
|
||||
result_tx.send(Ok(())).ok();
|
||||
|
@ -476,7 +774,7 @@ impl Repository {
|
|||
result_rx
|
||||
}
|
||||
|
||||
pub fn unstage_entries(&self, entries: Vec<RepoPath>) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
pub fn unstage_entries(&self, entries: Vec<RepoPath>) -> oneshot::Receiver<Result<()>> {
|
||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||
if entries.is_empty() {
|
||||
result_tx.send(Ok(())).ok();
|
||||
|
@ -488,7 +786,7 @@ impl Repository {
|
|||
result_rx
|
||||
}
|
||||
|
||||
pub fn stage_all(&self) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
pub fn stage_all(&self) -> oneshot::Receiver<Result<()>> {
|
||||
let to_stage = self
|
||||
.repository_entry
|
||||
.status()
|
||||
|
@ -498,7 +796,7 @@ impl Repository {
|
|||
self.stage_entries(to_stage)
|
||||
}
|
||||
|
||||
pub fn unstage_all(&self) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
pub fn unstage_all(&self) -> oneshot::Receiver<Result<()>> {
|
||||
let to_unstage = self
|
||||
.repository_entry
|
||||
.status()
|
||||
|
@ -530,7 +828,7 @@ impl Repository {
|
|||
&self,
|
||||
message: SharedString,
|
||||
name_and_email: Option<(SharedString, SharedString)>,
|
||||
) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||
self.update_sender
|
||||
.unbounded_send((
|
||||
|
|
|
@ -27,7 +27,7 @@ use git::Repository;
|
|||
pub mod search_history;
|
||||
mod yarn;
|
||||
|
||||
use crate::git::GitState;
|
||||
use crate::git::GitStore;
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use buffer_store::{BufferStore, BufferStoreEvent};
|
||||
use client::{
|
||||
|
@ -161,7 +161,7 @@ pub struct Project {
|
|||
fs: Arc<dyn Fs>,
|
||||
ssh_client: Option<Entity<SshRemoteClient>>,
|
||||
client_state: ProjectClientState,
|
||||
git_state: Entity<GitState>,
|
||||
git_store: Entity<GitStore>,
|
||||
collaborators: HashMap<proto::PeerId, Collaborator>,
|
||||
client_subscriptions: Vec<client::Subscription>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
|
@ -610,15 +610,10 @@ impl Project {
|
|||
client.add_entity_request_handler(Self::handle_open_new_buffer);
|
||||
client.add_entity_message_handler(Self::handle_create_buffer_for_peer);
|
||||
|
||||
client.add_entity_request_handler(Self::handle_stage);
|
||||
client.add_entity_request_handler(Self::handle_unstage);
|
||||
client.add_entity_request_handler(Self::handle_commit);
|
||||
client.add_entity_request_handler(Self::handle_set_index_text);
|
||||
client.add_entity_request_handler(Self::handle_open_commit_message_buffer);
|
||||
|
||||
WorktreeStore::init(&client);
|
||||
BufferStore::init(&client);
|
||||
LspStore::init(&client);
|
||||
GitStore::init(&client);
|
||||
SettingsObserver::init(&client);
|
||||
TaskStore::init(Some(&client));
|
||||
ToolchainStore::init(&client);
|
||||
|
@ -705,7 +700,8 @@ impl Project {
|
|||
)
|
||||
});
|
||||
|
||||
let git_state = cx.new(|cx| GitState::new(&worktree_store, None, None, cx));
|
||||
let git_store =
|
||||
cx.new(|cx| GitStore::new(&worktree_store, buffer_store.clone(), None, None, cx));
|
||||
|
||||
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
||||
|
||||
|
@ -718,7 +714,7 @@ impl Project {
|
|||
lsp_store,
|
||||
join_project_response_message_id: 0,
|
||||
client_state: ProjectClientState::Local,
|
||||
git_state,
|
||||
git_store,
|
||||
client_subscriptions: Vec::new(),
|
||||
_subscriptions: vec![cx.on_release(Self::release)],
|
||||
active_entry: None,
|
||||
|
@ -825,9 +821,10 @@ impl Project {
|
|||
});
|
||||
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
||||
|
||||
let git_state = cx.new(|cx| {
|
||||
GitState::new(
|
||||
let git_store = cx.new(|cx| {
|
||||
GitStore::new(
|
||||
&worktree_store,
|
||||
buffer_store.clone(),
|
||||
Some(ssh_proto.clone()),
|
||||
Some(ProjectId(SSH_PROJECT_ID)),
|
||||
cx,
|
||||
|
@ -846,7 +843,7 @@ impl Project {
|
|||
lsp_store,
|
||||
join_project_response_message_id: 0,
|
||||
client_state: ProjectClientState::Local,
|
||||
git_state,
|
||||
git_store,
|
||||
client_subscriptions: Vec::new(),
|
||||
_subscriptions: vec![
|
||||
cx.on_release(Self::release),
|
||||
|
@ -896,6 +893,7 @@ impl Project {
|
|||
ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.worktree_store);
|
||||
ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.lsp_store);
|
||||
ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.settings_observer);
|
||||
ssh.subscribe_to_entity(SSH_PROJECT_ID, &this.git_store);
|
||||
|
||||
ssh_proto.add_entity_message_handler(Self::handle_create_buffer_for_peer);
|
||||
ssh_proto.add_entity_message_handler(Self::handle_update_worktree);
|
||||
|
@ -909,6 +907,7 @@ impl Project {
|
|||
SettingsObserver::init(&ssh_proto);
|
||||
TaskStore::init(Some(&ssh_proto));
|
||||
ToolchainStore::init(&ssh_proto);
|
||||
GitStore::init(&ssh_proto);
|
||||
|
||||
this
|
||||
})
|
||||
|
@ -1030,9 +1029,10 @@ impl Project {
|
|||
SettingsObserver::new_remote(worktree_store.clone(), task_store.clone(), cx)
|
||||
})?;
|
||||
|
||||
let git_state = cx.new(|cx| {
|
||||
GitState::new(
|
||||
let git_store = cx.new(|cx| {
|
||||
GitStore::new(
|
||||
&worktree_store,
|
||||
buffer_store.clone(),
|
||||
Some(client.clone().into()),
|
||||
Some(ProjectId(remote_id)),
|
||||
cx,
|
||||
|
@ -1089,7 +1089,7 @@ impl Project {
|
|||
remote_id,
|
||||
replica_id,
|
||||
},
|
||||
git_state,
|
||||
git_store,
|
||||
buffers_needing_diff: Default::default(),
|
||||
git_diff_debouncer: DebouncedDelay::new(),
|
||||
terminals: Terminals {
|
||||
|
@ -1675,6 +1675,9 @@ impl Project {
|
|||
self.client
|
||||
.subscribe_to_entity(project_id)?
|
||||
.set_entity(&self.settings_observer, &mut cx.to_async()),
|
||||
self.client
|
||||
.subscribe_to_entity(project_id)?
|
||||
.set_entity(&self.git_store, &mut cx.to_async()),
|
||||
]);
|
||||
|
||||
self.buffer_store.update(cx, |buffer_store, cx| {
|
||||
|
@ -4038,142 +4041,6 @@ impl Project {
|
|||
Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
|
||||
}
|
||||
|
||||
async fn handle_stage(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Stage>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let entries = envelope
|
||||
.payload
|
||||
.paths
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.map(RepoPath::new)
|
||||
.collect();
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.stage_entries(entries)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_unstage(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Unstage>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let entries = envelope
|
||||
.payload
|
||||
.paths
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.map(RepoPath::new)
|
||||
.collect();
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.unstage_entries(entries)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_commit(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::Commit>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
let message = SharedString::from(envelope.payload.message);
|
||||
let name = envelope.payload.name.map(SharedString::from);
|
||||
let email = envelope.payload.email.map(SharedString::from);
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.commit(message, name.zip(email))
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_set_index_text(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::SetIndexText>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::Ack> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.set_index_text(
|
||||
&RepoPath::from_str(&envelope.payload.path),
|
||||
envelope.payload.text,
|
||||
)
|
||||
})?
|
||||
.await??;
|
||||
Ok(proto::Ack {})
|
||||
}
|
||||
|
||||
async fn handle_open_commit_message_buffer(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::OpenCommitMessageBuffer>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::OpenBufferResponse> {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let work_directory_id = ProjectEntryId::from_proto(envelope.payload.work_directory_id);
|
||||
let repository_handle =
|
||||
Self::repository_for_request(&this, worktree_id, work_directory_id, &mut cx)?;
|
||||
let buffer = repository_handle
|
||||
.update(&mut cx, |repository_handle, cx| {
|
||||
repository_handle.open_commit_buffer(None, this.read(cx).buffer_store.clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let peer_id = envelope.original_sender_id()?;
|
||||
Project::respond_to_open_buffer_request(this, buffer, peer_id, &mut cx)
|
||||
}
|
||||
|
||||
fn repository_for_request(
|
||||
this: &Entity<Self>,
|
||||
worktree_id: WorktreeId,
|
||||
work_directory_id: ProjectEntryId,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<Entity<Repository>> {
|
||||
this.update(cx, |project, cx| {
|
||||
let repository_handle = project
|
||||
.git_state()
|
||||
.read(cx)
|
||||
.all_repositories()
|
||||
.into_iter()
|
||||
.find(|repository_handle| {
|
||||
let repository_handle = repository_handle.read(cx);
|
||||
repository_handle.worktree_id == worktree_id
|
||||
&& repository_handle.repository_entry.work_directory_id()
|
||||
== work_directory_id
|
||||
})
|
||||
.context("missing repository handle")?;
|
||||
anyhow::Ok(repository_handle)
|
||||
})?
|
||||
}
|
||||
|
||||
fn respond_to_open_buffer_request(
|
||||
this: Entity<Self>,
|
||||
buffer: Entity<Buffer>,
|
||||
|
@ -4365,16 +4232,16 @@ impl Project {
|
|||
&self.buffer_store
|
||||
}
|
||||
|
||||
pub fn git_state(&self) -> &Entity<GitState> {
|
||||
&self.git_state
|
||||
pub fn git_store(&self) -> &Entity<GitStore> {
|
||||
&self.git_store
|
||||
}
|
||||
|
||||
pub fn active_repository(&self, cx: &App) -> Option<Entity<Repository>> {
|
||||
self.git_state.read(cx).active_repository()
|
||||
self.git_store.read(cx).active_repository()
|
||||
}
|
||||
|
||||
pub fn all_repositories(&self, cx: &App) -> Vec<Entity<Repository>> {
|
||||
self.git_state.read(cx).all_repositories()
|
||||
self.git_store.read(cx).all_repositories()
|
||||
}
|
||||
|
||||
pub fn repository_and_path_for_buffer_id(
|
||||
|
@ -4386,7 +4253,7 @@ impl Project {
|
|||
.buffer_for_id(buffer_id, cx)?
|
||||
.read(cx)
|
||||
.project_path(cx)?;
|
||||
self.git_state
|
||||
self.git_store
|
||||
.read(cx)
|
||||
.all_repositories()
|
||||
.into_iter()
|
||||
|
|
|
@ -12,6 +12,7 @@ use futures::{
|
|||
future::{BoxFuture, Shared},
|
||||
FutureExt, SinkExt,
|
||||
};
|
||||
use git::repository::Branch;
|
||||
use gpui::{App, AsyncApp, Context, Entity, EntityId, EventEmitter, Task, WeakEntity};
|
||||
use postage::oneshot;
|
||||
use rpc::{
|
||||
|
@ -24,7 +25,10 @@ use smol::{
|
|||
};
|
||||
use text::ReplicaId;
|
||||
use util::{paths::SanitizedPath, ResultExt};
|
||||
use worktree::{Entry, ProjectEntryId, UpdatedEntriesSet, Worktree, WorktreeId, WorktreeSettings};
|
||||
use worktree::{
|
||||
branch_to_proto, Entry, ProjectEntryId, UpdatedEntriesSet, Worktree, WorktreeId,
|
||||
WorktreeSettings,
|
||||
};
|
||||
|
||||
use crate::{search::SearchQuery, ProjectPath};
|
||||
|
||||
|
@ -133,11 +137,12 @@ impl WorktreeStore {
|
|||
.find(|worktree| worktree.read(cx).id() == id)
|
||||
}
|
||||
|
||||
pub fn current_branch(&self, repository: ProjectPath, cx: &App) -> Option<Arc<str>> {
|
||||
pub fn current_branch(&self, repository: ProjectPath, cx: &App) -> Option<Branch> {
|
||||
self.worktree_for_id(repository.worktree_id, cx)?
|
||||
.read(cx)
|
||||
.git_entry(repository.path)?
|
||||
.branch()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn worktree_for_entry(
|
||||
|
@ -938,9 +943,24 @@ impl WorktreeStore {
|
|||
.map(|proto_branch| git::repository::Branch {
|
||||
is_head: proto_branch.is_head,
|
||||
name: proto_branch.name.into(),
|
||||
unix_timestamp: proto_branch
|
||||
.unix_timestamp
|
||||
.map(|timestamp| timestamp as i64),
|
||||
upstream: proto_branch.upstream.map(|upstream| {
|
||||
git::repository::Upstream {
|
||||
ref_name: upstream.ref_name.into(),
|
||||
tracking: upstream.tracking.map(|tracking| {
|
||||
git::repository::UpstreamTracking {
|
||||
ahead: tracking.ahead as u32,
|
||||
behind: tracking.behind as u32,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
most_recent_commit: proto_branch.most_recent_commit.map(|commit| {
|
||||
git::repository::CommitSummary {
|
||||
sha: commit.sha.into(),
|
||||
subject: commit.subject.into(),
|
||||
commit_timestamp: commit.commit_timestamp,
|
||||
}
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1126,14 +1146,7 @@ impl WorktreeStore {
|
|||
.await?;
|
||||
|
||||
Ok(proto::GitBranchesResponse {
|
||||
branches: branches
|
||||
.into_iter()
|
||||
.map(|branch| proto::Branch {
|
||||
is_head: branch.is_head,
|
||||
name: branch.name.to_string(),
|
||||
unix_timestamp: branch.unix_timestamp.map(|timestamp| timestamp as u64),
|
||||
})
|
||||
.collect(),
|
||||
branches: branches.iter().map(branch_to_proto).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue