Git uncommit warning (#25977)

Adds a prompt when clicking the uncommit button when the current commit
is already present on a remote branch:

![screenshot showing
prompt](https://github.com/user-attachments/assets/d6421875-588e-4db0-aee0-a92f36bce94b)

Release Notes:

- N/A

---------

Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Julia Ryan 2025-03-05 15:56:51 -08:00 committed by GitHub
parent 0200dda83d
commit e505d6bf5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 171 additions and 19 deletions

View file

@ -1265,34 +1265,20 @@ impl GitPanel {
return;
};
// TODO: Use git merge-base to find the upstream and main branch split
let confirmation = Task::ready(true);
// let confirmation = if self.commit_editor.read(cx).is_empty(cx) {
// Task::ready(true)
// } else {
// let prompt = window.prompt(
// PromptLevel::Warning,
// "Uncomitting will replace the current commit message with the previous commit's message",
// None,
// &["Ok", "Cancel"],
// cx,
// );
// cx.spawn(|_, _| async move { prompt.await.is_ok_and(|i| i == 0) })
// };
let confirmation = self.check_for_pushed_commits(window, cx);
let prior_head = self.load_commit_details("HEAD", cx);
let task = cx.spawn_in(window, |this, mut cx| async move {
let result = maybe!(async {
if !confirmation.await {
Ok(None)
} else {
if let Ok(true) = confirmation.await {
let prior_head = prior_head.await?;
repo.update(&mut cx, |repo, _| repo.reset("HEAD^", ResetMode::Soft))?
.await??;
Ok(Some(prior_head))
} else {
Ok(None)
}
})
.await;
@ -1315,6 +1301,48 @@ impl GitPanel {
self.pending_commit = Some(task);
}
fn check_for_pushed_commits(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> impl Future<Output = Result<bool, anyhow::Error>> {
let repo = self.active_repository.clone();
let mut cx = window.to_async(cx);
async move {
let Some(repo) = repo else {
return Err(anyhow::anyhow!("No active repository"));
};
let pushed_to: Vec<SharedString> = repo
.update(&mut cx, |repo, _| repo.check_for_pushed_commits())?
.await??;
if pushed_to.is_empty() {
Ok(true)
} else {
#[derive(strum::EnumIter, strum::VariantNames)]
#[strum(serialize_all = "title_case")]
enum CancelUncommit {
Uncommit,
Cancel,
}
let detail = format!(
"This commit was already pushed to {}.",
pushed_to.into_iter().join(", ")
);
let result = cx
.update(|window, cx| prompt("Are you sure?", Some(&detail), window, cx))?
.await?;
match result {
CancelUncommit::Cancel => Ok(false),
CancelUncommit::Uncommit => Ok(true),
}
}
}
}
/// Suggests a commit message based on the changed files and their statuses
pub fn suggest_commit_message(&self) -> Option<String> {
if self.total_staged_count() != 1 {