WIP
This commit is contained in:
parent
4bee06e507
commit
231a348e69
6 changed files with 106 additions and 1 deletions
|
@ -320,6 +320,10 @@ impl GitRepository for FakeGitRepository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stash_entries(&self) -> BoxFuture<'_, Result<git::stash::GitStash>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>> {
|
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>> {
|
||||||
self.with_state_async(false, move |state| {
|
self.with_state_async(false, move |state| {
|
||||||
let current_branch = &state.current_branch_name;
|
let current_branch = &state.current_branch_name;
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub mod commit;
|
||||||
mod hosting_provider;
|
mod hosting_provider;
|
||||||
mod remote;
|
mod remote;
|
||||||
pub mod repository;
|
pub mod repository;
|
||||||
|
pub mod stash;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
pub use crate::hosting_provider::*;
|
pub use crate::hosting_provider::*;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::commit::parse_git_diff_name_status;
|
use crate::commit::parse_git_diff_name_status;
|
||||||
|
use crate::stash::GitStash;
|
||||||
use crate::status::{GitStatus, StatusCode};
|
use crate::status::{GitStatus, StatusCode};
|
||||||
use crate::{Oid, SHORT_SHA_LENGTH};
|
use crate::{Oid, SHORT_SHA_LENGTH};
|
||||||
use anyhow::{Context as _, Result, anyhow, bail};
|
use anyhow::{Context as _, Result, anyhow, bail};
|
||||||
|
@ -338,6 +339,8 @@ pub trait GitRepository: Send + Sync {
|
||||||
|
|
||||||
fn status(&self, path_prefixes: &[RepoPath]) -> Task<Result<GitStatus>>;
|
fn status(&self, path_prefixes: &[RepoPath]) -> Task<Result<GitStatus>>;
|
||||||
|
|
||||||
|
fn stash_entries(&self) -> BoxFuture<'_, Result<GitStash>>;
|
||||||
|
|
||||||
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>>;
|
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>>;
|
||||||
|
|
||||||
fn change_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
|
fn change_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
|
||||||
|
@ -974,6 +977,26 @@ impl GitRepository for RealGitRepository {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stash_entries(&self) -> BoxFuture<'_, Result<GitStash>> {
|
||||||
|
let git_binary_path = self.git_binary_path.clone();
|
||||||
|
let working_directory = self.working_directory();
|
||||||
|
self.executor
|
||||||
|
.spawn(async move {
|
||||||
|
let output = new_std_command(&git_binary_path)
|
||||||
|
.current_dir(working_directory?)
|
||||||
|
.args(&["stash", "list", "--pretty=%gd:%H:%s"])
|
||||||
|
.output()?;
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
stdout.parse()
|
||||||
|
} else {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
anyhow::bail!("git status failed: {stderr}");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>> {
|
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>> {
|
||||||
let working_directory = self.working_directory();
|
let working_directory = self.working_directory();
|
||||||
let git_binary_path = self.git_binary_path.clone();
|
let git_binary_path = self.git_binary_path.clone();
|
||||||
|
|
56
crates/git/src/stash.rs
Normal file
56
crates/git/src/stash.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use crate::Oid;
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct StashEntry {
|
||||||
|
pub index: usize,
|
||||||
|
pub oid: Oid,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
|
pub struct GitStash {
|
||||||
|
pub entries: Arc<[StashEntry]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for GitStash {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
// git stash list --pretty=%gd:%H:%s
|
||||||
|
let entries = s
|
||||||
|
.split('\n')
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let mut parts = entry.splitn(3, ':');
|
||||||
|
let raw_idx = parts.next().and_then(|i| {
|
||||||
|
let trimmed = i.trim();
|
||||||
|
if trimmed.starts_with("stash@{") && trimmed.ends_with('}') {
|
||||||
|
trimmed
|
||||||
|
.strip_prefix("stash@{")
|
||||||
|
.and_then(|s| s.strip_suffix('}'))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let raw_oid = parts.next();
|
||||||
|
let message = parts.next();
|
||||||
|
|
||||||
|
if let (Some(raw_idx), Some(raw_oid), Some(message)) = (raw_idx, raw_oid, message) {
|
||||||
|
let index = raw_idx.parse::<usize>().ok()?;
|
||||||
|
let oid = Oid::from_str(raw_oid).ok()?;
|
||||||
|
let entry = StashEntry {
|
||||||
|
index,
|
||||||
|
oid,
|
||||||
|
message: message.to_string(),
|
||||||
|
};
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect::<Arc<[StashEntry]>>();
|
||||||
|
Ok(Self {
|
||||||
|
entries: entries.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ use git::repository::{
|
||||||
PushOptions, Remote, RemoteCommandOutput, ResetMode, Upstream, UpstreamTracking,
|
PushOptions, Remote, RemoteCommandOutput, ResetMode, Upstream, UpstreamTracking,
|
||||||
UpstreamTrackingStatus, get_git_committer,
|
UpstreamTrackingStatus, get_git_committer,
|
||||||
};
|
};
|
||||||
|
use git::stash::GitStash;
|
||||||
use git::status::StageStatus;
|
use git::status::StageStatus;
|
||||||
use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus};
|
use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus};
|
||||||
use git::{
|
use git::{
|
||||||
|
@ -119,6 +120,7 @@ struct GitMenuState {
|
||||||
has_staged_changes: bool,
|
has_staged_changes: bool,
|
||||||
has_unstaged_changes: bool,
|
has_unstaged_changes: bool,
|
||||||
has_new_changes: bool,
|
has_new_changes: bool,
|
||||||
|
has_stash_items: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn git_panel_context_menu(
|
fn git_panel_context_menu(
|
||||||
|
@ -146,7 +148,7 @@ fn git_panel_context_menu(
|
||||||
"Stash All",
|
"Stash All",
|
||||||
StashAll.boxed_clone(),
|
StashAll.boxed_clone(),
|
||||||
)
|
)
|
||||||
.action("Stash Pop", StashPop.boxed_clone())
|
.action_disabled_when(!state.has_stash_items, "Stash Pop", StashPop.boxed_clone())
|
||||||
.separator()
|
.separator()
|
||||||
.action("Open Diff", project_diff::Diff.boxed_clone())
|
.action("Open Diff", project_diff::Diff.boxed_clone())
|
||||||
.separator()
|
.separator()
|
||||||
|
@ -369,6 +371,7 @@ pub struct GitPanel {
|
||||||
local_committer: Option<GitCommitter>,
|
local_committer: Option<GitCommitter>,
|
||||||
local_committer_task: Option<Task<()>>,
|
local_committer_task: Option<Task<()>>,
|
||||||
bulk_staging: Option<BulkStaging>,
|
bulk_staging: Option<BulkStaging>,
|
||||||
|
stash_entries: GitStash,
|
||||||
_settings_subscription: Subscription,
|
_settings_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,6 +561,7 @@ impl GitPanel {
|
||||||
horizontal_scrollbar,
|
horizontal_scrollbar,
|
||||||
vertical_scrollbar,
|
vertical_scrollbar,
|
||||||
bulk_staging: None,
|
bulk_staging: None,
|
||||||
|
stash_entries: Default::default(),
|
||||||
_settings_subscription,
|
_settings_subscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2685,6 +2689,8 @@ impl GitPanel {
|
||||||
|
|
||||||
let repo = repo.read(cx);
|
let repo = repo.read(cx);
|
||||||
|
|
||||||
|
self.stash_entries = repo.cached_stash();
|
||||||
|
|
||||||
for entry in repo.cached_status() {
|
for entry in repo.cached_status() {
|
||||||
let is_conflict = repo.had_conflict_on_last_merge_head_change(&entry.repo_path);
|
let is_conflict = repo.had_conflict_on_last_merge_head_change(&entry.repo_path);
|
||||||
let is_new = entry.status.is_created();
|
let is_new = entry.status.is_created();
|
||||||
|
@ -3053,6 +3059,7 @@ impl GitPanel {
|
||||||
let has_staged_changes = self.has_staged_changes();
|
let has_staged_changes = self.has_staged_changes();
|
||||||
let has_unstaged_changes = self.has_unstaged_changes();
|
let has_unstaged_changes = self.has_unstaged_changes();
|
||||||
let has_new_changes = self.new_count > 0;
|
let has_new_changes = self.new_count > 0;
|
||||||
|
let has_stash_items = self.stash_entries.entries.len() > 0;
|
||||||
|
|
||||||
PopoverMenu::new(id.into())
|
PopoverMenu::new(id.into())
|
||||||
.trigger(
|
.trigger(
|
||||||
|
@ -3068,6 +3075,7 @@ impl GitPanel {
|
||||||
has_staged_changes,
|
has_staged_changes,
|
||||||
has_unstaged_changes,
|
has_unstaged_changes,
|
||||||
has_new_changes,
|
has_new_changes,
|
||||||
|
has_stash_items,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -4115,6 +4123,7 @@ impl GitPanel {
|
||||||
has_staged_changes: self.has_staged_changes(),
|
has_staged_changes: self.has_staged_changes(),
|
||||||
has_unstaged_changes: self.has_unstaged_changes(),
|
has_unstaged_changes: self.has_unstaged_changes(),
|
||||||
has_new_changes: self.new_count > 0,
|
has_new_changes: self.new_count > 0,
|
||||||
|
has_stash_items: self.stash_entries.entries.len() > 0,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -28,6 +28,7 @@ use git::{
|
||||||
GitRepository, GitRepositoryCheckpoint, PushOptions, Remote, RemoteCommandOutput, RepoPath,
|
GitRepository, GitRepositoryCheckpoint, PushOptions, Remote, RemoteCommandOutput, RepoPath,
|
||||||
ResetMode, UpstreamTrackingStatus,
|
ResetMode, UpstreamTrackingStatus,
|
||||||
},
|
},
|
||||||
|
stash::GitStash,
|
||||||
status::{
|
status::{
|
||||||
FileStatus, GitSummary, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode,
|
FileStatus, GitSummary, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode,
|
||||||
},
|
},
|
||||||
|
@ -248,6 +249,7 @@ pub struct RepositorySnapshot {
|
||||||
pub merge: MergeDetails,
|
pub merge: MergeDetails,
|
||||||
pub remote_origin_url: Option<String>,
|
pub remote_origin_url: Option<String>,
|
||||||
pub remote_upstream_url: Option<String>,
|
pub remote_upstream_url: Option<String>,
|
||||||
|
pub stash_entries: GitStash,
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobId = u64;
|
type JobId = u64;
|
||||||
|
@ -2744,6 +2746,7 @@ impl RepositorySnapshot {
|
||||||
merge: Default::default(),
|
merge: Default::default(),
|
||||||
remote_origin_url: None,
|
remote_origin_url: None,
|
||||||
remote_upstream_url: None,
|
remote_upstream_url: None,
|
||||||
|
stash_entries: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3264,6 +3267,10 @@ impl Repository {
|
||||||
self.snapshot.status()
|
self.snapshot.status()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cached_stash(&self) -> GitStash {
|
||||||
|
self.snapshot.stash_entries.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn repo_path_to_project_path(&self, path: &RepoPath, cx: &App) -> Option<ProjectPath> {
|
pub fn repo_path_to_project_path(&self, path: &RepoPath, cx: &App) -> Option<ProjectPath> {
|
||||||
let git_store = self.git_store.upgrade()?;
|
let git_store = self.git_store.upgrade()?;
|
||||||
let worktree_store = git_store.read(cx).worktree_store.read(cx);
|
let worktree_store = git_store.read(cx).worktree_store.read(cx);
|
||||||
|
@ -4539,6 +4546,7 @@ impl Repository {
|
||||||
updates_tx: Option<mpsc::UnboundedSender<DownstreamUpdate>>,
|
updates_tx: Option<mpsc::UnboundedSender<DownstreamUpdate>>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
|
println!("paths changed with updates_tx: {:?}", updates_tx);
|
||||||
self.paths_needing_status_update.extend(paths);
|
self.paths_needing_status_update.extend(paths);
|
||||||
|
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
|
@ -4561,6 +4569,7 @@ impl Repository {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let statuses = backend.status(&paths).await?;
|
let statuses = backend.status(&paths).await?;
|
||||||
|
let stash_entries = backend.stash_entries().await?;
|
||||||
|
|
||||||
let changed_path_statuses = cx
|
let changed_path_statuses = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
|
@ -4592,6 +4601,7 @@ impl Repository {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.snapshot.stash_entries = stash_entries;
|
||||||
if !changed_path_statuses.is_empty() {
|
if !changed_path_statuses.is_empty() {
|
||||||
this.snapshot
|
this.snapshot
|
||||||
.statuses_by_path
|
.statuses_by_path
|
||||||
|
@ -4852,6 +4862,7 @@ async fn compute_snapshot(
|
||||||
let statuses = backend
|
let statuses = backend
|
||||||
.status(std::slice::from_ref(&WORK_DIRECTORY_REPO_PATH))
|
.status(std::slice::from_ref(&WORK_DIRECTORY_REPO_PATH))
|
||||||
.await?;
|
.await?;
|
||||||
|
let stash_entries = backend.stash_entries().await?;
|
||||||
let statuses_by_path = SumTree::from_iter(
|
let statuses_by_path = SumTree::from_iter(
|
||||||
statuses
|
statuses
|
||||||
.entries
|
.entries
|
||||||
|
@ -4902,6 +4913,7 @@ async fn compute_snapshot(
|
||||||
merge: merge_details,
|
merge: merge_details,
|
||||||
remote_origin_url,
|
remote_origin_url,
|
||||||
remote_upstream_url,
|
remote_upstream_url,
|
||||||
|
stash_entries,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((snapshot, events))
|
Ok((snapshot, events))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue