vcs: Add 'create branch' button
This commit is contained in:
parent
79ece8a86e
commit
cb24cb1ea5
2 changed files with 108 additions and 34 deletions
|
@ -1,6 +1,8 @@
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle};
|
use gpui::{
|
||||||
|
elements::*, platform::MouseButton, AppContext, MouseState, Task, ViewContext, ViewHandle,
|
||||||
|
};
|
||||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use std::{ops::Not, sync::Arc};
|
use std::{ops::Not, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -35,6 +37,14 @@ pub struct BranchListDelegate {
|
||||||
last_query: String,
|
last_query: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BranchListDelegate {
|
||||||
|
fn display_error_toast(&self, message: String, cx: &mut ViewContext<BranchList>) {
|
||||||
|
const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
|
||||||
|
self.workspace.update(cx, |model, ctx| {
|
||||||
|
model.show_toast(Toast::new(GIT_CHECKOUT_FAILURE_ID, message), ctx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
impl PickerDelegate for BranchListDelegate {
|
impl PickerDelegate for BranchListDelegate {
|
||||||
fn placeholder_text(&self) -> Arc<str> {
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
"Select branch...".into()
|
"Select branch...".into()
|
||||||
|
@ -136,40 +146,39 @@ impl PickerDelegate for BranchListDelegate {
|
||||||
let current_pick = self.selected_index();
|
let current_pick = self.selected_index();
|
||||||
let current_pick = self.matches[current_pick].string.clone();
|
let current_pick = self.matches[current_pick].string.clone();
|
||||||
cx.spawn(|picker, mut cx| async move {
|
cx.spawn(|picker, mut cx| async move {
|
||||||
picker.update(&mut cx, |this, cx| {
|
picker
|
||||||
let project = this.delegate().workspace.read(cx).project().read(cx);
|
.update(&mut cx, |this, cx| {
|
||||||
let mut cwd = project
|
let project = this.delegate().workspace.read(cx).project().read(cx);
|
||||||
.visible_worktrees(cx)
|
let mut cwd = project
|
||||||
.next()
|
.visible_worktrees(cx)
|
||||||
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
.next()
|
||||||
.read(cx)
|
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
||||||
.abs_path()
|
.read(cx)
|
||||||
.to_path_buf();
|
.abs_path()
|
||||||
cwd.push(".git");
|
.to_path_buf();
|
||||||
let status = project
|
cwd.push(".git");
|
||||||
.fs()
|
let status = project
|
||||||
.open_repo(&cwd)
|
.fs()
|
||||||
.ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))?
|
.open_repo(&cwd)
|
||||||
.lock()
|
.ok_or_else(|| {
|
||||||
.change_branch(¤t_pick);
|
anyhow!(
|
||||||
if status.is_err() {
|
"Could not open repository at path `{}`",
|
||||||
const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
|
cwd.as_os_str().to_string_lossy()
|
||||||
this.delegate().workspace.update(cx, |model, ctx| {
|
)
|
||||||
model.show_toast(
|
})?
|
||||||
Toast::new(
|
.lock()
|
||||||
GIT_CHECKOUT_FAILURE_ID,
|
.change_branch(¤t_pick);
|
||||||
format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"),
|
if status.is_err() {
|
||||||
),
|
this.delegate().display_error_toast(format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||||
ctx,
|
status?;
|
||||||
)
|
}
|
||||||
});
|
cx.emit(PickerEvent::Dismiss);
|
||||||
status?;
|
|
||||||
}
|
|
||||||
cx.emit(PickerEvent::Dismiss);
|
|
||||||
|
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
}).log_err();
|
})
|
||||||
}).detach();
|
.log_err();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
@ -235,4 +244,60 @@ impl PickerDelegate for BranchListDelegate {
|
||||||
};
|
};
|
||||||
Some(label.into_any())
|
Some(label.into_any())
|
||||||
}
|
}
|
||||||
|
fn render_footer(
|
||||||
|
&self,
|
||||||
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> Option<AnyElement<Picker<Self>>> {
|
||||||
|
if !self.last_query.is_empty() {
|
||||||
|
let theme = &theme::current(cx);
|
||||||
|
let style = theme.picker.footer.clone();
|
||||||
|
enum BranchCreateButton {}
|
||||||
|
Some(
|
||||||
|
Flex::row().with_child(MouseEventHandler::<BranchCreateButton, _>::new(0, cx, |_, _| {
|
||||||
|
Label::new("Create branch", style.label.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.aligned()
|
||||||
|
.right()
|
||||||
|
})
|
||||||
|
.on_down(MouseButton::Left, |_, _, cx| {
|
||||||
|
cx.spawn(|picker, mut cx| async move {
|
||||||
|
picker.update(&mut cx, |this, cx| {
|
||||||
|
let project = this.delegate().workspace.read(cx).project().read(cx);
|
||||||
|
let current_pick = &this.delegate().last_query;
|
||||||
|
let mut cwd = project
|
||||||
|
.visible_worktrees(cx)
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
||||||
|
.read(cx)
|
||||||
|
.abs_path()
|
||||||
|
.to_path_buf();
|
||||||
|
cwd.push(".git");
|
||||||
|
let repo = project
|
||||||
|
.fs()
|
||||||
|
.open_repo(&cwd)
|
||||||
|
.ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))?;
|
||||||
|
let repo = repo
|
||||||
|
.lock();
|
||||||
|
let status = repo
|
||||||
|
.create_branch(¤t_pick);
|
||||||
|
if status.is_err() {
|
||||||
|
this.delegate().display_error_toast(format!("Failed to create branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||||
|
status?;
|
||||||
|
}
|
||||||
|
let status = repo.change_branch(¤t_pick);
|
||||||
|
if status.is_err() {
|
||||||
|
this.delegate().display_error_toast(format!("Failed to chec branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||||
|
status?;
|
||||||
|
}
|
||||||
|
Ok::<(), anyhow::Error>(())
|
||||||
|
})
|
||||||
|
}).detach();
|
||||||
|
}))
|
||||||
|
.into_any(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ pub trait GitRepository: Send {
|
||||||
fn change_branch(&self, _: &str) -> Result<()> {
|
fn change_branch(&self, _: &str) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
fn create_branch(&self, _: &str) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for dyn GitRepository {
|
impl std::fmt::Debug for dyn GitRepository {
|
||||||
|
@ -152,6 +155,12 @@ impl GitRepository for LibGitRepository {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
fn create_branch(&self, name: &str) -> Result<()> {
|
||||||
|
let current_commit = self.head()?.peel_to_commit()?;
|
||||||
|
self.branch(name, ¤t_commit, false)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_status(status: git2::Status) -> Option<GitFileStatus> {
|
fn read_status(status: git2::Status) -> Option<GitFileStatus> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue