Implement staging and unstaging hunks (#24606)
- [x] Staging hunks - [x] Unstaging hunks - [x] Write a randomized test - [x] Get test passing - [x] Fix existing bug in diff_base_byte_range computation - [x] Remote project support - [ ] ~~Improve performance of buffer_range_to_unchanged_diff_base_range~~ - [ ] ~~Bug: project diff editor scrolls to top when staging/unstaging hunk~~ existing issue - [ ] ~~UI~~ deferred - [x] Tricky cases - [x] Correctly handle acting on multiple hunks for a single file - [x] Remove path from index when unstaging the last staged hunk, if it's absent from HEAD, or staging the only hunk, if it's deleted in the working copy Release Notes: - Add `ToggleStagedSelectedDiffHunks` action for staging and unstaging individual diff hunks
This commit is contained in:
parent
ea8da43c6b
commit
eea6b526dc
18 changed files with 768 additions and 70 deletions
|
@ -189,6 +189,7 @@ impl BufferDiffState {
|
|||
buffer: text::BufferSnapshot,
|
||||
cx: &mut Context<Self>,
|
||||
) -> oneshot::Receiver<()> {
|
||||
log::debug!("recalculate diffs");
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.diff_updated_futures.push(tx);
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ use util::{maybe, ResultExt};
|
|||
use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry};
|
||||
|
||||
pub struct GitState {
|
||||
project_id: Option<ProjectId>,
|
||||
client: Option<AnyProtoClient>,
|
||||
pub(super) project_id: Option<ProjectId>,
|
||||
pub(super) client: Option<AnyProtoClient>,
|
||||
pub update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<anyhow::Result<()>>)>,
|
||||
repositories: Vec<Entity<Repository>>,
|
||||
active_index: Option<usize>,
|
||||
update_sender: mpsc::UnboundedSender<(Message, oneshot::Sender<anyhow::Result<()>>)>,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ pub enum GitRepo {
|
|||
},
|
||||
}
|
||||
|
||||
enum Message {
|
||||
pub enum Message {
|
||||
Commit {
|
||||
git_repo: GitRepo,
|
||||
message: SharedString,
|
||||
|
@ -59,6 +59,7 @@ enum Message {
|
|||
},
|
||||
Stage(GitRepo, Vec<RepoPath>),
|
||||
Unstage(GitRepo, Vec<RepoPath>),
|
||||
SetIndexText(GitRepo, RepoPath, Option<String>),
|
||||
}
|
||||
|
||||
pub enum GitEvent {
|
||||
|
@ -291,11 +292,32 @@ impl GitState {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Message::SetIndexText(git_repo, path, text) => match git_repo {
|
||||
GitRepo::Local(repo) => repo.set_index_text(&path, text),
|
||||
GitRepo::Remote {
|
||||
project_id,
|
||||
client,
|
||||
worktree_id,
|
||||
work_directory_id,
|
||||
} => client.send(proto::SetIndexText {
|
||||
project_id: project_id.0,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
work_directory_id: work_directory_id.to_proto(),
|
||||
path: path.as_ref().to_proto(),
|
||||
text,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GitRepo {}
|
||||
|
||||
impl Repository {
|
||||
pub fn git_state(&self) -> Option<Entity<GitState>> {
|
||||
self.git_state.upgrade()
|
||||
}
|
||||
|
||||
fn id(&self) -> (WorktreeId, ProjectEntryId) {
|
||||
(self.worktree_id, self.repository_entry.work_directory_id())
|
||||
}
|
||||
|
@ -522,4 +544,19 @@ impl Repository {
|
|||
.ok();
|
||||
result_rx
|
||||
}
|
||||
|
||||
pub fn set_index_text(
|
||||
&self,
|
||||
path: &RepoPath,
|
||||
content: Option<String>,
|
||||
) -> oneshot::Receiver<anyhow::Result<()>> {
|
||||
let (result_tx, result_rx) = futures::channel::oneshot::channel();
|
||||
self.update_sender
|
||||
.unbounded_send((
|
||||
Message::SetIndexText(self.git_repo.clone(), path.clone(), content),
|
||||
result_tx,
|
||||
))
|
||||
.ok();
|
||||
result_rx
|
||||
}
|
||||
}
|
||||
|
|
|
@ -610,6 +610,7 @@ impl Project {
|
|||
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);
|
||||
|
@ -4092,6 +4093,27 @@ impl Project {
|
|||
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>,
|
||||
|
@ -4336,6 +4358,27 @@ impl Project {
|
|||
pub fn all_repositories(&self, cx: &App) -> Vec<Entity<Repository>> {
|
||||
self.git_state.read(cx).all_repositories()
|
||||
}
|
||||
|
||||
pub fn repository_and_path_for_buffer_id(
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
cx: &App,
|
||||
) -> Option<(Entity<Repository>, RepoPath)> {
|
||||
let path = self
|
||||
.buffer_for_id(buffer_id, cx)?
|
||||
.read(cx)
|
||||
.project_path(cx)?;
|
||||
self.git_state
|
||||
.read(cx)
|
||||
.all_repositories()
|
||||
.into_iter()
|
||||
.find_map(|repo| {
|
||||
Some((
|
||||
repo.clone(),
|
||||
repo.read(cx).repository_entry.relativize(&path.path).ok()?,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_code_actions(code_actions: &HashMap<String, bool>) -> Vec<lsp::CodeActionKind> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue