Git activity indicator (#28204)
Closes #26182 Release Notes: - Added an activity indicator for long-running git commands. --------- Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
4f9f443452
commit
e3830d2ef5
@ -11,13 +11,22 @@ use language::{BinaryStatus, LanguageRegistry, LanguageServerId};
|
||||
use project::{
|
||||
EnvironmentErrorMessage, LanguageServerProgress, LspStoreEvent, Project,
|
||||
ProjectEnvironmentEvent,
|
||||
git_store::{GitStoreEvent, Repository},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp::Reverse, fmt::Write, path::Path, sync::Arc, time::Duration};
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
fmt::Write,
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use ui::{ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
|
||||
use util::truncate_and_trailoff;
|
||||
use workspace::{StatusItemView, Workspace, item::ItemHandle};
|
||||
|
||||
const GIT_OPERATION_DELAY: Duration = Duration::from_millis(0);
|
||||
|
||||
actions!(activity_indicator, [ShowErrorMessage]);
|
||||
|
||||
pub enum Event {
|
||||
@ -105,6 +114,15 @@ impl ActivityIndicator {
|
||||
)
|
||||
.detach();
|
||||
|
||||
cx.subscribe(
|
||||
&project.read(cx).git_store().clone(),
|
||||
|_, _, event: &GitStoreEvent, cx| match event {
|
||||
project::git_store::GitStoreEvent::JobsUpdated => cx.notify(),
|
||||
_ => {}
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
if let Some(auto_updater) = auto_updater.as_ref() {
|
||||
cx.observe(auto_updater, |_, _, cx| cx.notify()).detach();
|
||||
}
|
||||
@ -285,6 +303,34 @@ impl ActivityIndicator {
|
||||
});
|
||||
}
|
||||
|
||||
let current_job = self
|
||||
.project
|
||||
.read(cx)
|
||||
.active_repository(cx)
|
||||
.map(|r| r.read(cx))
|
||||
.and_then(Repository::current_job);
|
||||
// Show any long-running git command
|
||||
if let Some(job_info) = current_job {
|
||||
if Instant::now() - job_info.start >= GIT_OPERATION_DELAY {
|
||||
return Some(Content {
|
||||
icon: Some(
|
||||
Icon::new(IconName::ArrowCircle)
|
||||
.size(IconSize::Small)
|
||||
.with_animation(
|
||||
"arrow-circle",
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
|icon, delta| {
|
||||
icon.transform(Transformation::rotate(percentage(delta)))
|
||||
},
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
message: job_info.message.into(),
|
||||
on_click: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Show any language server installation info.
|
||||
let mut downloading = SmallVec::<[_; 3]>::new();
|
||||
let mut checking_for_update = SmallVec::<[_; 3]>::new();
|
||||
|
@ -226,7 +226,8 @@ impl MessageEditor {
|
||||
|
||||
let thread = self.thread.clone();
|
||||
let context_store = self.context_store.clone();
|
||||
let checkpoint = self.project.read(cx).git_store().read(cx).checkpoint(cx);
|
||||
let git_store = self.project.read(cx).git_store().clone();
|
||||
let checkpoint = git_store.update(cx, |git_store, cx| git_store.checkpoint(cx));
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let checkpoint = checkpoint.await.ok();
|
||||
|
@ -471,11 +471,11 @@ impl Thread {
|
||||
cx.emit(ThreadEvent::CheckpointChanged);
|
||||
cx.notify();
|
||||
|
||||
let project = self.project.read(cx);
|
||||
let restore = project
|
||||
.git_store()
|
||||
.read(cx)
|
||||
.restore_checkpoint(checkpoint.git_checkpoint.clone(), cx);
|
||||
let git_store = self.project().read(cx).git_store().clone();
|
||||
let restore = git_store.update(cx, |git_store, cx| {
|
||||
git_store.restore_checkpoint(checkpoint.git_checkpoint.clone(), cx)
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let result = restore.await;
|
||||
this.update(cx, |this, cx| {
|
||||
@ -506,11 +506,11 @@ impl Thread {
|
||||
};
|
||||
|
||||
let git_store = self.project.read(cx).git_store().clone();
|
||||
let final_checkpoint = git_store.read(cx).checkpoint(cx);
|
||||
let final_checkpoint = git_store.update(cx, |git_store, cx| git_store.checkpoint(cx));
|
||||
cx.spawn(async move |this, cx| match final_checkpoint.await {
|
||||
Ok(final_checkpoint) => {
|
||||
let equal = git_store
|
||||
.read_with(cx, |store, cx| {
|
||||
.update(cx, |store, cx| {
|
||||
store.compare_checkpoints(
|
||||
pending_checkpoint.git_checkpoint.clone(),
|
||||
final_checkpoint.clone(),
|
||||
@ -522,7 +522,7 @@ impl Thread {
|
||||
|
||||
if equal {
|
||||
git_store
|
||||
.read_with(cx, |store, cx| {
|
||||
.update(cx, |store, cx| {
|
||||
store.delete_checkpoint(pending_checkpoint.git_checkpoint, cx)
|
||||
})?
|
||||
.detach();
|
||||
@ -533,7 +533,7 @@ impl Thread {
|
||||
}
|
||||
|
||||
git_store
|
||||
.read_with(cx, |store, cx| {
|
||||
.update(cx, |store, cx| {
|
||||
store.delete_checkpoint(final_checkpoint, cx)
|
||||
})?
|
||||
.detach();
|
||||
@ -1650,10 +1650,10 @@ impl Thread {
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|repo| {
|
||||
repo.read_with(cx, |repo, _| {
|
||||
repo.update(cx, |repo, _| {
|
||||
let current_branch =
|
||||
repo.branch.as_ref().map(|branch| branch.name.to_string());
|
||||
repo.send_job(|state, _| async move {
|
||||
repo.send_job(None, |state, _| async move {
|
||||
let RepositoryState::Local { backend, .. } = state else {
|
||||
return GitState {
|
||||
remote_url: None,
|
||||
|
@ -6866,10 +6866,14 @@ async fn test_remote_git_branches(
|
||||
|
||||
assert_eq!(branches_b, branches_set);
|
||||
|
||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.change_branch(new_branch.to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@ -6892,18 +6896,18 @@ async fn test_remote_git_branches(
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.create_branch("totally-new-branch".to_string())
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.create_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.change_branch("totally-new-branch".to_string())
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
|
@ -283,7 +283,7 @@ async fn test_ssh_collaboration_git_branches(
|
||||
let repo_b = cx_b.update(|cx| project_b.read(cx).active_repository(cx).unwrap());
|
||||
|
||||
let branches_b = cx_b
|
||||
.update(|cx| repo_b.read(cx).branches())
|
||||
.update(|cx| repo_b.update(cx, |repo_b, _cx| repo_b.branches()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
@ -297,10 +297,14 @@ async fn test_ssh_collaboration_git_branches(
|
||||
|
||||
assert_eq!(&branches_b, &branches_set);
|
||||
|
||||
cx_b.update(|cx| repo_b.read(cx).change_branch(new_branch.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repo_b, _cx| {
|
||||
repo_b.change_branch(new_branch.to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
executor.run_until_parked();
|
||||
|
||||
@ -325,18 +329,18 @@ async fn test_ssh_collaboration_git_branches(
|
||||
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.create_branch("totally-new-branch".to_string())
|
||||
repo_b.update(cx, |repo_b, _cx| {
|
||||
repo_b.create_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx_b.update(|cx| {
|
||||
repo_b
|
||||
.read(cx)
|
||||
.change_branch("totally-new-branch".to_string())
|
||||
repo_b.update(cx, |repo_b, _cx| {
|
||||
repo_b.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
|
@ -436,7 +436,9 @@ impl GitBlame {
|
||||
}
|
||||
let buffer_edits = self.buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||
let snapshot = self.buffer.read(cx).snapshot();
|
||||
let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx);
|
||||
let blame = self.project.update(cx, |project, cx| {
|
||||
project.blame_buffer(&self.buffer, None, cx)
|
||||
});
|
||||
let provider_registry = GitHostingProviderRegistry::default_global(cx);
|
||||
|
||||
self.task = cx.spawn(async move |this, cx| {
|
||||
|
@ -88,7 +88,7 @@ impl BranchList {
|
||||
) -> Self {
|
||||
let all_branches_request = repository
|
||||
.clone()
|
||||
.map(|repository| repository.read(cx).branches());
|
||||
.map(|repository| repository.update(cx, |repository, _| repository.branches()));
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let mut all_branches = all_branches_request
|
||||
@ -202,10 +202,15 @@ impl BranchListDelegate {
|
||||
return;
|
||||
};
|
||||
cx.spawn(async move |_, cx| {
|
||||
cx.update(|cx| repo.read(cx).create_branch(new_branch_name.to_string()))?
|
||||
.await??;
|
||||
cx.update(|cx| repo.read(cx).change_branch(new_branch_name.to_string()))?
|
||||
.await??;
|
||||
repo.update(cx, |repo, _| {
|
||||
repo.create_branch(new_branch_name.to_string())
|
||||
})?
|
||||
.await??;
|
||||
repo.update(cx, |repo, _| {
|
||||
repo.change_branch(new_branch_name.to_string())
|
||||
})?
|
||||
.await??;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_prompt_err("Failed to create branch", window, cx, |e, _, _| {
|
||||
@ -359,11 +364,13 @@ impl PickerDelegate for BranchListDelegate {
|
||||
.ok_or_else(|| anyhow!("No active repository"))?
|
||||
.clone();
|
||||
|
||||
let cx = cx.to_async();
|
||||
let mut cx = cx.to_async();
|
||||
|
||||
anyhow::Ok(async move {
|
||||
cx.update(|cx| repo.read(cx).change_branch(branch.name.to_string()))?
|
||||
.await?
|
||||
repo.update(&mut cx, |repo, _| {
|
||||
repo.change_branch(branch.name.to_string())
|
||||
})?
|
||||
.await?
|
||||
})
|
||||
})??;
|
||||
|
||||
|
@ -53,10 +53,8 @@ use project::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings as _, SettingsStore};
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::{collections::HashSet, sync::Arc, time::Duration, usize};
|
||||
use strum::{IntoEnumIterator, VariantNames};
|
||||
use time::OffsetDateTime;
|
||||
@ -64,7 +62,7 @@ use ui::{
|
||||
Checkbox, ContextMenu, ElevationIndex, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
|
||||
prelude::*,
|
||||
};
|
||||
use util::{ResultExt, TryFutureExt, maybe, post_inc};
|
||||
use util::{ResultExt, TryFutureExt, maybe};
|
||||
use workspace::AppState;
|
||||
|
||||
use notifications::status_toast::{StatusToast, ToastIcon};
|
||||
@ -232,8 +230,6 @@ struct PendingOperation {
|
||||
op_id: usize,
|
||||
}
|
||||
|
||||
type RemoteOperations = Rc<RefCell<HashSet<u32>>>;
|
||||
|
||||
// computed state related to how to render scrollbars
|
||||
// one per axis
|
||||
// on render we just read this off the panel
|
||||
@ -290,8 +286,6 @@ impl ScrollbarProperties {
|
||||
}
|
||||
|
||||
pub struct GitPanel {
|
||||
remote_operation_id: u32,
|
||||
pending_remote_operations: RemoteOperations,
|
||||
pub(crate) active_repository: Option<Entity<Repository>>,
|
||||
pub(crate) commit_editor: Entity<Editor>,
|
||||
conflicted_count: usize,
|
||||
@ -327,17 +321,6 @@ pub struct GitPanel {
|
||||
_settings_subscription: Subscription,
|
||||
}
|
||||
|
||||
struct RemoteOperationGuard {
|
||||
id: u32,
|
||||
pending_remote_operations: RemoteOperations,
|
||||
}
|
||||
|
||||
impl Drop for RemoteOperationGuard {
|
||||
fn drop(&mut self) {
|
||||
self.pending_remote_operations.borrow_mut().remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_PANEL_EDITOR_LINES: usize = 6;
|
||||
|
||||
pub(crate) fn commit_message_editor(
|
||||
@ -416,7 +399,7 @@ impl GitPanel {
|
||||
) => {
|
||||
this.schedule_update(*full_scan, window, cx);
|
||||
}
|
||||
GitStoreEvent::RepositoryUpdated(_, _, _) => {}
|
||||
|
||||
GitStoreEvent::RepositoryAdded(_) | GitStoreEvent::RepositoryRemoved(_) => {
|
||||
this.schedule_update(false, window, cx);
|
||||
}
|
||||
@ -427,6 +410,8 @@ impl GitPanel {
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
GitStoreEvent::RepositoryUpdated(_, _, _) => {}
|
||||
GitStoreEvent::JobsUpdated => {}
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
@ -458,8 +443,6 @@ impl GitPanel {
|
||||
});
|
||||
|
||||
let mut git_panel = Self {
|
||||
pending_remote_operations: Default::default(),
|
||||
remote_operation_id: 0,
|
||||
active_repository,
|
||||
commit_editor,
|
||||
conflicted_count: 0,
|
||||
@ -671,16 +654,6 @@ impl GitPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn start_remote_operation(&mut self) -> RemoteOperationGuard {
|
||||
let id = post_inc(&mut self.remote_operation_id);
|
||||
self.pending_remote_operations.borrow_mut().insert(id);
|
||||
|
||||
RemoteOperationGuard {
|
||||
id,
|
||||
pending_remote_operations: self.pending_remote_operations.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&mut self, cx: &mut Context<Self>) {
|
||||
let width = self.width;
|
||||
self.pending_serialization = cx.background_spawn(
|
||||
@ -1743,7 +1716,6 @@ impl GitPanel {
|
||||
return;
|
||||
};
|
||||
telemetry::event!("Git Fetched");
|
||||
let guard = self.start_remote_operation();
|
||||
let askpass = self.askpass_delegate("git fetch", window, cx);
|
||||
let this = cx.weak_entity();
|
||||
window
|
||||
@ -1751,7 +1723,6 @@ impl GitPanel {
|
||||
let fetch = repo.update(cx, |repo, cx| repo.fetch(askpass, cx))?;
|
||||
|
||||
let remote_message = fetch.await?;
|
||||
drop(guard);
|
||||
this.update(cx, |this, cx| {
|
||||
let action = RemoteAction::Fetch;
|
||||
match remote_message {
|
||||
@ -1883,16 +1854,11 @@ impl GitPanel {
|
||||
this.askpass_delegate(format!("git pull {}", remote.name), window, cx)
|
||||
})?;
|
||||
|
||||
let guard = this
|
||||
.update(cx, |this, _| this.start_remote_operation())
|
||||
.ok();
|
||||
|
||||
let pull = repo.update(cx, |repo, cx| {
|
||||
repo.pull(branch.name.clone(), remote.name.clone(), askpass, cx)
|
||||
})?;
|
||||
|
||||
let remote_message = pull.await?;
|
||||
drop(guard);
|
||||
|
||||
let action = RemoteAction::Pull(remote);
|
||||
this.update(cx, |this, cx| match remote_message {
|
||||
@ -1954,10 +1920,6 @@ impl GitPanel {
|
||||
this.askpass_delegate(format!("git push {}", remote.name), window, cx)
|
||||
})?;
|
||||
|
||||
let guard = this
|
||||
.update(cx, |this, _| this.start_remote_operation())
|
||||
.ok();
|
||||
|
||||
let push = repo.update(cx, |repo, cx| {
|
||||
repo.push(
|
||||
branch.name.clone(),
|
||||
@ -1969,7 +1931,6 @@ impl GitPanel {
|
||||
})?;
|
||||
|
||||
let remote_output = push.await?;
|
||||
drop(guard);
|
||||
|
||||
let action = RemoteAction::Push(branch.name, remote);
|
||||
this.update(cx, |this, cx| match remote_output {
|
||||
@ -2590,20 +2551,6 @@ impl GitPanel {
|
||||
workspace.add_item_to_center(Box::new(editor), window, cx);
|
||||
}
|
||||
|
||||
pub fn render_spinner(&self) -> Option<impl IntoElement> {
|
||||
(!self.pending_remote_operations.borrow().is_empty()).then(|| {
|
||||
Icon::new(IconName::ArrowCircle)
|
||||
.size(IconSize::XSmall)
|
||||
.color(Color::Info)
|
||||
.with_animation(
|
||||
"arrow-circle",
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
|
||||
)
|
||||
.into_any_element()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn can_commit(&self) -> bool {
|
||||
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
|
||||
}
|
||||
@ -2832,12 +2779,10 @@ impl GitPanel {
|
||||
if !self.can_push_and_pull(cx) {
|
||||
return None;
|
||||
}
|
||||
let spinner = self.render_spinner();
|
||||
Some(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.flex_shrink_0()
|
||||
.children(spinner)
|
||||
.when_some(branch, |this, branch| {
|
||||
let focus_handle = Some(self.focus_handle(cx));
|
||||
|
||||
@ -4129,17 +4074,17 @@ impl RenderOnce for PanelRepoFooter {
|
||||
.truncate(true)
|
||||
.tooltip(Tooltip::for_action_title(
|
||||
"Switch Branch",
|
||||
&zed_actions::git::Branch,
|
||||
&zed_actions::git::Switch,
|
||||
))
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx);
|
||||
window.dispatch_action(zed_actions::git::Switch.boxed_clone(), cx);
|
||||
});
|
||||
|
||||
let branch_selector = PopoverMenu::new("popover-button")
|
||||
.menu(move |window, cx| Some(branch_picker::popover(repo.clone(), window, cx)))
|
||||
.trigger_with_tooltip(
|
||||
branch_selector_button,
|
||||
Tooltip::for_action_title("Switch Branch", &zed_actions::git::Branch),
|
||||
Tooltip::for_action_title("Switch Branch", &zed_actions::git::Switch),
|
||||
)
|
||||
.anchor(Corner::BottomLeft)
|
||||
.offset(gpui::Point {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4135,20 +4135,22 @@ impl Project {
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
version: Option<clock::Global>,
|
||||
cx: &App,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<Option<Blame>>> {
|
||||
self.git_store.read(cx).blame_buffer(buffer, version, cx)
|
||||
self.git_store.update(cx, |git_store, cx| {
|
||||
git_store.blame_buffer(buffer, version, cx)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_permalink_to_line(
|
||||
&self,
|
||||
buffer: &Entity<Buffer>,
|
||||
selection: Range<u32>,
|
||||
cx: &App,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<url::Url>> {
|
||||
self.git_store
|
||||
.read(cx)
|
||||
.get_permalink_to_line(buffer, selection, cx)
|
||||
self.git_store.update(cx, |git_store, cx| {
|
||||
git_store.get_permalink_to_line(buffer, selection, cx)
|
||||
})
|
||||
}
|
||||
|
||||
// RPC message handlers
|
||||
|
@ -1366,10 +1366,14 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
|
||||
|
||||
assert_eq!(&remote_branches, &branches_set);
|
||||
|
||||
cx.update(|cx| repository.read(cx).change_branch(new_branch.to_string()))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
cx.update(|cx| {
|
||||
repository.update(cx, |repository, _cx| {
|
||||
repository.change_branch(new_branch.to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
@ -1394,18 +1398,18 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
|
||||
|
||||
// Also try creating a new branch
|
||||
cx.update(|cx| {
|
||||
repository
|
||||
.read(cx)
|
||||
.create_branch("totally-new-branch".to_string())
|
||||
repository.update(cx, |repo, _cx| {
|
||||
repo.create_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
cx.update(|cx| {
|
||||
repository
|
||||
.read(cx)
|
||||
.change_branch("totally-new-branch".to_string())
|
||||
repository.update(cx, |repo, _cx| {
|
||||
repo.change_branch("totally-new-branch".to_string())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
|
Loading…
x
Reference in New Issue
Block a user