diff --git a/assets/settings/default.json b/assets/settings/default.json index d3a9f5b9191befa3d8e48e81fda040e19247ff16..edcf18a6c74acd03905d41fff7d977f4ca27d884 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -555,6 +555,12 @@ // // Default: icon "status_style": "icon", + // What branch name to use if init.defaultBranch + // is not set + // + // Default: main + "fallback_branch_name": "main", + "scrollbar": { // When to show the scrollbar in the git panel. // diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 0057bafee7a7c34893c1f26304af6f755738673f..c820fcdfc8bc8feec7af99c8472ea7c42d3ffd5b 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -396,6 +396,7 @@ impl Server { .add_request_handler(forward_mutating_project_request::) .add_request_handler(forward_mutating_project_request::) .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) .add_request_handler(forward_read_only_project_request::) diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 05946de3b7bf3764d025d01f18e1bfd6b51c9cf0..46df19722c236fa4453205a2b555ef86ce347e2c 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -16,12 +16,14 @@ use git::{repository::RepoPath, status::FileStatus}; #[cfg(any(target_os = "linux", target_os = "freebsd"))] use ashpd::desktop::trash; +use std::borrow::Cow; #[cfg(any(test, feature = "test-support"))] use std::collections::HashSet; #[cfg(unix)] use std::os::fd::AsFd; #[cfg(unix)] use std::os::fd::AsRawFd; +use util::command::new_std_command; #[cfg(unix)] use std::os::unix::fs::MetadataExt; @@ -136,6 +138,7 @@ pub trait Fs: Send + Sync { fn home_dir(&self) -> Option; fn open_repo(&self, abs_dot_git: &Path) -> Option>; + fn git_init(&self, abs_work_directory: &Path, fallback_branch_name: String) -> Result<()>; fn is_fake(&self) -> bool; async fn is_case_sensitive(&self) -> Result; @@ -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 { 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 { true } diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index d5f2136df0af8464ac2823f085a8bf0be09a896e..f78557702e56f5eb1568b3f2aa18aaf1c1577079 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -50,7 +50,8 @@ actions!( Fetch, Commit, ExpandCommitEditor, - GenerateCommitMessage + GenerateCommitMessage, + Init, ] ); action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]); diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 7efb74ef014ca0b3134ba54e9ec000e2edcfd790..8fe306c932106b9bd15db9e0e0c19ac7846f0943 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1767,6 +1767,88 @@ impl GitPanel { .detach_and_log_err(cx); } + pub(crate) fn git_init(&mut self, window: &mut Window, cx: &mut Context) { + let worktrees = self + .project + .read(cx) + .visible_worktrees(cx) + .collect::>(); + + 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) { if !self.can_push_and_pull(cx) { return; @@ -1971,7 +2053,7 @@ impl GitPanel { cx, ) })? - .await?; + .await; Ok(selection.map(|selection| Remote { name: current_remotes[selection].clone(), @@ -2677,7 +2759,13 @@ impl GitPanel { }) } - fn render_panel_header(&self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + fn render_panel_header( + &self, + window: &mut Window, + cx: &mut Context, + ) -> Option { + self.active_repository.as_ref()?; + let text; let action; let tooltip; @@ -2697,39 +2785,42 @@ impl GitPanel { _ => format!("{} Changes", self.entry_count), }; - self.panel_header_container(window, cx) - .px_2() - .child( - panel_button(change_string) - .color(Color::Muted) - .tooltip(Tooltip::for_action_title_in( - "Open diff", - &Diff, - &self.focus_handle, - )) - .on_click(|_, _, cx| { - cx.defer(|cx| { - cx.dispatch_action(&Diff); - }) - }), - ) - .child(div().flex_grow()) // spacer - .child(self.render_overflow_menu("overflow_menu")) - .child( - panel_filled_button(text) - .tooltip(Tooltip::for_action_title_in( - tooltip, - action.as_ref(), - &self.focus_handle, - )) - .disabled(self.entry_count == 0) - .on_click(move |_, _, cx| { - let action = action.boxed_clone(); - cx.defer(move |cx| { - cx.dispatch_action(action.as_ref()); - }) - }), - ) + Some( + self.panel_header_container(window, cx) + .px_2() + .child( + panel_button(change_string) + .color(Color::Muted) + .tooltip(Tooltip::for_action_title_in( + "Open diff", + &Diff, + &self.focus_handle, + )) + .on_click(|_, _, cx| { + cx.defer(|cx| { + cx.dispatch_action(&Diff); + }) + }), + ) + .child(div().flex_grow()) // spacer + .child(self.render_overflow_menu("overflow_menu")) + .child(div().w_2()) // another spacer + .child( + panel_filled_button(text) + .tooltip(Tooltip::for_action_title_in( + tooltip, + action.as_ref(), + &self.focus_handle, + )) + .disabled(self.entry_count == 0) + .on_click(move |_, _, cx| { + let action = action.boxed_clone(); + cx.defer(move |cx| { + cx.dispatch_action(action.as_ref()); + }) + }), + ), + ) } pub fn render_footer( @@ -2949,12 +3040,29 @@ impl GitPanel { .items_center() .child( v_flex() - .gap_3() - .child(if self.active_repository.is_some() { - "No changes to commit" - } else { - "No Git repositories" - }) + .gap_2() + .child(h_flex().w_full().justify_around().child( + if self.active_repository.is_some() { + "No changes to commit" + } else { + "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) .mx_auto() .text_color(Color::Placeholder.color(cx)), @@ -3674,7 +3782,7 @@ impl Render for GitPanel { .child( v_flex() .size_full() - .child(self.render_panel_header(window, cx)) + .children(self.render_panel_header(window, cx)) .map(|this| { if has_entries { this.child(self.render_entries(has_write_access, window, cx)) diff --git a/crates/git_ui/src/git_panel_settings.rs b/crates/git_ui/src/git_panel_settings.rs index aac84b5fbe6f2a5c4cbcf4d49c1edd8971d2210f..99710225fcdb57fb206ad9ca211eae6b8d5b5a2c 100644 --- a/crates/git_ui/src/git_panel_settings.rs +++ b/crates/git_ui/src/git_panel_settings.rs @@ -58,15 +58,22 @@ pub struct GitPanelSettingsContent { /// /// Default: inherits editor scrollbar settings pub scrollbar: Option, + + /// What the default branch name should be when + /// `init.defaultBranch` is not set in git + /// + /// Default: main + pub fallback_branch_name: Option, } -#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] +#[derive(Deserialize, Debug, Clone, PartialEq)] pub struct GitPanelSettings { pub button: bool, pub dock: DockPosition, pub default_width: Pixels, pub status_style: StatusStyle, pub scrollbar: ScrollbarSettings, + pub fallback_branch_name: String, } impl Settings for GitPanelSettings { diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index a49434a258c2c78259ec7f75f112b0fe9d4cca27..5e10289f1c1e8cdd2cefcb01d1d9437881d95891 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -82,6 +82,14 @@ pub fn init(cx: &mut App) { panel.unstage_all(action, window, cx); }); }); + workspace.register_action(|workspace, _action: &git::Init, window, cx| { + let Some(panel) = workspace.panel::(cx) else { + return; + }; + panel.update(cx, |panel, cx| { + panel.git_init(window, cx); + }); + }); }) .detach(); } diff --git a/crates/git_ui/src/picker_prompt.rs b/crates/git_ui/src/picker_prompt.rs index 3723dfe9f508b93b3ee855c363576565312a70ca..7de17c0ceeb9cb7b9fbfc1b0d049f3e345f7d10d 100644 --- a/crates/git_ui/src/picker_prompt.rs +++ b/crates/git_ui/src/picker_prompt.rs @@ -1,4 +1,3 @@ -use anyhow::{anyhow, Result}; use futures::channel::oneshot; use fuzzy::{StringMatch, StringMatchCandidate}; @@ -26,9 +25,9 @@ pub fn prompt( workspace: WeakEntity, window: &mut Window, cx: &mut App, -) -> Task>> { +) -> Task> { if options.is_empty() { - return Task::ready(Err(anyhow!("No options"))); + return Task::ready(None); } let prompt = prompt.to_string().into(); @@ -37,15 +36,17 @@ pub fn prompt( let (tx, rx) = oneshot::channel(); let delegate = PickerPromptDelegate::new(prompt, options, tx, 70); - workspace.update_in(&mut cx, |workspace, window, cx| { - workspace.toggle_modal(window, cx, |window, cx| { - PickerPrompt::new(delegate, 34., window, cx) + workspace + .update_in(&mut cx, |workspace, window, cx| { + workspace.toggle_modal(window, cx, |window, cx| { + PickerPrompt::new(delegate, 34., window, cx) + }) }) - })?; + .ok(); match rx.await { - Ok(selection) => Some(selection).transpose(), - Err(_) => anyhow::Ok(None), // User cancelled + Ok(selection) => Some(selection), + Err(_) => None, // User cancelled } }) } @@ -94,14 +95,14 @@ pub struct PickerPromptDelegate { all_options: Vec, selected_index: usize, max_match_length: usize, - tx: Option>>, + tx: Option>, } impl PickerPromptDelegate { pub fn new( prompt: Arc, options: Vec, - tx: oneshot::Sender>, + tx: oneshot::Sender, max_chars: usize, ) -> Self { Self { @@ -200,7 +201,7 @@ impl PickerDelegate for PickerPromptDelegate { 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); } diff --git a/crates/project/src/git.rs b/crates/project/src/git.rs index 6e24caa8b5ade4e90cf4d8500ced0fed6ad6ef06..e56c6eb998e7938c6c9ee8c05df4277ca3ba7ce3 100644 --- a/crates/project/src/git.rs +++ b/crates/project/src/git.rs @@ -8,6 +8,7 @@ use askpass::{AskPassDelegate, AskPassSession}; use buffer_diff::BufferDiffEvent; use client::ProjectId; use collections::HashMap; +use fs::Fs; use futures::{ channel::{mpsc, oneshot}, future::OptionFuture, @@ -38,21 +39,37 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; + use text::BufferId; use util::{debug_panic, maybe, ResultExt}; use worktree::{ProjectEntryId, RepositoryEntry, StatusEntry, WorkDirectory}; pub struct GitStore { + state: GitStoreState, buffer_store: Entity, - environment: Option>, - pub(super) project_id: Option, - pub(super) client: AnyProtoClient, repositories: Vec>, active_index: Option, update_sender: mpsc::UnboundedSender, _subscriptions: [Subscription; 2], } +enum GitStoreState { + Local { + client: AnyProtoClient, + environment: Entity, + fs: Arc, + }, + Ssh { + environment: Entity, + upstream_client: AnyProtoClient, + project_id: ProjectId, + }, + Remote { + upstream_client: AnyProtoClient, + project_id: ProjectId, + }, +} + pub struct Repository { commit_message_buffer: Option>, git_store: WeakEntity, @@ -101,12 +118,12 @@ enum GitJobKey { impl EventEmitter for GitStore {} impl GitStore { - pub fn new( + pub fn local( worktree_store: &Entity, buffer_store: Entity, - environment: Option>, + environment: Entity, + fs: Arc, client: AnyProtoClient, - project_id: Option, cx: &mut Context<'_, Self>, ) -> Self { let update_sender = Self::spawn_git_worker(cx); @@ -115,11 +132,73 @@ impl GitStore { cx.subscribe(&buffer_store, Self::on_buffer_store_event), ]; + let state = GitStoreState::Local { + client, + environment, + fs, + }; + GitStore { + state, + buffer_store, + repositories: Vec::new(), + active_index: None, + update_sender, + _subscriptions, + } + } + + pub fn remote( + worktree_store: &Entity, + buffer_store: Entity, + 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, - client, + }; + + GitStore { + state, buffer_store, + repositories: Vec::new(), + active_index: None, + update_sender, + _subscriptions, + } + } + + pub fn ssh( + worktree_store: &Entity, + buffer_store: Entity, + environment: Entity, + 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(), active_index: None, update_sender, @@ -132,6 +211,7 @@ impl GitStore { 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_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_pull); client.add_entity_request_handler(Self::handle_fetch); @@ -153,6 +233,34 @@ impl GitStore { .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> { + match &self.state { + GitStoreState::Local { environment, .. } => Some(environment.clone()), + GitStoreState::Ssh { environment, .. } => Some(environment.clone()), + GitStoreState::Remote { .. } => None, + } + } + + fn project_id(&self) -> Option { + 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( &mut self, worktree_store: Entity, @@ -162,8 +270,8 @@ impl GitStore { let mut new_repositories = Vec::new(); let mut new_active_index = None; let this = cx.weak_entity(); - let client = self.client.clone(); - let project_id = self.project_id; + let client = self.client(); + let project_id = self.project_id(); worktree_store.update(cx, |worktree_store, cx| { for worktree in worktree_store.worktrees() { @@ -229,9 +337,9 @@ impl GitStore { }); existing_handle } else { + let environment = self.project_environment(); cx.new(|_| Repository { - project_environment: self - .environment + project_environment: environment .as_ref() .map(|env| env.downgrade()), git_store: this.clone(), @@ -382,6 +490,56 @@ impl GitStore { job_tx } + pub fn git_init( + &self, + path: Arc, + fallback_branch_name: String, + cx: &App, + ) -> Task> { + 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, + envelope: TypedEnvelope, + cx: AsyncApp, + ) -> Result { + let path: Arc = 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( this: Entity, envelope: TypedEnvelope, @@ -889,7 +1047,7 @@ fn make_remote_delegate( ) -> AskPassDelegate { AskPassDelegate::new(cx, move |prompt, tx, cx| { this.update(cx, |this, cx| { - let response = this.client.request(proto::AskPassRequest { + let response = this.client().request(proto::AskPassRequest { project_id, worktree_id: worktree_id.to_proto(), work_directory_id: work_directory_id.to_proto(), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 52351a5eb83c4043d6d80fcd07cf1abc2d3f67cd..5ffadd023cb405332751404851f2174646b90795 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -841,12 +841,12 @@ impl Project { }); let git_store = cx.new(|cx| { - GitStore::new( + GitStore::local( &worktree_store, buffer_store.clone(), - Some(environment.clone()), + environment.clone(), + fs.clone(), client.clone().into(), - None, cx, ) }); @@ -970,12 +970,12 @@ impl Project { cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach(); let git_store = cx.new(|cx| { - GitStore::new( + GitStore::ssh( &worktree_store, buffer_store.clone(), - Some(environment.clone()), + environment.clone(), ssh_proto.clone(), - Some(ProjectId(SSH_PROJECT_ID)), + ProjectId(SSH_PROJECT_ID), cx, ) }); @@ -1177,12 +1177,12 @@ impl Project { })?; let git_store = cx.new(|cx| { - GitStore::new( + GitStore::remote( + // In this remote case we pass None for the environment &worktree_store, buffer_store.clone(), - None, client.clone().into(), - Some(ProjectId(remote_id)), + ProjectId(remote_id), cx, ) })?; @@ -4443,6 +4443,17 @@ impl Project { }) } + pub fn git_init( + &self, + path: Arc, + fallback_branch_name: String, + cx: &App, + ) -> Task> { + self.git_store + .read(cx) + .git_init(path, fallback_branch_name, cx) + } + pub fn buffer_store(&self) -> &Entity { &self.buffer_store } diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 5feddb9269912d9029b463af8cb8e143fdf9d0b6..5493b870173a840c04c041f6e7199ee78e2bec98 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -344,7 +344,9 @@ message Envelope { AskPassResponse ask_pass_response = 318; 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; @@ -2937,3 +2939,9 @@ message GitDiff { message GitDiffResponse { string diff = 1; } + +message GitInit { + uint64 project_id = 1; + string abs_path = 2; + string fallback_branch_name = 3; +} diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index ac0a290c43a433dd51a0ecabe0700bb14b38d344..16a08cd42f73094920a20f8d0ed484233f2c88f6 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -460,6 +460,7 @@ messages!( (CheckForPushedCommitsResponse, Background), (GitDiff, Background), (GitDiffResponse, Background), + (GitInit, Background), ); request_messages!( @@ -607,6 +608,7 @@ request_messages!( (GitChangeBranch, Ack), (CheckForPushedCommits, CheckForPushedCommitsResponse), (GitDiff, GitDiffResponse), + (GitInit, Ack), ); entity_messages!( @@ -713,6 +715,7 @@ entity_messages!( GitCreateBranch, CheckForPushedCommits, GitDiff, + GitInit, ); entity_messages!( diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 0617c195f6fca71e9158f6de29b86138bea24c25..3d32ccbec9c32b3389acfcfd8d2f0ad8c8a95bd3 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -89,12 +89,12 @@ impl HeadlessProject { let environment = project::ProjectEnvironment::new(&worktree_store, None, cx); let git_store = cx.new(|cx| { - GitStore::new( + GitStore::local( &worktree_store, buffer_store.clone(), - Some(environment.clone()), + environment.clone(), + fs.clone(), session.clone().into(), - None, cx, ) });