git: Git Panel UI, continued (#22960)
TODO: - [ ] Investigate incorrect hit target for `stage all` button - [ ] Add top level context menu - [ ] Add entry context menus - [x] Show paths in list view - [ ] For now, `enter` can just open the file - [ ] 🐞: Hover deadzone in list caused by scrollbar - [x] 🐞: Incorrect status/nothing shown when multiple worktrees are added --- This PR continues work on the feature flagged git panel. Changes: - Defines and wires up git panel actions & keybindings - Re-scopes some actions from `git_ui` -> `git`. - General git actions (StageAll, CommitChanges, ...) are scoped to `git`. - Git panel specific actions (Close, FocusCommitEditor, ...) are scoped to `git_panel. - Staging actions & UI are now connected to git! - Unify more reusable git status into the GitState global over being tied to the panel directly. - Uses the new git status codepaths instead of filtering all workspace entries Release Notes: - N/A --------- Co-authored-by: Cole Miller <53574922+cole-miller@users.noreply.github.com> Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
parent
1c6dd03e50
commit
102e70816c
13 changed files with 1006 additions and 840 deletions
|
@ -1,6 +1,7 @@
|
|||
use crate::status::GitStatusPair;
|
||||
use crate::GitHostingProviderRegistry;
|
||||
use crate::{blame::Blame, status::GitStatus};
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
use git2::BranchType;
|
||||
use gpui::SharedString;
|
||||
|
@ -15,6 +16,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
use sum_tree::MapSeekTarget;
|
||||
use util::command::new_std_command;
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
|
@ -51,6 +53,8 @@ pub trait GitRepository: Send + Sync {
|
|||
|
||||
/// Returns the path to the repository, typically the `.git` folder.
|
||||
fn dot_git_dir(&self) -> PathBuf;
|
||||
|
||||
fn update_index(&self, stage: &[RepoPath], unstage: &[RepoPath]) -> Result<()>;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn GitRepository {
|
||||
|
@ -152,7 +156,7 @@ impl GitRepository for RealGitRepository {
|
|||
Ok(_) => Ok(true),
|
||||
Err(e) => match e.code() {
|
||||
git2::ErrorCode::NotFound => Ok(false),
|
||||
_ => Err(anyhow::anyhow!(e)),
|
||||
_ => Err(anyhow!(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +200,7 @@ impl GitRepository for RealGitRepository {
|
|||
repo.set_head(
|
||||
revision
|
||||
.name()
|
||||
.ok_or_else(|| anyhow::anyhow!("Branch name could not be retrieved"))?,
|
||||
.ok_or_else(|| anyhow!("Branch name could not be retrieved"))?,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -228,6 +232,36 @@ impl GitRepository for RealGitRepository {
|
|||
self.hosting_provider_registry.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn update_index(&self, stage: &[RepoPath], unstage: &[RepoPath]) -> Result<()> {
|
||||
let working_directory = self
|
||||
.repository
|
||||
.lock()
|
||||
.workdir()
|
||||
.context("failed to read git work directory")?
|
||||
.to_path_buf();
|
||||
if !stage.is_empty() {
|
||||
let add = new_std_command(&self.git_binary_path)
|
||||
.current_dir(&working_directory)
|
||||
.args(["add", "--"])
|
||||
.args(stage.iter().map(|p| p.as_ref()))
|
||||
.status()?;
|
||||
if !add.success() {
|
||||
return Err(anyhow!("Failed to stage files: {add}"));
|
||||
}
|
||||
}
|
||||
if !unstage.is_empty() {
|
||||
let rm = new_std_command(&self.git_binary_path)
|
||||
.current_dir(&working_directory)
|
||||
.args(["restore", "--staged", "--"])
|
||||
.args(unstage.iter().map(|p| p.as_ref()))
|
||||
.status()?;
|
||||
if !rm.success() {
|
||||
return Err(anyhow!("Failed to unstage files: {rm}"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -298,18 +332,24 @@ impl GitRepository for FakeGitRepository {
|
|||
let mut entries = state
|
||||
.worktree_statuses
|
||||
.iter()
|
||||
.filter_map(|(repo_path, status)| {
|
||||
.filter_map(|(repo_path, status_worktree)| {
|
||||
if path_prefixes
|
||||
.iter()
|
||||
.any(|path_prefix| repo_path.0.starts_with(path_prefix))
|
||||
{
|
||||
Some((repo_path.to_owned(), *status))
|
||||
Some((
|
||||
repo_path.to_owned(),
|
||||
GitStatusPair {
|
||||
index_status: None,
|
||||
worktree_status: Some(*status_worktree),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_unstable_by(|a, b| a.0.cmp(&b.0));
|
||||
entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(&b));
|
||||
|
||||
Ok(GitStatus {
|
||||
entries: entries.into(),
|
||||
|
@ -363,6 +403,10 @@ impl GitRepository for FakeGitRepository {
|
|||
.with_context(|| format!("failed to get blame for {:?}", path))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn update_index(&self, _stage: &[RepoPath], _unstage: &[RepoPath]) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
|
||||
|
@ -398,6 +442,7 @@ fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
|
|||
pub enum GitFileStatus {
|
||||
Added,
|
||||
Modified,
|
||||
// TODO conflicts should be represented by the GitStatusPair
|
||||
Conflict,
|
||||
Deleted,
|
||||
Untracked,
|
||||
|
@ -426,6 +471,16 @@ impl GitFileStatus {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
match byte {
|
||||
b'M' => Some(GitFileStatus::Modified),
|
||||
b'A' => Some(GitFileStatus::Added),
|
||||
b'D' => Some(GitFileStatus::Deleted),
|
||||
b'?' => Some(GitFileStatus::Untracked),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static WORK_DIRECTORY_REPO_PATH: LazyLock<RepoPath> =
|
||||
|
@ -453,6 +508,12 @@ impl RepoPath {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RepoPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.to_string_lossy().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Path> for RepoPath {
|
||||
fn from(value: &Path) -> Self {
|
||||
RepoPath::new(value.into())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue