Add support for git branches on remote projects (#19755)
Release Notes: - Fixed a bug where the branch switcher could not be used remotely.
This commit is contained in:
parent
5506669b06
commit
c69da2df70
25 changed files with 993 additions and 127 deletions
|
@ -1,8 +1,9 @@
|
|||
use crate::GitHostingProviderRegistry;
|
||||
use crate::{blame::Blame, status::GitStatus};
|
||||
use anyhow::{Context, Result};
|
||||
use collections::HashMap;
|
||||
use collections::{HashMap, HashSet};
|
||||
use git2::BranchType;
|
||||
use gpui::SharedString;
|
||||
use parking_lot::Mutex;
|
||||
use rope::Rope;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -17,7 +18,7 @@ use util::ResultExt;
|
|||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
pub struct Branch {
|
||||
pub is_head: bool,
|
||||
pub name: Box<str>,
|
||||
pub name: SharedString,
|
||||
/// Timestamp of most recent commit, normalized to Unix Epoch format.
|
||||
pub unix_timestamp: Option<i64>,
|
||||
}
|
||||
|
@ -41,6 +42,7 @@ pub trait GitRepository: Send + Sync {
|
|||
fn branches(&self) -> Result<Vec<Branch>>;
|
||||
fn change_branch(&self, _: &str) -> Result<()>;
|
||||
fn create_branch(&self, _: &str) -> Result<()>;
|
||||
fn branch_exits(&self, _: &str) -> Result<bool>;
|
||||
|
||||
fn blame(&self, path: &Path, content: Rope) -> Result<crate::blame::Blame>;
|
||||
}
|
||||
|
@ -132,6 +134,18 @@ impl GitRepository for RealGitRepository {
|
|||
GitStatus::new(&self.git_binary_path, &working_directory, path_prefixes)
|
||||
}
|
||||
|
||||
fn branch_exits(&self, name: &str) -> Result<bool> {
|
||||
let repo = self.repository.lock();
|
||||
let branch = repo.find_branch(name, BranchType::Local);
|
||||
match branch {
|
||||
Ok(_) => Ok(true),
|
||||
Err(e) => match e.code() {
|
||||
git2::ErrorCode::NotFound => Ok(false),
|
||||
_ => Err(anyhow::anyhow!(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn branches(&self) -> Result<Vec<Branch>> {
|
||||
let repo = self.repository.lock();
|
||||
let local_branches = repo.branches(Some(BranchType::Local))?;
|
||||
|
@ -139,7 +153,11 @@ impl GitRepository for RealGitRepository {
|
|||
.filter_map(|branch| {
|
||||
branch.ok().and_then(|(branch, _)| {
|
||||
let is_head = branch.is_head();
|
||||
let name = branch.name().ok().flatten().map(Box::from)?;
|
||||
let name = branch
|
||||
.name()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|name| name.to_string().into())?;
|
||||
let timestamp = branch.get().peel_to_commit().ok()?.time();
|
||||
let unix_timestamp = timestamp.seconds();
|
||||
let timezone_offset = timestamp.offset_minutes();
|
||||
|
@ -201,17 +219,20 @@ impl GitRepository for RealGitRepository {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FakeGitRepository {
|
||||
state: Arc<Mutex<FakeGitRepositoryState>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FakeGitRepositoryState {
|
||||
pub path: PathBuf,
|
||||
pub event_emitter: smol::channel::Sender<PathBuf>,
|
||||
pub index_contents: HashMap<PathBuf, String>,
|
||||
pub blames: HashMap<PathBuf, Blame>,
|
||||
pub worktree_statuses: HashMap<RepoPath, GitFileStatus>,
|
||||
pub branch_name: Option<String>,
|
||||
pub current_branch_name: Option<String>,
|
||||
pub branches: HashSet<String>,
|
||||
}
|
||||
|
||||
impl FakeGitRepository {
|
||||
|
@ -220,6 +241,20 @@ impl FakeGitRepository {
|
|||
}
|
||||
}
|
||||
|
||||
impl FakeGitRepositoryState {
|
||||
pub fn new(path: PathBuf, event_emitter: smol::channel::Sender<PathBuf>) -> Self {
|
||||
FakeGitRepositoryState {
|
||||
path,
|
||||
event_emitter,
|
||||
index_contents: Default::default(),
|
||||
blames: Default::default(),
|
||||
worktree_statuses: Default::default(),
|
||||
current_branch_name: Default::default(),
|
||||
branches: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GitRepository for FakeGitRepository {
|
||||
fn reload_index(&self) {}
|
||||
|
||||
|
@ -234,7 +269,7 @@ impl GitRepository for FakeGitRepository {
|
|||
|
||||
fn branch_name(&self) -> Option<String> {
|
||||
let state = self.state.lock();
|
||||
state.branch_name.clone()
|
||||
state.current_branch_name.clone()
|
||||
}
|
||||
|
||||
fn head_sha(&self) -> Option<String> {
|
||||
|
@ -264,18 +299,41 @@ impl GitRepository for FakeGitRepository {
|
|||
}
|
||||
|
||||
fn branches(&self) -> Result<Vec<Branch>> {
|
||||
Ok(vec![])
|
||||
let state = self.state.lock();
|
||||
let current_branch = &state.current_branch_name;
|
||||
Ok(state
|
||||
.branches
|
||||
.iter()
|
||||
.map(|branch_name| Branch {
|
||||
is_head: Some(branch_name) == current_branch.as_ref(),
|
||||
name: branch_name.into(),
|
||||
unix_timestamp: None,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn branch_exits(&self, name: &str) -> Result<bool> {
|
||||
let state = self.state.lock();
|
||||
Ok(state.branches.contains(name))
|
||||
}
|
||||
|
||||
fn change_branch(&self, name: &str) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
state.branch_name = Some(name.to_owned());
|
||||
state.current_branch_name = Some(name.to_owned());
|
||||
state
|
||||
.event_emitter
|
||||
.try_send(state.path.clone())
|
||||
.expect("Dropped repo change event");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_branch(&self, name: &str) -> Result<()> {
|
||||
let mut state = self.state.lock();
|
||||
state.branch_name = Some(name.to_owned());
|
||||
state.branches.insert(name.to_owned());
|
||||
state
|
||||
.event_emitter
|
||||
.try_send(state.path.clone())
|
||||
.expect("Dropped repo change event");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue