wip
This commit is contained in:
@@ -325,8 +325,6 @@ pub struct CommitDiffView {
|
|||||||
error: Option<SharedString>,
|
error: Option<SharedString>,
|
||||||
/// Changed-files explorer and per-file diff, also used by the new PR panel's compare view.
|
/// Changed-files explorer and per-file diff, also used by the new PR panel's compare view.
|
||||||
pane: Entity<DiffPane>,
|
pane: Entity<DiffPane>,
|
||||||
/// In-flight tasks, pruned on every push.
|
|
||||||
tasks: Vec<gpui::Task<Result<(), anyhow::Error>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommitDiffView {
|
impl CommitDiffView {
|
||||||
@@ -358,7 +356,6 @@ impl CommitDiffView {
|
|||||||
loading: true,
|
loading: true,
|
||||||
error: None,
|
error: None,
|
||||||
pane,
|
pane,
|
||||||
tasks: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,42 +368,43 @@ impl CommitDiffView {
|
|||||||
let worktree = self.worktree.clone();
|
let worktree = self.worktree.clone();
|
||||||
let id = self.commit.id.clone();
|
let id = self.commit.id.clone();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
let commit = cx
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
.background_spawn({
|
let commit = cx
|
||||||
let worktree = worktree.clone();
|
.background_spawn({
|
||||||
let id = id.clone();
|
let worktree = worktree.clone();
|
||||||
async move { signed_git::worktree_commit(&worktree, &id) }
|
let id = id.clone();
|
||||||
})
|
async move { signed_git::worktree_commit(&worktree, &id) }
|
||||||
.await;
|
})
|
||||||
let diff = cx
|
.await;
|
||||||
.background_spawn({
|
let diff = cx
|
||||||
let worktree = worktree.clone();
|
.background_spawn({
|
||||||
let id = id.clone();
|
let worktree = worktree.clone();
|
||||||
async move { signed_git::worktree_commit_diff(&worktree, &id) }
|
let id = id.clone();
|
||||||
})
|
async move { signed_git::worktree_commit_diff(&worktree, &id) }
|
||||||
.await;
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
this.update_in(cx, |this, _window, cx| {
|
this.update_in(cx, |this, _window, cx| {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if let Ok(Some(commit)) = commit {
|
if let Ok(Some(commit)) = commit {
|
||||||
this.commit = commit;
|
this.commit = commit;
|
||||||
}
|
|
||||||
match diff {
|
|
||||||
Ok(diff) => {
|
|
||||||
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
|
||||||
}
|
}
|
||||||
Err(error) => {
|
match diff {
|
||||||
this.error = Some(error.to_string().into());
|
Ok(diff) => {
|
||||||
|
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
this.error = Some(error.to_string().into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
cx.notify();
|
||||||
cx.notify();
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Header with the commit id, summary, author/time and overall change stats.
|
/// Header with the commit id, summary, author/time and overall change stats.
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use gix::Repository;
|
|||||||
use gpui::prelude::*;
|
use gpui::prelude::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, Anchor, AnyElement, App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
Action, Anchor, AnyElement, App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
||||||
Focusable, PathPromptOptions, Pixels, Render, SharedString, Size, Subscription, Task,
|
Focusable, PathPromptOptions, Pixels, Render, SharedString, Size, Subscription, WeakEntity,
|
||||||
WeakEntity, Window, div, px, relative, size, transparent_white,
|
Window, div, px, relative, size, transparent_white,
|
||||||
};
|
};
|
||||||
use gpui_base::{Button as BaseButton, Disableable, Popover};
|
use gpui_base::{Button as BaseButton, Disableable, Popover};
|
||||||
use gpui_component::alert::Alert;
|
use gpui_component::alert::Alert;
|
||||||
@@ -175,9 +175,6 @@ pub struct RepoDetailView {
|
|||||||
/// Bumped on every branch/tag switch.
|
/// Bumped on every branch/tag switch.
|
||||||
/// In-flight loads with an older generation are discarded when they complete.
|
/// In-flight loads with an older generation are discarded when they complete.
|
||||||
ref_generation: u64,
|
ref_generation: u64,
|
||||||
/// In-flight tasks, finished tasks are pruned on every push.
|
|
||||||
/// The vec stays bounded by the number of concurrent loads.
|
|
||||||
tasks: Vec<Task<Result<(), Error>>>,
|
|
||||||
/// Subscriptions keeping the selectors' confirm events alive.
|
/// Subscriptions keeping the selectors' confirm events alive.
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
/// `(path, branch)` ready-suggestions dismissed by the user, per panel.
|
/// `(path, branch)` ready-suggestions dismissed by the user, per panel.
|
||||||
@@ -346,7 +343,6 @@ impl RepoDetailView {
|
|||||||
push_statuses: Vec::new(),
|
push_statuses: Vec::new(),
|
||||||
pending_upstream: None,
|
pending_upstream: None,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
tasks: Vec::new(),
|
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +359,7 @@ impl RepoDetailView {
|
|||||||
// Local repositories live on disk at their scan path.
|
// Local repositories live on disk at their scan path.
|
||||||
// No clone step or network refresh applies here.
|
// No clone step or network refresh applies here.
|
||||||
if let Some(local_path) = self.local_path.clone() {
|
if let Some(local_path) = self.local_path.clone() {
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
let data = cx
|
let data = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
let repo = gix::open(&local_path)?;
|
let repo = gix::open(&local_path)?;
|
||||||
@@ -383,7 +379,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -411,7 +407,7 @@ impl RepoDetailView {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
let disk = disk.await;
|
let disk = disk.await;
|
||||||
let had_clone = matches!(&disk, Ok(Some(_)));
|
let had_clone = matches!(&disk, Ok(Some(_)));
|
||||||
|
|
||||||
@@ -531,7 +527,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the loaded repository data.
|
/// Apply the loaded repository data.
|
||||||
@@ -619,7 +615,7 @@ impl RepoDetailView {
|
|||||||
prompt: Some("Clone".into()),
|
prompt: Some("Clone".into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
// `Ok(Ok(Some(paths)))` means the user picked a folder.
|
// `Ok(Ok(Some(paths)))` means the user picked a folder.
|
||||||
// A cancel or picker failure resolves to anything else.
|
// A cancel or picker failure resolves to anything else.
|
||||||
let picked = match prompt.await {
|
let picked = match prompt.await {
|
||||||
@@ -649,7 +645,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preview the file at `path`, relative to the worktree root.
|
/// Preview the file at `path`, relative to the worktree root.
|
||||||
@@ -703,7 +699,7 @@ impl RepoDetailView {
|
|||||||
self.load_commit(&path, cx);
|
self.load_commit(&path, cx);
|
||||||
let generation = self.ref_generation;
|
let generation = self.ref_generation;
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
let path_for_read = path.clone();
|
let path_for_read = path.clone();
|
||||||
let content = cx
|
let content = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
@@ -772,7 +768,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue `path` for the per-file commit query.
|
/// Queue `path` for the per-file commit query.
|
||||||
@@ -804,7 +800,7 @@ impl RepoDetailView {
|
|||||||
let paths = std::mem::take(&mut self.pending_commits);
|
let paths = std::mem::take(&mut self.pending_commits);
|
||||||
let generation = self.ref_generation;
|
let generation = self.ref_generation;
|
||||||
|
|
||||||
let task = cx.spawn(async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
|
||||||
let rels: Vec<PathBuf> = paths.iter().map(PathBuf::from).collect();
|
let rels: Vec<PathBuf> = paths.iter().map(PathBuf::from).collect();
|
||||||
let result = cx
|
let result = cx
|
||||||
.background_spawn(
|
.background_spawn(
|
||||||
@@ -834,7 +830,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walk all commits reachable from HEAD on a background task.
|
/// Walk all commits reachable from HEAD on a background task.
|
||||||
@@ -852,7 +848,7 @@ impl RepoDetailView {
|
|||||||
self.loading_all_commits = true;
|
self.loading_all_commits = true;
|
||||||
let generation = self.ref_generation;
|
let generation = self.ref_generation;
|
||||||
|
|
||||||
let task = cx.spawn(async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
|
||||||
let result = cx
|
let result = cx
|
||||||
.background_spawn(async move { signed_git::worktree_all_commits(&worktree) })
|
.background_spawn(async move { signed_git::worktree_all_commits(&worktree) })
|
||||||
.await;
|
.await;
|
||||||
@@ -876,7 +872,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a new panel showing the diff of `commit_id`.
|
/// Open a new panel showing the diff of `commit_id`.
|
||||||
@@ -909,8 +905,9 @@ impl RepoDetailView {
|
|||||||
self.error = None;
|
self.error = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
self.tasks
|
store
|
||||||
.push(store.update(cx, |store, cx| store.push_repository(cx)));
|
.update(cx, |store, cx| store.push_repository(cx))
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push the unpushed commits of the local checkout at `path`.
|
/// Push the unpushed commits of the local checkout at `path`.
|
||||||
@@ -931,7 +928,7 @@ impl RepoDetailView {
|
|||||||
self.error = None;
|
self.error = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
// The store owns the push, its busy flag and error reporting.
|
// The store owns the push, its busy flag and error reporting.
|
||||||
let push = this.update_in(cx, |_this, _window, cx| {
|
let push = this.update_in(cx, |_this, _window, cx| {
|
||||||
store.update(cx, |store, cx| store.push_checkout(path.clone(), cx))
|
store.update(cx, |store, cx| store.push_checkout(path.clone(), cx))
|
||||||
@@ -948,7 +945,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete the repository from nostr, announcement, state and activity.
|
/// Delete the repository from nostr, announcement, state and activity.
|
||||||
@@ -956,8 +953,9 @@ impl RepoDetailView {
|
|||||||
let Some(store) = self.store.clone() else {
|
let Some(store) = self.store.clone() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.tasks
|
store
|
||||||
.push(store.update(cx, |store, cx| store.delete_repository(cx)));
|
.update(cx, |store, cx| store.delete_repository(cx))
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the issues list panel in the dock area.
|
/// Open the issues list panel in the dock area.
|
||||||
@@ -1025,7 +1023,7 @@ impl RepoDetailView {
|
|||||||
});
|
});
|
||||||
self.pending_upstream = Some(addr);
|
self.pending_upstream = Some(addr);
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
for _ in 0..60 {
|
for _ in 0..60 {
|
||||||
cx.background_executor()
|
cx.background_executor()
|
||||||
.timer(Duration::from_millis(250))
|
.timer(Duration::from_millis(250))
|
||||||
@@ -1060,7 +1058,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check out `name`, a branch or tag picked in the header.
|
/// Check out `name`, a branch or tag picked in the header.
|
||||||
@@ -1101,7 +1099,7 @@ impl RepoDetailView {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let checkout_name = name.clone();
|
let checkout_name = name.clone();
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
|
||||||
let result = cx
|
let result = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
match kind {
|
match kind {
|
||||||
@@ -1131,7 +1129,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore a selector to `previous`, or clear it after a failed switch.
|
/// Restore a selector to `previous`, or clear it after a failed switch.
|
||||||
@@ -1157,7 +1155,7 @@ impl RepoDetailView {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let task = cx.spawn(async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
|
||||||
let result = cx
|
let result = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
let snapshot = signed_git::worktree_snapshot(&worktree)?;
|
let snapshot = signed_git::worktree_snapshot(&worktree)?;
|
||||||
@@ -1218,7 +1216,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh the file explorer, previews and commit list after the mirror
|
/// Refresh the file explorer, previews and commit list after the mirror
|
||||||
@@ -1233,7 +1231,7 @@ impl RepoDetailView {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let task = cx.spawn(async move |this, cx| {
|
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
|
||||||
let result = cx
|
let result = cx
|
||||||
.background_spawn(async move {
|
.background_spawn(async move {
|
||||||
let snapshot = signed_git::worktree_snapshot(&worktree)?;
|
let snapshot = signed_git::worktree_snapshot(&worktree)?;
|
||||||
@@ -1314,7 +1312,7 @@ impl RepoDetailView {
|
|||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop the cached preview, editor and commit state of `path`.
|
/// Drop the cached preview, editor and commit state of `path`.
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ use dock::{BasePanel, DockArea, Panel, PanelEvent, add_center_panel, panel_handl
|
|||||||
use gpui::prelude::*;
|
use gpui::prelude::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, PathPromptOptions,
|
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, PathPromptOptions,
|
||||||
Pixels, Render, SharedString, Size, Subscription, Task, WeakEntity, Window, div, px, relative,
|
Pixels, Render, SharedString, Size, Subscription, WeakEntity, Window, div, px, relative, size,
|
||||||
size,
|
|
||||||
};
|
};
|
||||||
use gpui_base::{Button as BaseButton, StyledExt};
|
use gpui_base::{Button as BaseButton, StyledExt};
|
||||||
use gpui_component::button::{Button, ButtonVariants};
|
use gpui_component::button::{Button, ButtonVariants};
|
||||||
@@ -79,7 +78,6 @@ pub struct NewPullRequestView {
|
|||||||
scroll_handle: VirtualListScrollHandle,
|
scroll_handle: VirtualListScrollHandle,
|
||||||
item_sizes: Rc<Vec<Size<Pixels>>>,
|
item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
tasks: Vec<Task<Result<(), anyhow::Error>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A fork-backed compare.
|
/// A fork-backed compare.
|
||||||
@@ -363,7 +361,6 @@ impl NewPullRequestView {
|
|||||||
scroll_handle: VirtualListScrollHandle::new(),
|
scroll_handle: VirtualListScrollHandle::new(),
|
||||||
item_sizes: Rc::new(Vec::new()),
|
item_sizes: Rc::new(Vec::new()),
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
tasks: Vec::new(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prefill with the store's freshest associated checkout, no folder dialog.
|
// Prefill with the store's freshest associated checkout, no folder dialog.
|
||||||
@@ -426,25 +423,26 @@ impl NewPullRequestView {
|
|||||||
prompt: Some("Choose local checkout".into()),
|
prompt: Some("Choose local checkout".into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
// `Ok(Ok(Some(paths)))` means the user picked a folder.
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
// A cancel or picker failure resolves to anything else.
|
// `Ok(Ok(Some(paths)))` means the user picked a folder.
|
||||||
let picked = match prompt.await {
|
// A cancel or picker failure resolves to anything else.
|
||||||
Ok(Ok(Some(mut paths))) => paths.pop(),
|
let picked = match prompt.await {
|
||||||
_ => None,
|
Ok(Ok(Some(mut paths))) => paths.pop(),
|
||||||
};
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let Some(path) = picked else {
|
let Some(path) = picked else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
this.apply_folder_path(path, window, cx);
|
this.apply_folder_path(path, window, cx);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply `path` as the local checkout, no picker.
|
/// Apply `path` as the local checkout, no picker.
|
||||||
@@ -453,28 +451,29 @@ impl NewPullRequestView {
|
|||||||
fn apply_folder_path(&mut self, path: PathBuf, window: &mut Window, cx: &mut Context<Self>) {
|
fn apply_folder_path(&mut self, path: PathBuf, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let path = path.to_string_lossy().to_string();
|
let path = path.to_string_lossy().to_string();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
// Branches and the current branch are read off the UI thread.
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let info = cx
|
// Branches and the current branch are read off the UI thread.
|
||||||
.background_spawn({
|
let info = cx
|
||||||
let path = path.clone();
|
.background_spawn({
|
||||||
async move {
|
let path = path.clone();
|
||||||
let repo = gix::open(Path::new(&path)).ok()?;
|
async move {
|
||||||
let branches =
|
let repo = gix::open(Path::new(&path)).ok()?;
|
||||||
signed_git::worktree_branches(Path::new(&path)).unwrap_or_default();
|
let branches =
|
||||||
let current = signed_git::current_branch(&repo).ok().flatten();
|
signed_git::worktree_branches(Path::new(&path)).unwrap_or_default();
|
||||||
Some((branches, current))
|
let current = signed_git::current_branch(&repo).ok().flatten();
|
||||||
}
|
Some((branches, current))
|
||||||
})
|
}
|
||||||
.await;
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
this.apply_checkout(path, info, window, cx);
|
this.apply_checkout(path, info, window, cx);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a picked checkout, filling the selectors and loading the compare.
|
/// Apply a picked checkout, filling the selectors and loading the compare.
|
||||||
@@ -615,88 +614,89 @@ impl NewPullRequestView {
|
|||||||
self.error = None;
|
self.error = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
// The fork and base must share history for a merge-base to exist.
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
// The target's mirror is the object store both sides land in.
|
// The fork and base must share history for a merge-base to exist.
|
||||||
// `ensure_clone` fetches `origin` when the mirror already exists.
|
// The target's mirror is the object store both sides land in.
|
||||||
let result = cx
|
// `ensure_clone` fetches `origin` when the mirror already exists.
|
||||||
.background_spawn({
|
let result = cx
|
||||||
let cache = cache.clone();
|
.background_spawn({
|
||||||
let base = base.clone();
|
let cache = cache.clone();
|
||||||
let base_clone_urls = base_clone_urls.clone();
|
let base = base.clone();
|
||||||
let namespace = namespace.clone();
|
let base_clone_urls = base_clone_urls.clone();
|
||||||
let clone_urls = clone_urls.clone();
|
let namespace = namespace.clone();
|
||||||
let mirror_path = mirror_path.clone();
|
let clone_urls = clone_urls.clone();
|
||||||
async move {
|
let mirror_path = mirror_path.clone();
|
||||||
// The fork and base must share history for a merge-base to exist.
|
async move {
|
||||||
// The target's mirror is the object store both sides land in.
|
// The fork and base must share history for a merge-base to exist.
|
||||||
// `ensure_clone` fetches `origin` when the mirror already exists.
|
// The target's mirror is the object store both sides land in.
|
||||||
cache.ensure_clone(&base, &base_clone_urls)?;
|
// `ensure_clone` fetches `origin` when the mirror already exists.
|
||||||
|
cache.ensure_clone(&base, &base_clone_urls)?;
|
||||||
|
|
||||||
// Prune stale imports of any fork.
|
// Prune stale imports of any fork.
|
||||||
// Then import this fork's heads under its namespace.
|
// Then import this fork's heads under its namespace.
|
||||||
delete_refs_with_prefix(&mirror_path, "refs/fork")?;
|
delete_refs_with_prefix(&mirror_path, "refs/fork")?;
|
||||||
|
|
||||||
fetch_repo_refs(
|
fetch_repo_refs(
|
||||||
&mirror_path,
|
&mirror_path,
|
||||||
&clone_urls,
|
&clone_urls,
|
||||||
&format!("+refs/heads/*:refs/fork/{namespace}/*"),
|
&format!("+refs/heads/*:refs/fork/{namespace}/*"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Both branch lists are short names, sorted like the checkout's.
|
// Both branch lists are short names, sorted like the checkout's.
|
||||||
let strip = |refs: Vec<String>, prefix: &str| {
|
let strip = |refs: Vec<String>, prefix: &str| {
|
||||||
let mut names: Vec<String> = refs
|
let mut names: Vec<String> = refs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|name| {
|
.filter_map(|name| {
|
||||||
name.strip_prefix(prefix)
|
name.strip_prefix(prefix)
|
||||||
.map(|rest| rest.trim_start_matches('/').to_owned())
|
.map(|rest| rest.trim_start_matches('/').to_owned())
|
||||||
})
|
})
|
||||||
.filter(|name| !name.is_empty())
|
.filter(|name| !name.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
names.sort();
|
names.sort();
|
||||||
names
|
names
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_branches = strip(
|
let base_branches = strip(
|
||||||
refs_with_prefix(&mirror_path, "refs/remotes/origin")?,
|
refs_with_prefix(&mirror_path, "refs/remotes/origin")?,
|
||||||
"refs/remotes/origin",
|
"refs/remotes/origin",
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_branches = strip(
|
let compare_branches = strip(
|
||||||
refs_with_prefix(&mirror_path, &format!("refs/fork/{namespace}"))?,
|
refs_with_prefix(&mirror_path, &format!("refs/fork/{namespace}"))?,
|
||||||
&format!("refs/fork/{namespace}"),
|
&format!("refs/fork/{namespace}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>((base_branches, compare_branches))
|
Ok::<_, anyhow::Error>((base_branches, compare_branches))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
this.update_in(cx, |this, window, cx| {
|
||||||
|
// A source switch mid-flight discards the stale result.
|
||||||
|
// E.g. the user picked a folder while the fork was fetching.
|
||||||
|
let applied = this.fork.as_ref().map(|fork| fork.announcement.addr());
|
||||||
|
if applied != expected_fork {
|
||||||
|
this.loading = false;
|
||||||
|
cx.notify();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.apply_fork(
|
||||||
// A source switch mid-flight discards the stale result.
|
announcement,
|
||||||
// E.g. the user picked a folder while the fork was fetching.
|
mirror_path,
|
||||||
let applied = this.fork.as_ref().map(|fork| fork.announcement.addr());
|
namespace,
|
||||||
if applied != expected_fork {
|
result,
|
||||||
this.loading = false;
|
keep_base,
|
||||||
cx.notify();
|
keep_compare,
|
||||||
return;
|
window,
|
||||||
}
|
cx,
|
||||||
|
);
|
||||||
|
})?;
|
||||||
|
|
||||||
this.apply_fork(
|
Ok(())
|
||||||
announcement,
|
});
|
||||||
mirror_path,
|
task.detach();
|
||||||
namespace,
|
|
||||||
result,
|
|
||||||
keep_base,
|
|
||||||
keep_compare,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
self.tasks.push(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply an imported fork, filling the selectors and loading the compare.
|
/// Apply an imported fork, filling the selectors and loading the compare.
|
||||||
@@ -834,66 +834,68 @@ impl NewPullRequestView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
let result = cx
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
.background_spawn({
|
let result = cx
|
||||||
let repo_path = repo_path.clone();
|
.background_spawn({
|
||||||
let base = base.clone();
|
let repo_path = repo_path.clone();
|
||||||
let compare = compare.clone();
|
let base = base.clone();
|
||||||
let base_name = base_name.clone();
|
let compare = compare.clone();
|
||||||
let compare_name = compare_name.clone();
|
let base_name = base_name.clone();
|
||||||
async move {
|
let compare_name = compare_name.clone();
|
||||||
let merge_base = merge_base(Path::new(&repo_path), &base, &compare)?
|
async move {
|
||||||
.ok_or_else(|| {
|
let merge_base = merge_base(Path::new(&repo_path), &base, &compare)?
|
||||||
anyhow::anyhow!(
|
.ok_or_else(|| {
|
||||||
"{base_name} and {compare_name} share no common ancestor"
|
anyhow::anyhow!(
|
||||||
)
|
"{base_name} and {compare_name} share no common ancestor"
|
||||||
})?;
|
)
|
||||||
let commits = worktree_commit_range_commits(
|
})?;
|
||||||
Path::new(&repo_path),
|
let commits = worktree_commit_range_commits(
|
||||||
&merge_base,
|
Path::new(&repo_path),
|
||||||
&compare,
|
&merge_base,
|
||||||
)?;
|
&compare,
|
||||||
let diff = worktree_commit_range_diff(
|
)?;
|
||||||
Path::new(&repo_path),
|
let diff = worktree_commit_range_diff(
|
||||||
&merge_base,
|
Path::new(&repo_path),
|
||||||
&compare,
|
&merge_base,
|
||||||
)?;
|
&compare,
|
||||||
Ok::<_, anyhow::Error>((merge_base, commits, diff))
|
)?;
|
||||||
|
Ok::<_, anyhow::Error>((merge_base, commits, diff))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
this.update_in(cx, |this, _window, cx| {
|
||||||
|
// A stale result, branches changed mid-flight, must not clobber a newer compare.
|
||||||
|
if generation != this.compare_generation {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})
|
this.loading = false;
|
||||||
.await;
|
|
||||||
|
|
||||||
this.update_in(cx, |this, _window, cx| {
|
match result {
|
||||||
// A stale result, branches changed mid-flight, must not clobber a newer compare.
|
Ok((merge_base, commits, diff)) => {
|
||||||
if generation != this.compare_generation {
|
this.merge_base = Some(merge_base);
|
||||||
return;
|
let count = commits.len();
|
||||||
}
|
this.item_sizes =
|
||||||
this.loading = false;
|
Rc::new(vec![size(px(0.), px(COMMIT_ROW_HEIGHT)); count]);
|
||||||
|
this.commits = Some(commits);
|
||||||
match result {
|
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
||||||
Ok((merge_base, commits, diff)) => {
|
}
|
||||||
this.merge_base = Some(merge_base);
|
Err(error) => {
|
||||||
let count = commits.len();
|
this.merge_base = None;
|
||||||
this.item_sizes = Rc::new(vec![size(px(0.), px(COMMIT_ROW_HEIGHT)); count]);
|
this.commits = None;
|
||||||
this.commits = Some(commits);
|
this.pane.update(cx, |pane, cx| pane.clear(cx));
|
||||||
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
this.error = Some(error.to_string().into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
|
||||||
this.merge_base = None;
|
|
||||||
this.commits = None;
|
|
||||||
this.pane.update(cx, |pane, cx| pane.clear(cx));
|
|
||||||
this.error = Some(error.to_string().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Publish the pull request.
|
/// Publish the pull request.
|
||||||
@@ -927,76 +929,78 @@ impl NewPullRequestView {
|
|||||||
self.error = None;
|
self.error = None;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
// Regenerate the series at submit time.
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
// The published patch covers the current tip of the compare branch.
|
// Regenerate the series at submit time.
|
||||||
let patch = cx
|
// The published patch covers the current tip of the compare branch.
|
||||||
.background_spawn({
|
let patch = cx
|
||||||
let repo_path = repo_path.clone();
|
.background_spawn({
|
||||||
let merge_base = merge_base.clone();
|
let repo_path = repo_path.clone();
|
||||||
let compare_ref = compare_ref.clone();
|
let merge_base = merge_base.clone();
|
||||||
async move {
|
let compare_ref = compare_ref.clone();
|
||||||
format_patch_between(Path::new(&repo_path), &merge_base, &compare_ref)
|
async move {
|
||||||
}
|
format_patch_between(Path::new(&repo_path), &merge_base, &compare_ref)
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let patch = match patch {
|
|
||||||
Ok(patch) if !patch.is_empty() => patch,
|
|
||||||
Ok(_) => {
|
|
||||||
this.update_in(cx, |this, _window, cx| {
|
|
||||||
this.submitting = false;
|
|
||||||
this.error = Some("No commits between the branches to propose".into());
|
|
||||||
cx.notify();
|
|
||||||
})?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
this.update_in(cx, |this, _window, cx| {
|
|
||||||
this.submitting = false;
|
|
||||||
this.error = Some(format!("Failed to generate the patch: {error}").into());
|
|
||||||
cx.notify();
|
|
||||||
})?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.update_in(cx, |this, window, cx| {
|
|
||||||
this.submitting = false;
|
|
||||||
|
|
||||||
store.update(cx, |store, cx| {
|
|
||||||
store.open_pull_request(
|
|
||||||
(!subject.is_empty()).then_some(subject),
|
|
||||||
description,
|
|
||||||
Some(branch_name),
|
|
||||||
patch,
|
|
||||||
false,
|
|
||||||
Some(merge_base),
|
|
||||||
Some(repo_path),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close the panel once the publish is underway.
|
|
||||||
cx.defer_in(window, {
|
|
||||||
let dock_area = dock_area.clone();
|
|
||||||
let entity = entity.clone();
|
|
||||||
move |_, window, cx| {
|
|
||||||
if let Some(dock_area) = dock_area.upgrade() {
|
|
||||||
dock_area.update(cx, |dock, cx| {
|
|
||||||
dock.remove_panel(entity, window, cx);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let patch = match patch {
|
||||||
|
Ok(patch) if !patch.is_empty() => patch,
|
||||||
|
Ok(_) => {
|
||||||
|
this.update_in(cx, |this, _window, cx| {
|
||||||
|
this.submitting = false;
|
||||||
|
this.error = Some("No commits between the branches to propose".into());
|
||||||
|
cx.notify();
|
||||||
|
})?;
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
});
|
Err(error) => {
|
||||||
|
this.update_in(cx, |this, _window, cx| {
|
||||||
|
this.submitting = false;
|
||||||
|
this.error =
|
||||||
|
Some(format!("Failed to generate the patch: {error}").into());
|
||||||
|
cx.notify();
|
||||||
|
})?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
cx.notify();
|
this.update_in(cx, |this, window, cx| {
|
||||||
})?;
|
this.submitting = false;
|
||||||
|
|
||||||
Ok(())
|
store.update(cx, |store, cx| {
|
||||||
});
|
store.open_pull_request(
|
||||||
|
(!subject.is_empty()).then_some(subject),
|
||||||
|
description,
|
||||||
|
Some(branch_name),
|
||||||
|
patch,
|
||||||
|
false,
|
||||||
|
Some(merge_base),
|
||||||
|
Some(repo_path),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
self.tasks.push(task);
|
// Close the panel once the publish is underway.
|
||||||
|
cx.defer_in(window, {
|
||||||
|
let dock_area = dock_area.clone();
|
||||||
|
let entity = entity.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
if let Some(dock_area) = dock_area.upgrade() {
|
||||||
|
dock_area.update(cx, |dock, cx| {
|
||||||
|
dock.remove_panel(entity, window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
task.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the diff of `commit_id`, from the Commits tab, in a new panel.
|
/// Open the diff of `commit_id`, from the Commits tab, in a new panel.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use dock::{BasePanel, DockArea, DockPlacement, Panel, PanelEvent, panel_handle};
|
|||||||
use gpui::prelude::*;
|
use gpui::prelude::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render,
|
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render,
|
||||||
SharedString, Size, Task, WeakEntity, Window, div, px, relative, size,
|
SharedString, Size, WeakEntity, Window, div, px, relative, size,
|
||||||
};
|
};
|
||||||
use gpui_component::button::{Button, ButtonVariants};
|
use gpui_component::button::{Button, ButtonVariants};
|
||||||
use gpui_component::clipboard::Clipboard;
|
use gpui_component::clipboard::Clipboard;
|
||||||
@@ -66,8 +66,6 @@ pub struct PullRequestDetailView {
|
|||||||
commit_item_sizes: Rc<Vec<Size<Pixels>>>,
|
commit_item_sizes: Rc<Vec<Size<Pixels>>>,
|
||||||
/// Virtual list state of the commits tab.
|
/// Virtual list state of the commits tab.
|
||||||
commit_scroll_handle: VirtualListScrollHandle,
|
commit_scroll_handle: VirtualListScrollHandle,
|
||||||
/// In-flight tasks, finished tasks are pruned on every push.
|
|
||||||
tasks: Vec<Task<Result<(), anyhow::Error>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PullRequestDetailView {
|
impl PullRequestDetailView {
|
||||||
@@ -106,7 +104,6 @@ impl PullRequestDetailView {
|
|||||||
pane,
|
pane,
|
||||||
commit_item_sizes: Rc::new(Vec::new()),
|
commit_item_sizes: Rc::new(Vec::new()),
|
||||||
commit_scroll_handle: VirtualListScrollHandle::new(),
|
commit_scroll_handle: VirtualListScrollHandle::new(),
|
||||||
tasks: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,101 +159,103 @@ impl PullRequestDetailView {
|
|||||||
|
|
||||||
self.description = description.into();
|
self.description = description.into();
|
||||||
|
|
||||||
let task = cx.spawn_in(window, async move |this, cx| {
|
let task: gpui::Task<Result<(), anyhow::Error>> =
|
||||||
let nostr_diff = cx
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
.background_spawn({
|
let nostr_diff = cx
|
||||||
let patch = patch.clone();
|
.background_spawn({
|
||||||
async move { patch_diffs(&patch) }
|
let patch = patch.clone();
|
||||||
})
|
async move { patch_diffs(&patch) }
|
||||||
.await;
|
|
||||||
|
|
||||||
let nostr_commits = cx
|
|
||||||
.background_spawn({
|
|
||||||
let patch = patch.clone();
|
|
||||||
async move { patch_commits(&patch) }
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// PRs without patch events, e.g. published by ngit, carry their changes in git.
|
|
||||||
// Fetch the clone and diff the `merge-base..tip` range.
|
|
||||||
let use_nostr = match &nostr_diff {
|
|
||||||
Ok(diff) => has_patch_link || !diff.files.is_empty(),
|
|
||||||
Err(_) => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let git = if use_nostr {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let cache = cache.clone();
|
|
||||||
let addr = addr.clone();
|
|
||||||
let clone_urls = clone_urls.clone();
|
|
||||||
let base = merge_base.clone();
|
|
||||||
let tip = current_commit.clone();
|
|
||||||
|
|
||||||
Some(
|
|
||||||
cx.background_spawn(async move {
|
|
||||||
let repo = cache.ensure_clone(&addr, &clone_urls)?;
|
|
||||||
|
|
||||||
let workdir = repo
|
|
||||||
.workdir()
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("repository has no worktree"))?
|
|
||||||
.to_path_buf();
|
|
||||||
|
|
||||||
let tip =
|
|
||||||
tip.ok_or_else(|| anyhow::anyhow!("pull request has no tip commit"))?;
|
|
||||||
|
|
||||||
let base = match base {
|
|
||||||
Some(base) => base,
|
|
||||||
// No `merge-base` tag. Use the merge base of the tip and the default branch.
|
|
||||||
None => {
|
|
||||||
let head = repo
|
|
||||||
.head_id()
|
|
||||||
.map_err(|_| anyhow::anyhow!("repository has no HEAD"))?;
|
|
||||||
let tip_id = repo.rev_parse_single(tip.as_bytes())?;
|
|
||||||
repo.merge_base(tip_id, head)?.to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let diff = signed_git::worktree_commit_range_diff(&workdir, &base, &tip)?;
|
|
||||||
let commits =
|
|
||||||
signed_git::worktree_commit_range_commits(&workdir, &base, &tip)?;
|
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>((diff, commits, workdir))
|
|
||||||
})
|
})
|
||||||
.await,
|
.await;
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (diff, commits, worktree) = match git {
|
let nostr_commits = cx
|
||||||
Some(Ok((diff, commits, worktree))) => (Ok(diff), commits, Some(worktree)),
|
.background_spawn({
|
||||||
Some(Err(error)) => (Err(error), Vec::new(), None),
|
let patch = patch.clone();
|
||||||
None => (nostr_diff, nostr_commits, None),
|
async move { patch_commits(&patch) }
|
||||||
};
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
this.update_in(cx, |this, _window, cx| {
|
// PRs without patch events, e.g. published by ngit, carry their changes in git.
|
||||||
this.loading = false;
|
// Fetch the clone and diff the `merge-base..tip` range.
|
||||||
this.worktree = worktree;
|
let use_nostr = match &nostr_diff {
|
||||||
this.current_commit = current_commit.map(SharedString::from);
|
Ok(diff) => has_patch_link || !diff.files.is_empty(),
|
||||||
this.commit_item_sizes = Rc::new(vec![size(px(0.), px(ROW_HEIGHT)); commits.len()]);
|
Err(_) => true,
|
||||||
this.commits = commits;
|
};
|
||||||
|
|
||||||
match diff {
|
let git = if use_nostr {
|
||||||
Ok(diff) => {
|
None
|
||||||
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
} else {
|
||||||
|
let cache = cache.clone();
|
||||||
|
let addr = addr.clone();
|
||||||
|
let clone_urls = clone_urls.clone();
|
||||||
|
let base = merge_base.clone();
|
||||||
|
let tip = current_commit.clone();
|
||||||
|
|
||||||
|
Some(
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
let repo = cache.ensure_clone(&addr, &clone_urls)?;
|
||||||
|
|
||||||
|
let workdir = repo
|
||||||
|
.workdir()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("repository has no worktree"))?
|
||||||
|
.to_path_buf();
|
||||||
|
|
||||||
|
let tip = tip
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("pull request has no tip commit"))?;
|
||||||
|
|
||||||
|
let base = match base {
|
||||||
|
Some(base) => base,
|
||||||
|
// No `merge-base` tag. Use the merge base of the tip and the default branch.
|
||||||
|
None => {
|
||||||
|
let head = repo
|
||||||
|
.head_id()
|
||||||
|
.map_err(|_| anyhow::anyhow!("repository has no HEAD"))?;
|
||||||
|
let tip_id = repo.rev_parse_single(tip.as_bytes())?;
|
||||||
|
repo.merge_base(tip_id, head)?.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let diff =
|
||||||
|
signed_git::worktree_commit_range_diff(&workdir, &base, &tip)?;
|
||||||
|
let commits =
|
||||||
|
signed_git::worktree_commit_range_commits(&workdir, &base, &tip)?;
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>((diff, commits, workdir))
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (diff, commits, worktree) = match git {
|
||||||
|
Some(Ok((diff, commits, worktree))) => (Ok(diff), commits, Some(worktree)),
|
||||||
|
Some(Err(error)) => (Err(error), Vec::new(), None),
|
||||||
|
None => (nostr_diff, nostr_commits, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.update_in(cx, |this, _window, cx| {
|
||||||
|
this.loading = false;
|
||||||
|
this.worktree = worktree;
|
||||||
|
this.current_commit = current_commit.map(SharedString::from);
|
||||||
|
this.commit_item_sizes =
|
||||||
|
Rc::new(vec![size(px(0.), px(ROW_HEIGHT)); commits.len()]);
|
||||||
|
this.commits = commits;
|
||||||
|
|
||||||
|
match diff {
|
||||||
|
Ok(diff) => {
|
||||||
|
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
this.error = Some(error.to_string().into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
|
||||||
this.error = Some(error.to_string().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
self.tasks.retain(|task| !task.is_ready());
|
task.detach();
|
||||||
self.tasks.push(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the diff of `commit_id` in the bottom dock of the area.
|
/// Open the diff of `commit_id` in the bottom dock of the area.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user