parent
050f5f6723
commit
036c123488
13 changed files with 429 additions and 83 deletions
|
@ -555,6 +555,12 @@
|
||||||
//
|
//
|
||||||
// Default: icon
|
// Default: icon
|
||||||
"status_style": "icon",
|
"status_style": "icon",
|
||||||
|
// What branch name to use if init.defaultBranch
|
||||||
|
// is not set
|
||||||
|
//
|
||||||
|
// Default: main
|
||||||
|
"fallback_branch_name": "main",
|
||||||
|
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the git panel.
|
// When to show the scrollbar in the git panel.
|
||||||
//
|
//
|
||||||
|
|
|
@ -396,6 +396,7 @@ impl Server {
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
|
.add_request_handler(forward_mutating_project_request::<proto::Stage>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
.add_request_handler(forward_mutating_project_request::<proto::Unstage>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
.add_request_handler(forward_mutating_project_request::<proto::Commit>)
|
||||||
|
.add_request_handler(forward_mutating_project_request::<proto::GitInit>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GetRemotes>)
|
.add_request_handler(forward_read_only_project_request::<proto::GetRemotes>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
.add_request_handler(forward_read_only_project_request::<proto::GitShow>)
|
||||||
.add_request_handler(forward_read_only_project_request::<proto::GitReset>)
|
.add_request_handler(forward_read_only_project_request::<proto::GitReset>)
|
||||||
|
|
|
@ -16,12 +16,14 @@ use git::{repository::RepoPath, status::FileStatus};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||||
use ashpd::desktop::trash;
|
use ashpd::desktop::trash;
|
||||||
|
use std::borrow::Cow;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::fd::AsFd;
|
use std::os::fd::AsFd;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
|
use util::command::new_std_command;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
@ -136,6 +138,7 @@ pub trait Fs: Send + Sync {
|
||||||
|
|
||||||
fn home_dir(&self) -> Option<PathBuf>;
|
fn home_dir(&self) -> Option<PathBuf>;
|
||||||
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<dyn GitRepository>>;
|
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<dyn GitRepository>>;
|
||||||
|
fn git_init(&self, abs_work_directory: &Path, fallback_branch_name: String) -> Result<()>;
|
||||||
fn is_fake(&self) -> bool;
|
fn is_fake(&self) -> bool;
|
||||||
async fn is_case_sensitive(&self) -> Result<bool>;
|
async fn is_case_sensitive(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
@ -765,6 +768,29 @@ impl Fs for RealFs {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn git_init(&self, abs_work_directory_path: &Path, fallback_branch_name: String) -> Result<()> {
|
||||||
|
let config = new_std_command("git")
|
||||||
|
.current_dir(abs_work_directory_path)
|
||||||
|
.args(&["config", "--global", "--get", "init.defaultBranch"])
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
let branch_name;
|
||||||
|
|
||||||
|
if config.status.success() && !config.stdout.is_empty() {
|
||||||
|
branch_name = String::from_utf8_lossy(&config.stdout);
|
||||||
|
} else {
|
||||||
|
branch_name = Cow::Borrowed(fallback_branch_name.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
new_std_command("git")
|
||||||
|
.current_dir(abs_work_directory_path)
|
||||||
|
.args(&["init", "-b"])
|
||||||
|
.arg(branch_name.trim())
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_fake(&self) -> bool {
|
fn is_fake(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -2075,6 +2101,14 @@ impl Fs for FakeFs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn git_init(
|
||||||
|
&self,
|
||||||
|
abs_work_directory_path: &Path,
|
||||||
|
_fallback_branch_name: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
smol::block_on(self.create_dir(&abs_work_directory_path.join(".git")))
|
||||||
|
}
|
||||||
|
|
||||||
fn is_fake(&self) -> bool {
|
fn is_fake(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,8 @@ actions!(
|
||||||
Fetch,
|
Fetch,
|
||||||
Commit,
|
Commit,
|
||||||
ExpandCommitEditor,
|
ExpandCommitEditor,
|
||||||
GenerateCommitMessage
|
GenerateCommitMessage,
|
||||||
|
Init,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);
|
action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);
|
||||||
|
|
|
@ -1767,6 +1767,88 @@ impl GitPanel {
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn git_init(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
let worktrees = self
|
||||||
|
.project
|
||||||
|
.read(cx)
|
||||||
|
.visible_worktrees(cx)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let worktree = if worktrees.len() == 1 {
|
||||||
|
Task::ready(Some(worktrees.first().unwrap().clone()))
|
||||||
|
} else if worktrees.len() == 0 {
|
||||||
|
let result = window.prompt(
|
||||||
|
PromptLevel::Warning,
|
||||||
|
"Unable to initialize a git repository",
|
||||||
|
Some("Open a directory first"),
|
||||||
|
&["Ok"],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn(async move {
|
||||||
|
result.await.ok();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
let worktree_directories = worktrees
|
||||||
|
.iter()
|
||||||
|
.map(|worktree| worktree.read(cx).abs_path())
|
||||||
|
.map(|worktree_abs_path| {
|
||||||
|
if let Ok(path) = worktree_abs_path.strip_prefix(util::paths::home_dir()) {
|
||||||
|
Path::new("~")
|
||||||
|
.join(path)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
worktree_abs_path.to_string_lossy().to_string().into()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
let prompt = picker_prompt::prompt(
|
||||||
|
"Where would you like to initialize this git repository?",
|
||||||
|
worktree_directories,
|
||||||
|
self.workspace.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.spawn(|_, _| async move { prompt.await.map(|ix| worktrees[ix].clone()) })
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.spawn_in(window, |this, mut cx| async move {
|
||||||
|
let worktree = match worktree.await {
|
||||||
|
Some(worktree) => worktree,
|
||||||
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(result) = this.update(&mut cx, |this, cx| {
|
||||||
|
let fallback_branch_name = GitPanelSettings::get_global(cx)
|
||||||
|
.fallback_branch_name
|
||||||
|
.clone();
|
||||||
|
this.project.read(cx).git_init(
|
||||||
|
worktree.read(cx).abs_path(),
|
||||||
|
fallback_branch_name,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = result.await;
|
||||||
|
|
||||||
|
this.update_in(&mut cx, |this, _, cx| match result {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => this.show_error_toast("init", e, cx),
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn pull(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
pub(crate) fn pull(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
if !self.can_push_and_pull(cx) {
|
if !self.can_push_and_pull(cx) {
|
||||||
return;
|
return;
|
||||||
|
@ -1971,7 +2053,7 @@ impl GitPanel {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})?
|
})?
|
||||||
.await?;
|
.await;
|
||||||
|
|
||||||
Ok(selection.map(|selection| Remote {
|
Ok(selection.map(|selection| Remote {
|
||||||
name: current_remotes[selection].clone(),
|
name: current_remotes[selection].clone(),
|
||||||
|
@ -2677,7 +2759,13 @@ impl GitPanel {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_panel_header(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render_panel_header(
|
||||||
|
&self,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Option<impl IntoElement> {
|
||||||
|
self.active_repository.as_ref()?;
|
||||||
|
|
||||||
let text;
|
let text;
|
||||||
let action;
|
let action;
|
||||||
let tooltip;
|
let tooltip;
|
||||||
|
@ -2697,6 +2785,7 @@ impl GitPanel {
|
||||||
_ => format!("{} Changes", self.entry_count),
|
_ => format!("{} Changes", self.entry_count),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Some(
|
||||||
self.panel_header_container(window, cx)
|
self.panel_header_container(window, cx)
|
||||||
.px_2()
|
.px_2()
|
||||||
.child(
|
.child(
|
||||||
|
@ -2715,6 +2804,7 @@ impl GitPanel {
|
||||||
)
|
)
|
||||||
.child(div().flex_grow()) // spacer
|
.child(div().flex_grow()) // spacer
|
||||||
.child(self.render_overflow_menu("overflow_menu"))
|
.child(self.render_overflow_menu("overflow_menu"))
|
||||||
|
.child(div().w_2()) // another spacer
|
||||||
.child(
|
.child(
|
||||||
panel_filled_button(text)
|
panel_filled_button(text)
|
||||||
.tooltip(Tooltip::for_action_title_in(
|
.tooltip(Tooltip::for_action_title_in(
|
||||||
|
@ -2729,6 +2819,7 @@ impl GitPanel {
|
||||||
cx.dispatch_action(action.as_ref());
|
cx.dispatch_action(action.as_ref());
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2949,12 +3040,29 @@ impl GitPanel {
|
||||||
.items_center()
|
.items_center()
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_3()
|
.gap_2()
|
||||||
.child(if self.active_repository.is_some() {
|
.child(h_flex().w_full().justify_around().child(
|
||||||
|
if self.active_repository.is_some() {
|
||||||
"No changes to commit"
|
"No changes to commit"
|
||||||
} else {
|
} else {
|
||||||
"No Git repositories"
|
"No Git repositories"
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.children(self.active_repository.is_none().then(|| {
|
||||||
|
h_flex().w_full().justify_around().child(
|
||||||
|
panel_filled_button("Initialize Repository")
|
||||||
|
.tooltip(Tooltip::for_action_title_in(
|
||||||
|
"git init",
|
||||||
|
&git::Init,
|
||||||
|
&self.focus_handle,
|
||||||
|
))
|
||||||
|
.on_click(move |_, _, cx| {
|
||||||
|
cx.defer(move |cx| {
|
||||||
|
cx.dispatch_action(&git::Init);
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}))
|
||||||
.text_ui_sm(cx)
|
.text_ui_sm(cx)
|
||||||
.mx_auto()
|
.mx_auto()
|
||||||
.text_color(Color::Placeholder.color(cx)),
|
.text_color(Color::Placeholder.color(cx)),
|
||||||
|
@ -3674,7 +3782,7 @@ impl Render for GitPanel {
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.child(self.render_panel_header(window, cx))
|
.children(self.render_panel_header(window, cx))
|
||||||
.map(|this| {
|
.map(|this| {
|
||||||
if has_entries {
|
if has_entries {
|
||||||
this.child(self.render_entries(has_write_access, window, cx))
|
this.child(self.render_entries(has_write_access, window, cx))
|
||||||
|
|
|
@ -58,15 +58,22 @@ pub struct GitPanelSettingsContent {
|
||||||
///
|
///
|
||||||
/// Default: inherits editor scrollbar settings
|
/// Default: inherits editor scrollbar settings
|
||||||
pub scrollbar: Option<ScrollbarSettings>,
|
pub scrollbar: Option<ScrollbarSettings>,
|
||||||
|
|
||||||
|
/// What the default branch name should be when
|
||||||
|
/// `init.defaultBranch` is not set in git
|
||||||
|
///
|
||||||
|
/// Default: main
|
||||||
|
pub fallback_branch_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub struct GitPanelSettings {
|
pub struct GitPanelSettings {
|
||||||
pub button: bool,
|
pub button: bool,
|
||||||
pub dock: DockPosition,
|
pub dock: DockPosition,
|
||||||
pub default_width: Pixels,
|
pub default_width: Pixels,
|
||||||
pub status_style: StatusStyle,
|
pub status_style: StatusStyle,
|
||||||
pub scrollbar: ScrollbarSettings,
|
pub scrollbar: ScrollbarSettings,
|
||||||
|
pub fallback_branch_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings for GitPanelSettings {
|
impl Settings for GitPanelSettings {
|
||||||
|
|
|
@ -82,6 +82,14 @@ pub fn init(cx: &mut App) {
|
||||||
panel.unstage_all(action, window, cx);
|
panel.unstage_all(action, window, cx);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
workspace.register_action(|workspace, _action: &git::Init, window, cx| {
|
||||||
|
let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
panel.update(cx, |panel, cx| {
|
||||||
|
panel.git_init(window, cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
|
|
||||||
|
@ -26,9 +25,9 @@ pub fn prompt(
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<Result<Option<usize>>> {
|
) -> Task<Option<usize>> {
|
||||||
if options.is_empty() {
|
if options.is_empty() {
|
||||||
return Task::ready(Err(anyhow!("No options")));
|
return Task::ready(None);
|
||||||
}
|
}
|
||||||
let prompt = prompt.to_string().into();
|
let prompt = prompt.to_string().into();
|
||||||
|
|
||||||
|
@ -37,15 +36,17 @@ pub fn prompt(
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
let delegate = PickerPromptDelegate::new(prompt, options, tx, 70);
|
let delegate = PickerPromptDelegate::new(prompt, options, tx, 70);
|
||||||
|
|
||||||
workspace.update_in(&mut cx, |workspace, window, cx| {
|
workspace
|
||||||
|
.update_in(&mut cx, |workspace, window, cx| {
|
||||||
workspace.toggle_modal(window, cx, |window, cx| {
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
PickerPrompt::new(delegate, 34., window, cx)
|
PickerPrompt::new(delegate, 34., window, cx)
|
||||||
})
|
})
|
||||||
})?;
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
match rx.await {
|
match rx.await {
|
||||||
Ok(selection) => Some(selection).transpose(),
|
Ok(selection) => Some(selection),
|
||||||
Err(_) => anyhow::Ok(None), // User cancelled
|
Err(_) => None, // User cancelled
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -94,14 +95,14 @@ pub struct PickerPromptDelegate {
|
||||||
all_options: Vec<SharedString>,
|
all_options: Vec<SharedString>,
|
||||||
selected_index: usize,
|
selected_index: usize,
|
||||||
max_match_length: usize,
|
max_match_length: usize,
|
||||||
tx: Option<oneshot::Sender<Result<usize>>>,
|
tx: Option<oneshot::Sender<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PickerPromptDelegate {
|
impl PickerPromptDelegate {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
prompt: Arc<str>,
|
prompt: Arc<str>,
|
||||||
options: Vec<SharedString>,
|
options: Vec<SharedString>,
|
||||||
tx: oneshot::Sender<Result<usize>>,
|
tx: oneshot::Sender<usize>,
|
||||||
max_chars: usize,
|
max_chars: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -200,7 +201,7 @@ impl PickerDelegate for PickerPromptDelegate {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.tx.take().map(|tx| tx.send(Ok(option.candidate_id)));
|
self.tx.take().map(|tx| tx.send(option.candidate_id));
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ use askpass::{AskPassDelegate, AskPassSession};
|
||||||
use buffer_diff::BufferDiffEvent;
|
use buffer_diff::BufferDiffEvent;
|
||||||
use client::ProjectId;
|
use client::ProjectId;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
use fs::Fs;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
future::OptionFuture,
|
future::OptionFuture,
|
||||||
|
@ -38,21 +39,37 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use text::BufferId;
|
use text::BufferId;
|
||||||
use util::{debug_panic, maybe, ResultExt};
|
use util::{debug_panic, maybe, ResultExt};
|
||||||
use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry, WorkDirectory};
|
use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry, WorkDirectory};
|
||||||
|
|
||||||
pub struct GitStore {
|
pub struct GitStore {
|
||||||
|
state: GitStoreState,
|
||||||
buffer_store: Entity<BufferStore>,
|
buffer_store: Entity<BufferStore>,
|
||||||
environment: Option<Entity<ProjectEnvironment>>,
|
|
||||||
pub(super) project_id: Option<ProjectId>,
|
|
||||||
pub(super) client: AnyProtoClient,
|
|
||||||
repositories: Vec<Entity<Repository>>,
|
repositories: Vec<Entity<Repository>>,
|
||||||
active_index: Option<usize>,
|
active_index: Option<usize>,
|
||||||
update_sender: mpsc::UnboundedSender<GitJob>,
|
update_sender: mpsc::UnboundedSender<GitJob>,
|
||||||
_subscriptions: [Subscription; 2],
|
_subscriptions: [Subscription; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum GitStoreState {
|
||||||
|
Local {
|
||||||
|
client: AnyProtoClient,
|
||||||
|
environment: Entity<ProjectEnvironment>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
},
|
||||||
|
Ssh {
|
||||||
|
environment: Entity<ProjectEnvironment>,
|
||||||
|
upstream_client: AnyProtoClient,
|
||||||
|
project_id: ProjectId,
|
||||||
|
},
|
||||||
|
Remote {
|
||||||
|
upstream_client: AnyProtoClient,
|
||||||
|
project_id: ProjectId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Repository {
|
pub struct Repository {
|
||||||
commit_message_buffer: Option<Entity<Buffer>>,
|
commit_message_buffer: Option<Entity<Buffer>>,
|
||||||
git_store: WeakEntity<GitStore>,
|
git_store: WeakEntity<GitStore>,
|
||||||
|
@ -101,12 +118,12 @@ enum GitJobKey {
|
||||||
impl EventEmitter<GitEvent> for GitStore {}
|
impl EventEmitter<GitEvent> for GitStore {}
|
||||||
|
|
||||||
impl GitStore {
|
impl GitStore {
|
||||||
pub fn new(
|
pub fn local(
|
||||||
worktree_store: &Entity<WorktreeStore>,
|
worktree_store: &Entity<WorktreeStore>,
|
||||||
buffer_store: Entity<BufferStore>,
|
buffer_store: Entity<BufferStore>,
|
||||||
environment: Option<Entity<ProjectEnvironment>>,
|
environment: Entity<ProjectEnvironment>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
client: AnyProtoClient,
|
client: AnyProtoClient,
|
||||||
project_id: Option<ProjectId>,
|
|
||||||
cx: &mut Context<'_, Self>,
|
cx: &mut Context<'_, Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let update_sender = Self::spawn_git_worker(cx);
|
let update_sender = Self::spawn_git_worker(cx);
|
||||||
|
@ -115,11 +132,73 @@ impl GitStore {
|
||||||
cx.subscribe(&buffer_store, Self::on_buffer_store_event),
|
cx.subscribe(&buffer_store, Self::on_buffer_store_event),
|
||||||
];
|
];
|
||||||
|
|
||||||
GitStore {
|
let state = GitStoreState::Local {
|
||||||
project_id,
|
|
||||||
client,
|
client,
|
||||||
buffer_store,
|
|
||||||
environment,
|
environment,
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
GitStore {
|
||||||
|
state,
|
||||||
|
buffer_store,
|
||||||
|
repositories: Vec::new(),
|
||||||
|
active_index: None,
|
||||||
|
update_sender,
|
||||||
|
_subscriptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remote(
|
||||||
|
worktree_store: &Entity<WorktreeStore>,
|
||||||
|
buffer_store: Entity<BufferStore>,
|
||||||
|
upstream_client: AnyProtoClient,
|
||||||
|
project_id: ProjectId,
|
||||||
|
cx: &mut Context<'_, Self>,
|
||||||
|
) -> Self {
|
||||||
|
let update_sender = Self::spawn_git_worker(cx);
|
||||||
|
let _subscriptions = [
|
||||||
|
cx.subscribe(worktree_store, Self::on_worktree_store_event),
|
||||||
|
cx.subscribe(&buffer_store, Self::on_buffer_store_event),
|
||||||
|
];
|
||||||
|
|
||||||
|
let state = GitStoreState::Remote {
|
||||||
|
upstream_client,
|
||||||
|
project_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
GitStore {
|
||||||
|
state,
|
||||||
|
buffer_store,
|
||||||
|
repositories: Vec::new(),
|
||||||
|
active_index: None,
|
||||||
|
update_sender,
|
||||||
|
_subscriptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ssh(
|
||||||
|
worktree_store: &Entity<WorktreeStore>,
|
||||||
|
buffer_store: Entity<BufferStore>,
|
||||||
|
environment: Entity<ProjectEnvironment>,
|
||||||
|
upstream_client: AnyProtoClient,
|
||||||
|
project_id: ProjectId,
|
||||||
|
cx: &mut Context<'_, Self>,
|
||||||
|
) -> Self {
|
||||||
|
let update_sender = Self::spawn_git_worker(cx);
|
||||||
|
let _subscriptions = [
|
||||||
|
cx.subscribe(worktree_store, Self::on_worktree_store_event),
|
||||||
|
cx.subscribe(&buffer_store, Self::on_buffer_store_event),
|
||||||
|
];
|
||||||
|
|
||||||
|
let state = GitStoreState::Ssh {
|
||||||
|
upstream_client,
|
||||||
|
project_id,
|
||||||
|
environment,
|
||||||
|
};
|
||||||
|
|
||||||
|
GitStore {
|
||||||
|
state,
|
||||||
|
buffer_store,
|
||||||
repositories: Vec::new(),
|
repositories: Vec::new(),
|
||||||
active_index: None,
|
active_index: None,
|
||||||
update_sender,
|
update_sender,
|
||||||
|
@ -132,6 +211,7 @@ impl GitStore {
|
||||||
client.add_entity_request_handler(Self::handle_get_branches);
|
client.add_entity_request_handler(Self::handle_get_branches);
|
||||||
client.add_entity_request_handler(Self::handle_change_branch);
|
client.add_entity_request_handler(Self::handle_change_branch);
|
||||||
client.add_entity_request_handler(Self::handle_create_branch);
|
client.add_entity_request_handler(Self::handle_create_branch);
|
||||||
|
client.add_entity_request_handler(Self::handle_git_init);
|
||||||
client.add_entity_request_handler(Self::handle_push);
|
client.add_entity_request_handler(Self::handle_push);
|
||||||
client.add_entity_request_handler(Self::handle_pull);
|
client.add_entity_request_handler(Self::handle_pull);
|
||||||
client.add_entity_request_handler(Self::handle_fetch);
|
client.add_entity_request_handler(Self::handle_fetch);
|
||||||
|
@ -153,6 +233,34 @@ impl GitStore {
|
||||||
.map(|index| self.repositories[index].clone())
|
.map(|index| self.repositories[index].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client(&self) -> AnyProtoClient {
|
||||||
|
match &self.state {
|
||||||
|
GitStoreState::Local { client, .. } => client.clone(),
|
||||||
|
GitStoreState::Ssh {
|
||||||
|
upstream_client, ..
|
||||||
|
} => upstream_client.clone(),
|
||||||
|
GitStoreState::Remote {
|
||||||
|
upstream_client, ..
|
||||||
|
} => upstream_client.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project_environment(&self) -> Option<Entity<ProjectEnvironment>> {
|
||||||
|
match &self.state {
|
||||||
|
GitStoreState::Local { environment, .. } => Some(environment.clone()),
|
||||||
|
GitStoreState::Ssh { environment, .. } => Some(environment.clone()),
|
||||||
|
GitStoreState::Remote { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project_id(&self) -> Option<ProjectId> {
|
||||||
|
match &self.state {
|
||||||
|
GitStoreState::Local { .. } => None,
|
||||||
|
GitStoreState::Ssh { project_id, .. } => Some(*project_id),
|
||||||
|
GitStoreState::Remote { project_id, .. } => Some(*project_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_worktree_store_event(
|
fn on_worktree_store_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
worktree_store: Entity<WorktreeStore>,
|
worktree_store: Entity<WorktreeStore>,
|
||||||
|
@ -162,8 +270,8 @@ impl GitStore {
|
||||||
let mut new_repositories = Vec::new();
|
let mut new_repositories = Vec::new();
|
||||||
let mut new_active_index = None;
|
let mut new_active_index = None;
|
||||||
let this = cx.weak_entity();
|
let this = cx.weak_entity();
|
||||||
let client = self.client.clone();
|
let client = self.client();
|
||||||
let project_id = self.project_id;
|
let project_id = self.project_id();
|
||||||
|
|
||||||
worktree_store.update(cx, |worktree_store, cx| {
|
worktree_store.update(cx, |worktree_store, cx| {
|
||||||
for worktree in worktree_store.worktrees() {
|
for worktree in worktree_store.worktrees() {
|
||||||
|
@ -229,9 +337,9 @@ impl GitStore {
|
||||||
});
|
});
|
||||||
existing_handle
|
existing_handle
|
||||||
} else {
|
} else {
|
||||||
|
let environment = self.project_environment();
|
||||||
cx.new(|_| Repository {
|
cx.new(|_| Repository {
|
||||||
project_environment: self
|
project_environment: environment
|
||||||
.environment
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|env| env.downgrade()),
|
.map(|env| env.downgrade()),
|
||||||
git_store: this.clone(),
|
git_store: this.clone(),
|
||||||
|
@ -382,6 +490,56 @@ impl GitStore {
|
||||||
job_tx
|
job_tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn git_init(
|
||||||
|
&self,
|
||||||
|
path: Arc<Path>,
|
||||||
|
fallback_branch_name: String,
|
||||||
|
cx: &App,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
match &self.state {
|
||||||
|
GitStoreState::Local { fs, .. } => {
|
||||||
|
let fs = fs.clone();
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn(async move { fs.git_init(&path, fallback_branch_name) })
|
||||||
|
}
|
||||||
|
GitStoreState::Ssh {
|
||||||
|
upstream_client,
|
||||||
|
project_id,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| GitStoreState::Remote {
|
||||||
|
upstream_client,
|
||||||
|
project_id,
|
||||||
|
} => {
|
||||||
|
let client = upstream_client.clone();
|
||||||
|
let project_id = *project_id;
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
client
|
||||||
|
.request(proto::GitInit {
|
||||||
|
project_id: project_id.0,
|
||||||
|
abs_path: path.to_string_lossy().to_string(),
|
||||||
|
fallback_branch_name,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_git_init(
|
||||||
|
this: Entity<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::GitInit>,
|
||||||
|
cx: AsyncApp,
|
||||||
|
) -> Result<proto::Ack> {
|
||||||
|
let path: Arc<Path> = PathBuf::from(envelope.payload.abs_path).into();
|
||||||
|
let name = envelope.payload.fallback_branch_name;
|
||||||
|
cx.update(|cx| this.read(cx).git_init(path, name, cx))?
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(proto::Ack {})
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_fetch(
|
async fn handle_fetch(
|
||||||
this: Entity<Self>,
|
this: Entity<Self>,
|
||||||
envelope: TypedEnvelope<proto::Fetch>,
|
envelope: TypedEnvelope<proto::Fetch>,
|
||||||
|
@ -889,7 +1047,7 @@ fn make_remote_delegate(
|
||||||
) -> AskPassDelegate {
|
) -> AskPassDelegate {
|
||||||
AskPassDelegate::new(cx, move |prompt, tx, cx| {
|
AskPassDelegate::new(cx, move |prompt, tx, cx| {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
let response = this.client.request(proto::AskPassRequest {
|
let response = this.client().request(proto::AskPassRequest {
|
||||||
project_id,
|
project_id,
|
||||||
worktree_id: worktree_id.to_proto(),
|
worktree_id: worktree_id.to_proto(),
|
||||||
work_directory_id: work_directory_id.to_proto(),
|
work_directory_id: work_directory_id.to_proto(),
|
||||||
|
|
|
@ -841,12 +841,12 @@ impl Project {
|
||||||
});
|
});
|
||||||
|
|
||||||
let git_store = cx.new(|cx| {
|
let git_store = cx.new(|cx| {
|
||||||
GitStore::new(
|
GitStore::local(
|
||||||
&worktree_store,
|
&worktree_store,
|
||||||
buffer_store.clone(),
|
buffer_store.clone(),
|
||||||
Some(environment.clone()),
|
environment.clone(),
|
||||||
|
fs.clone(),
|
||||||
client.clone().into(),
|
client.clone().into(),
|
||||||
None,
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -970,12 +970,12 @@ impl Project {
|
||||||
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
||||||
|
|
||||||
let git_store = cx.new(|cx| {
|
let git_store = cx.new(|cx| {
|
||||||
GitStore::new(
|
GitStore::ssh(
|
||||||
&worktree_store,
|
&worktree_store,
|
||||||
buffer_store.clone(),
|
buffer_store.clone(),
|
||||||
Some(environment.clone()),
|
environment.clone(),
|
||||||
ssh_proto.clone(),
|
ssh_proto.clone(),
|
||||||
Some(ProjectId(SSH_PROJECT_ID)),
|
ProjectId(SSH_PROJECT_ID),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -1177,12 +1177,12 @@ impl Project {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let git_store = cx.new(|cx| {
|
let git_store = cx.new(|cx| {
|
||||||
GitStore::new(
|
GitStore::remote(
|
||||||
|
// In this remote case we pass None for the environment
|
||||||
&worktree_store,
|
&worktree_store,
|
||||||
buffer_store.clone(),
|
buffer_store.clone(),
|
||||||
None,
|
|
||||||
client.clone().into(),
|
client.clone().into(),
|
||||||
Some(ProjectId(remote_id)),
|
ProjectId(remote_id),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -4443,6 +4443,17 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn git_init(
|
||||||
|
&self,
|
||||||
|
path: Arc<Path>,
|
||||||
|
fallback_branch_name: String,
|
||||||
|
cx: &App,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
self.git_store
|
||||||
|
.read(cx)
|
||||||
|
.git_init(path, fallback_branch_name, cx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn buffer_store(&self) -> &Entity<BufferStore> {
|
pub fn buffer_store(&self) -> &Entity<BufferStore> {
|
||||||
&self.buffer_store
|
&self.buffer_store
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,7 +344,9 @@ message Envelope {
|
||||||
AskPassResponse ask_pass_response = 318;
|
AskPassResponse ask_pass_response = 318;
|
||||||
|
|
||||||
GitDiff git_diff = 319;
|
GitDiff git_diff = 319;
|
||||||
GitDiffResponse git_diff_response = 320; // current max
|
GitDiffResponse git_diff_response = 320;
|
||||||
|
|
||||||
|
GitInit git_init = 321; // current max
|
||||||
}
|
}
|
||||||
|
|
||||||
reserved 87 to 88;
|
reserved 87 to 88;
|
||||||
|
@ -2937,3 +2939,9 @@ message GitDiff {
|
||||||
message GitDiffResponse {
|
message GitDiffResponse {
|
||||||
string diff = 1;
|
string diff = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GitInit {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
string abs_path = 2;
|
||||||
|
string fallback_branch_name = 3;
|
||||||
|
}
|
||||||
|
|
|
@ -460,6 +460,7 @@ messages!(
|
||||||
(CheckForPushedCommitsResponse, Background),
|
(CheckForPushedCommitsResponse, Background),
|
||||||
(GitDiff, Background),
|
(GitDiff, Background),
|
||||||
(GitDiffResponse, Background),
|
(GitDiffResponse, Background),
|
||||||
|
(GitInit, Background),
|
||||||
);
|
);
|
||||||
|
|
||||||
request_messages!(
|
request_messages!(
|
||||||
|
@ -607,6 +608,7 @@ request_messages!(
|
||||||
(GitChangeBranch, Ack),
|
(GitChangeBranch, Ack),
|
||||||
(CheckForPushedCommits, CheckForPushedCommitsResponse),
|
(CheckForPushedCommits, CheckForPushedCommitsResponse),
|
||||||
(GitDiff, GitDiffResponse),
|
(GitDiff, GitDiffResponse),
|
||||||
|
(GitInit, Ack),
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
|
@ -713,6 +715,7 @@ entity_messages!(
|
||||||
GitCreateBranch,
|
GitCreateBranch,
|
||||||
CheckForPushedCommits,
|
CheckForPushedCommits,
|
||||||
GitDiff,
|
GitDiff,
|
||||||
|
GitInit,
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
|
|
|
@ -89,12 +89,12 @@ impl HeadlessProject {
|
||||||
|
|
||||||
let environment = project::ProjectEnvironment::new(&worktree_store, None, cx);
|
let environment = project::ProjectEnvironment::new(&worktree_store, None, cx);
|
||||||
let git_store = cx.new(|cx| {
|
let git_store = cx.new(|cx| {
|
||||||
GitStore::new(
|
GitStore::local(
|
||||||
&worktree_store,
|
&worktree_store,
|
||||||
buffer_store.clone(),
|
buffer_store.clone(),
|
||||||
Some(environment.clone()),
|
environment.clone(),
|
||||||
|
fs.clone(),
|
||||||
session.clone().into(),
|
session.clone().into(),
|
||||||
None,
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue