From 7169f5c7609a93ad282aa0d4b5f9969ea6f447cc Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 9 May 2023 08:36:43 -0700 Subject: [PATCH] Add git status to the file system abstraction co-authored-by: petros --- Cargo.lock | 1 + crates/fs/Cargo.toml | 1 + crates/fs/src/repository.rs | 82 +++++++++++++++++++++++++++++++++- crates/project/src/worktree.rs | 37 +++++---------- 4 files changed, 94 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bef04fce14..bd1dd4f33b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2350,6 +2350,7 @@ dependencies = [ "serde_derive", "serde_json", "smol", + "sum_tree", "tempfile", "util", ] diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index d080fe3cd1..54c6ce362a 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -13,6 +13,7 @@ gpui = { path = "../gpui" } lsp = { path = "../lsp" } rope = { path = "../rope" } util = { path = "../util" } +sum_tree = { path = "../sum_tree" } anyhow.workspace = true async-trait.workspace = true futures.workspace = true diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index 5624ce42f1..14e7e75a3d 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -1,9 +1,10 @@ use anyhow::Result; use collections::HashMap; use parking_lot::Mutex; +use sum_tree::TreeMap; use std::{ path::{Component, Path, PathBuf}, - sync::Arc, + sync::Arc, ffi::OsStr, os::unix::prelude::OsStrExt, }; use util::ResultExt; @@ -16,6 +17,8 @@ pub trait GitRepository: Send { fn load_index_text(&self, relative_file_path: &Path) -> Option; fn branch_name(&self) -> Option; + + fn statuses(&self) -> Option>; } impl std::fmt::Debug for dyn GitRepository { @@ -61,6 +64,79 @@ impl GitRepository for LibGitRepository { let branch = String::from_utf8_lossy(head.shorthand_bytes()); Some(branch.to_string()) } + + fn statuses(&self) -> Option> { + let statuses = self.statuses(None).log_err()?; + + let mut map = TreeMap::default(); + + for status in statuses.iter() { + let path = RepoPath(PathBuf::from(OsStr::from_bytes(status.path_bytes()))); + + let status_data = status.status(); + + let status = if status_data.contains(git2::Status::CONFLICTED) { + GitStatus::Conflict + } else if status_data.intersects(git2::Status::INDEX_MODIFIED + | git2::Status::WT_MODIFIED + | git2::Status::INDEX_RENAMED + | git2::Status::WT_RENAMED) { + GitStatus::Modified + } else if status_data.intersects(git2::Status::INDEX_NEW | git2::Status::WT_NEW) { + GitStatus::Added + } else { + GitStatus::Untracked + }; + + map.insert(path, status) + } + + Some(map) + } +} + +#[derive(Debug, Clone, Default)] +pub enum GitStatus { + Added, + Modified, + Conflict, + #[default] + Untracked, +} + +#[derive(Clone, Debug, Ord, Hash, PartialOrd, Eq, PartialEq)] +pub struct RepoPath(PathBuf); + +impl From<&Path> for RepoPath { + fn from(value: &Path) -> Self { + RepoPath(value.to_path_buf()) + } +} + +impl From for RepoPath { + fn from(value: PathBuf) -> Self { + RepoPath(value) + } +} + +impl Default for RepoPath { + fn default() -> Self { + RepoPath(PathBuf::new()) + } +} + +impl AsRef for RepoPath { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } +} + +impl std::ops::Deref for RepoPath { + type Target = PathBuf; + + fn deref(&self) -> &Self::Target { + &self.0 + } } #[derive(Debug, Clone, Default)] @@ -93,6 +169,10 @@ impl GitRepository for FakeGitRepository { let state = self.state.lock(); state.branch_name.clone() } + + fn statuses(&self) -> Option>{ + todo!() + } } fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 554304f3d3..e236d18efd 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Context, Result}; use client::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, VecDeque}; -use fs::{repository::GitRepository, Fs, LineEnding}; +use fs::{repository::{GitRepository, RepoPath, GitStatus}, Fs, LineEnding}; use futures::{ channel::{ mpsc::{self, UnboundedSender}, @@ -117,10 +117,11 @@ pub struct Snapshot { completed_scan_id: usize, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct RepositoryEntry { pub(crate) work_directory: WorkDirectoryEntry, pub(crate) branch: Option>, + // pub(crate) statuses: TreeMap } impl RepositoryEntry { @@ -162,6 +163,13 @@ impl Default for RepositoryWorkDirectory { } } +impl AsRef for RepositoryWorkDirectory { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } +} + + #[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct WorkDirectoryEntry(ProjectEntryId); @@ -178,7 +186,7 @@ impl WorkDirectoryEntry { worktree.entry_for_id(self.0).and_then(|entry| { path.strip_prefix(&entry.path) .ok() - .map(move |path| RepoPath(path.to_owned())) + .map(move |path| path.into()) }) } } @@ -197,29 +205,6 @@ impl<'a> From for WorkDirectoryEntry { } } -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] -pub struct RepoPath(PathBuf); - -impl AsRef for RepoPath { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } -} - -impl Deref for RepoPath { - type Target = PathBuf; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsRef for RepositoryWorkDirectory { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } -} - #[derive(Debug, Clone)] pub struct LocalSnapshot { ignores_by_parent_abs_path: HashMap, (Arc, usize)>,