Merge branch 'main' of https://github.com/jacobtread/zed into feat-git-commit-list

This commit is contained in:
Jacobtread 2025-08-22 20:56:08 +12:00
commit eb01cd2ad3
1049 changed files with 30391 additions and 19547 deletions

View file

@ -561,7 +561,7 @@ impl GitStore {
pub fn active_repository(&self) -> Option<Entity<Repository>> {
self.active_repo_id
.as_ref()
.map(|id| self.repositories[&id].clone())
.map(|id| self.repositories[id].clone())
}
pub fn open_unstaged_diff(
@ -570,23 +570,22 @@ impl GitStore {
cx: &mut Context<Self>,
) -> Task<Result<Entity<BufferDiff>>> {
let buffer_id = buffer.read(cx).remote_id();
if let Some(diff_state) = self.diffs.get(&buffer_id) {
if let Some(unstaged_diff) = diff_state
if let Some(diff_state) = self.diffs.get(&buffer_id)
&& let Some(unstaged_diff) = diff_state
.read(cx)
.unstaged_diff
.as_ref()
.and_then(|weak| weak.upgrade())
{
if let Some(task) =
diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation())
{
if let Some(task) =
diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation())
{
return cx.background_executor().spawn(async move {
task.await;
Ok(unstaged_diff)
});
}
return Task::ready(Ok(unstaged_diff));
return cx.background_executor().spawn(async move {
task.await;
Ok(unstaged_diff)
});
}
return Task::ready(Ok(unstaged_diff));
}
let Some((repo, repo_path)) =
@ -627,23 +626,22 @@ impl GitStore {
) -> Task<Result<Entity<BufferDiff>>> {
let buffer_id = buffer.read(cx).remote_id();
if let Some(diff_state) = self.diffs.get(&buffer_id) {
if let Some(uncommitted_diff) = diff_state
if let Some(diff_state) = self.diffs.get(&buffer_id)
&& let Some(uncommitted_diff) = diff_state
.read(cx)
.uncommitted_diff
.as_ref()
.and_then(|weak| weak.upgrade())
{
if let Some(task) =
diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation())
{
if let Some(task) =
diff_state.update(cx, |diff_state, _| diff_state.wait_for_recalculation())
{
return cx.background_executor().spawn(async move {
task.await;
Ok(uncommitted_diff)
});
}
return Task::ready(Ok(uncommitted_diff));
return cx.background_executor().spawn(async move {
task.await;
Ok(uncommitted_diff)
});
}
return Task::ready(Ok(uncommitted_diff));
}
let Some((repo, repo_path)) =
@ -764,29 +762,26 @@ impl GitStore {
log::debug!("open conflict set");
let buffer_id = buffer.read(cx).remote_id();
if let Some(git_state) = self.diffs.get(&buffer_id) {
if let Some(conflict_set) = git_state
if let Some(git_state) = self.diffs.get(&buffer_id)
&& let Some(conflict_set) = git_state
.read(cx)
.conflict_set
.as_ref()
.and_then(|weak| weak.upgrade())
{
let conflict_set = conflict_set.clone();
let buffer_snapshot = buffer.read(cx).text_snapshot();
{
let conflict_set = conflict_set;
let buffer_snapshot = buffer.read(cx).text_snapshot();
git_state.update(cx, |state, cx| {
let _ = state.reparse_conflict_markers(buffer_snapshot, cx);
});
git_state.update(cx, |state, cx| {
let _ = state.reparse_conflict_markers(buffer_snapshot, cx);
});
return conflict_set;
}
return conflict_set;
}
let is_unmerged = self
.repository_and_path_for_buffer_id(buffer_id, cx)
.map_or(false, |(repo, path)| {
repo.read(cx).snapshot.has_conflict(&path)
});
.is_some_and(|(repo, path)| repo.read(cx).snapshot.has_conflict(&path));
let git_store = cx.weak_entity();
let buffer_git_state = self
.diffs
@ -917,7 +912,7 @@ impl GitStore {
return Task::ready(Err(anyhow!("failed to find a git repository for buffer")));
};
let content = match &version {
Some(version) => buffer.rope_for_version(version).clone(),
Some(version) => buffer.rope_for_version(version),
None => buffer.as_rope().clone(),
};
let version = version.unwrap_or(buffer.version());
@ -1151,29 +1146,26 @@ impl GitStore {
for (buffer_id, diff) in self.diffs.iter() {
if let Some((buffer_repo, repo_path)) =
self.repository_and_path_for_buffer_id(*buffer_id, cx)
&& buffer_repo == repo
{
if buffer_repo == repo {
diff.update(cx, |diff, cx| {
if let Some(conflict_set) = &diff.conflict_set {
let conflict_status_changed =
conflict_set.update(cx, |conflict_set, cx| {
let has_conflict = repo_snapshot.has_conflict(&repo_path);
conflict_set.set_has_conflict(has_conflict, cx)
})?;
if conflict_status_changed {
let buffer_store = self.buffer_store.read(cx);
if let Some(buffer) = buffer_store.get(*buffer_id) {
let _ = diff.reparse_conflict_markers(
buffer.read(cx).text_snapshot(),
cx,
);
}
diff.update(cx, |diff, cx| {
if let Some(conflict_set) = &diff.conflict_set {
let conflict_status_changed =
conflict_set.update(cx, |conflict_set, cx| {
let has_conflict = repo_snapshot.has_conflict(&repo_path);
conflict_set.set_has_conflict(has_conflict, cx)
})?;
if conflict_status_changed {
let buffer_store = self.buffer_store.read(cx);
if let Some(buffer) = buffer_store.get(*buffer_id) {
let _ = diff
.reparse_conflict_markers(buffer.read(cx).text_snapshot(), cx);
}
}
anyhow::Ok(())
})
.ok();
}
}
anyhow::Ok(())
})
.ok();
}
}
cx.emit(GitStoreEvent::RepositoryUpdated(
@ -1277,7 +1269,7 @@ impl GitStore {
) {
match event {
BufferStoreEvent::BufferAdded(buffer) => {
cx.subscribe(&buffer, |this, buffer, event, cx| {
cx.subscribe(buffer, |this, buffer, event, cx| {
if let BufferEvent::LanguageChanged = event {
let buffer_id = buffer.read(cx).remote_id();
if let Some(diff_state) = this.diffs.get(&buffer_id) {
@ -1295,7 +1287,7 @@ impl GitStore {
}
}
BufferStoreEvent::BufferDropped(buffer_id) => {
self.diffs.remove(&buffer_id);
self.diffs.remove(buffer_id);
for diffs in self.shared_diffs.values_mut() {
diffs.remove(buffer_id);
}
@ -1384,8 +1376,8 @@ impl GitStore {
repository.update(cx, |repository, cx| {
let repo_abs_path = &repository.work_directory_abs_path;
if changed_repos.iter().any(|update| {
update.old_work_directory_abs_path.as_ref() == Some(&repo_abs_path)
|| update.new_work_directory_abs_path.as_ref() == Some(&repo_abs_path)
update.old_work_directory_abs_path.as_ref() == Some(repo_abs_path)
|| update.new_work_directory_abs_path.as_ref() == Some(repo_abs_path)
}) {
repository.reload_buffer_diff_bases(cx);
}
@ -1514,10 +1506,7 @@ impl GitStore {
let mut update = envelope.payload;
let id = RepositoryId::from_proto(update.id);
let client = this
.upstream_client()
.context("no upstream client")?
.clone();
let client = this.upstream_client().context("no upstream client")?;
let mut is_new = false;
let repo = this.repositories.entry(id).or_insert_with(|| {
@ -1536,7 +1525,7 @@ impl GitStore {
});
if is_new {
this._subscriptions
.push(cx.subscribe(&repo, Self::on_repository_event))
.push(cx.subscribe(repo, Self::on_repository_event))
}
repo.update(cx, {
@ -2231,13 +2220,13 @@ impl GitStore {
) -> Result<()> {
let buffer_id = BufferId::new(request.payload.buffer_id)?;
this.update(&mut cx, |this, cx| {
if let Some(diff_state) = this.diffs.get_mut(&buffer_id) {
if let Some(buffer) = this.buffer_store.read(cx).get(buffer_id) {
let buffer = buffer.read(cx).text_snapshot();
diff_state.update(cx, |diff_state, cx| {
diff_state.handle_base_texts_updated(buffer, request.payload, cx);
})
}
if let Some(diff_state) = this.diffs.get_mut(&buffer_id)
&& let Some(buffer) = this.buffer_store.read(cx).get(buffer_id)
{
let buffer = buffer.read(cx).text_snapshot();
diff_state.update(cx, |diff_state, cx| {
diff_state.handle_base_texts_updated(buffer, request.payload, cx);
})
}
})
}
@ -2353,7 +2342,7 @@ impl GitStore {
// All paths prefixed by a given repo will constitute a continuous range.
while let Some(path) = entries.get(ix)
&& let Some(repo_path) =
RepositorySnapshot::abs_path_to_repo_path_inner(&repo_path, &path)
RepositorySnapshot::abs_path_to_repo_path_inner(&repo_path, path)
{
paths.push((repo_path, ix));
ix += 1;
@ -2507,14 +2496,14 @@ impl BufferGitState {
pub fn wait_for_recalculation(&mut self) -> Option<impl Future<Output = ()> + use<>> {
if *self.recalculating_tx.borrow() {
let mut rx = self.recalculating_tx.subscribe();
return Some(async move {
Some(async move {
loop {
let is_recalculating = rx.recv().await;
if is_recalculating != Some(true) {
break;
}
}
});
})
} else {
None
}
@ -2774,6 +2763,7 @@ impl RepositorySnapshot {
.iter()
.map(|repo_path| repo_path.to_proto())
.collect(),
merge_message: self.merge.message.as_ref().map(|msg| msg.to_string()),
project_id,
id: self.id.to_proto(),
abs_path: self.work_directory_abs_path.to_proto(),
@ -2836,6 +2826,7 @@ impl RepositorySnapshot {
.iter()
.map(|path| path.as_ref().to_proto())
.collect(),
merge_message: self.merge.message.as_ref().map(|msg| msg.to_string()),
project_id,
id: self.id.to_proto(),
abs_path: self.work_directory_abs_path.to_proto(),
@ -2875,15 +2866,15 @@ impl RepositorySnapshot {
}
pub fn had_conflict_on_last_merge_head_change(&self, repo_path: &RepoPath) -> bool {
self.merge.conflicted_paths.contains(&repo_path)
self.merge.conflicted_paths.contains(repo_path)
}
pub fn has_conflict(&self, repo_path: &RepoPath) -> bool {
let had_conflict_on_last_merge_head_change =
self.merge.conflicted_paths.contains(&repo_path);
self.merge.conflicted_paths.contains(repo_path);
let has_conflict_currently = self
.status_for_path(&repo_path)
.map_or(false, |entry| entry.status.is_conflicted());
.status_for_path(repo_path)
.is_some_and(|entry| entry.status.is_conflicted());
had_conflict_on_last_merge_head_change || has_conflict_currently
}
@ -3424,7 +3415,6 @@ impl Repository {
reset_mode: ResetMode,
_cx: &mut App,
) -> oneshot::Receiver<Result<()>> {
let commit = commit.to_string();
let id = self.id;
self.send_job(None, move |git_repo, _| async move {
@ -3566,14 +3556,13 @@ impl Repository {
let Some(project_path) = self.repo_path_to_project_path(path, cx) else {
continue;
};
if let Some(buffer) = buffer_store.get_by_path(&project_path) {
if buffer
if let Some(buffer) = buffer_store.get_by_path(&project_path)
&& buffer
.read(cx)
.file()
.map_or(false, |file| file.disk_state().exists())
{
save_futures.push(buffer_store.save_buffer(buffer, cx));
}
.is_some_and(|file| file.disk_state().exists())
{
save_futures.push(buffer_store.save_buffer(buffer, cx));
}
}
})
@ -3633,14 +3622,13 @@ impl Repository {
let Some(project_path) = self.repo_path_to_project_path(path, cx) else {
continue;
};
if let Some(buffer) = buffer_store.get_by_path(&project_path) {
if buffer
if let Some(buffer) = buffer_store.get_by_path(&project_path)
&& buffer
.read(cx)
.file()
.map_or(false, |file| file.disk_state().exists())
{
save_futures.push(buffer_store.save_buffer(buffer, cx));
}
.is_some_and(|file| file.disk_state().exists())
{
save_futures.push(buffer_store.save_buffer(buffer, cx));
}
}
})
@ -3687,7 +3675,7 @@ impl Repository {
let to_stage = self
.cached_status()
.filter(|entry| !entry.status.staging().is_fully_staged())
.map(|entry| entry.repo_path.clone())
.map(|entry| entry.repo_path)
.collect();
self.stage_entries(to_stage, cx)
}
@ -3696,16 +3684,13 @@ impl Repository {
let to_unstage = self
.cached_status()
.filter(|entry| entry.status.staging().has_staged())
.map(|entry| entry.repo_path.clone())
.map(|entry| entry.repo_path)
.collect();
self.unstage_entries(to_unstage, cx)
}
pub fn stash_all(&mut self, cx: &mut Context<Self>) -> Task<anyhow::Result<()>> {
let to_stash = self
.cached_status()
.map(|entry| entry.repo_path.clone())
.collect();
let to_stash = self.cached_status().map(|entry| entry.repo_path).collect();
self.stash_entries(to_stash, cx)
}
@ -4301,6 +4286,7 @@ impl Repository {
.map(proto_to_commit_details);
self.snapshot.merge.conflicted_paths = conflicted_paths;
self.snapshot.merge.message = update.merge_message.map(SharedString::from);
let edits = update
.removed_statuses
@ -4453,14 +4439,13 @@ impl Repository {
}
if let Some(job) = jobs.pop_front() {
if let Some(current_key) = &job.key {
if jobs
if let Some(current_key) = &job.key
&& jobs
.iter()
.any(|other_job| other_job.key.as_ref() == Some(current_key))
{
continue;
}
}
(job.job)(state.clone(), cx).await;
} else if let Some(job) = job_rx.next().await {
jobs.push_back(job);
@ -4491,13 +4476,12 @@ impl Repository {
}
if let Some(job) = jobs.pop_front() {
if let Some(current_key) = &job.key {
if jobs
if let Some(current_key) = &job.key
&& jobs
.iter()
.any(|other_job| other_job.key.as_ref() == Some(current_key))
{
continue;
}
{
continue;
}
(job.job)(state.clone(), cx).await;
} else if let Some(job) = job_rx.next().await {
@ -4621,10 +4605,10 @@ impl Repository {
for (repo_path, status) in &*statuses.entries {
changed_paths.remove(repo_path);
if cursor.seek_forward(&PathTarget::Path(repo_path), Bias::Left) {
if cursor.item().is_some_and(|entry| entry.status == *status) {
continue;
}
if cursor.seek_forward(&PathTarget::Path(repo_path), Bias::Left)
&& cursor.item().is_some_and(|entry| entry.status == *status)
{
continue;
}
changed_path_statuses.push(Edit::Insert(StatusEntry {