Add file history view (#42441)
Closes #16827 Release Notes: - Added: File history view accessible via right-click context menu on files in the editor or project panel. Shows commit history for the selected file with author, timestamp, and commit message. Clicking a commit opens a diff view filtered to show only changes for that specific file. <img width="1293" height="834" alt="Screenshot 2025-11-11 at 16 31 32" src="https://github.com/user-attachments/assets/3780d21b-a719-40b3-955c-d928c45a47cc" /> <img width="1283" height="836" alt="Screenshot 2025-11-11 at 16 31 24" src="https://github.com/user-attachments/assets/1dc4e56b-b225-4ffa-a2af-c5dcfb2efaa0" /> --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com>
This commit is contained in:
@@ -488,6 +488,7 @@ impl GitStore {
|
||||
client.add_entity_request_handler(Self::handle_reset);
|
||||
client.add_entity_request_handler(Self::handle_show);
|
||||
client.add_entity_request_handler(Self::handle_load_commit_diff);
|
||||
client.add_entity_request_handler(Self::handle_file_history);
|
||||
client.add_entity_request_handler(Self::handle_checkout_files);
|
||||
client.add_entity_request_handler(Self::handle_open_commit_message_buffer);
|
||||
client.add_entity_request_handler(Self::handle_set_index_text);
|
||||
@@ -1057,6 +1058,30 @@ impl GitStore {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file_history(
|
||||
&self,
|
||||
repo: &Entity<Repository>,
|
||||
path: RepoPath,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<git::repository::FileHistory>> {
|
||||
let rx = repo.update(cx, |repo, _| repo.file_history(path));
|
||||
|
||||
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
||||
}
|
||||
|
||||
pub fn file_history_paginated(
|
||||
&self,
|
||||
repo: &Entity<Repository>,
|
||||
path: RepoPath,
|
||||
skip: usize,
|
||||
limit: Option<usize>,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<git::repository::FileHistory>> {
|
||||
let rx = repo.update(cx, |repo, _| repo.file_history_paginated(path, skip, limit));
|
||||
|
||||
cx.spawn(|_: &mut AsyncApp| async move { rx.await? })
|
||||
}
|
||||
|
||||
pub fn get_permalink_to_line(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
@@ -2314,6 +2339,40 @@ impl GitStore {
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_file_history(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitFileHistory>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::GitFileHistoryResponse> {
|
||||
let repository_id = RepositoryId::from_proto(envelope.payload.repository_id);
|
||||
let repository_handle = Self::repository_for_request(&this, repository_id, &mut cx)?;
|
||||
let path = RepoPath::from_proto(&envelope.payload.path)?;
|
||||
let skip = envelope.payload.skip as usize;
|
||||
let limit = envelope.payload.limit.map(|l| l as usize);
|
||||
|
||||
let file_history = repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.file_history_paginated(path, skip, limit)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(proto::GitFileHistoryResponse {
|
||||
entries: file_history
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| proto::FileHistoryEntry {
|
||||
sha: entry.sha.to_string(),
|
||||
subject: entry.subject.to_string(),
|
||||
message: entry.message.to_string(),
|
||||
commit_timestamp: entry.commit_timestamp,
|
||||
author_name: entry.author_name.to_string(),
|
||||
author_email: entry.author_email.to_string(),
|
||||
})
|
||||
.collect(),
|
||||
path: file_history.path.to_proto(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_reset(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GitReset>,
|
||||
@@ -4016,6 +4075,55 @@ impl Repository {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file_history(
|
||||
&mut self,
|
||||
path: RepoPath,
|
||||
) -> oneshot::Receiver<Result<git::repository::FileHistory>> {
|
||||
self.file_history_paginated(path, 0, None)
|
||||
}
|
||||
|
||||
pub fn file_history_paginated(
|
||||
&mut self,
|
||||
path: RepoPath,
|
||||
skip: usize,
|
||||
limit: Option<usize>,
|
||||
) -> oneshot::Receiver<Result<git::repository::FileHistory>> {
|
||||
let id = self.id;
|
||||
self.send_job(None, move |git_repo, _cx| async move {
|
||||
match git_repo {
|
||||
RepositoryState::Local(LocalRepositoryState { backend, .. }) => {
|
||||
backend.file_history_paginated(path, skip, limit).await
|
||||
}
|
||||
RepositoryState::Remote(RemoteRepositoryState { client, project_id }) => {
|
||||
let response = client
|
||||
.request(proto::GitFileHistory {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
path: path.to_proto(),
|
||||
skip: skip as u64,
|
||||
limit: limit.map(|l| l as u64),
|
||||
})
|
||||
.await?;
|
||||
Ok(git::repository::FileHistory {
|
||||
entries: response
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| git::repository::FileHistoryEntry {
|
||||
sha: entry.sha.into(),
|
||||
subject: entry.subject.into(),
|
||||
message: entry.message.into(),
|
||||
commit_timestamp: entry.commit_timestamp,
|
||||
author_name: entry.author_name.into(),
|
||||
author_email: entry.author_email.into(),
|
||||
})
|
||||
.collect(),
|
||||
path: RepoPath::from_proto(&response.path)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn buffer_store(&self, cx: &App) -> Option<Entity<BufferStore>> {
|
||||
Some(self.git_store.upgrade()?.read(cx).buffer_store.clone())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user