diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index fccedaa809..3714086dd0 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -77,6 +77,8 @@ actions!( Commit, /// Amends the last commit with staged changes. Amend, + /// Enable the --signoff option. + Signoff, /// Cancels the current git operation. Cancel, /// Expands the commit message editor. diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 2ecd4bb894..9cc3442392 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -96,6 +96,7 @@ impl Upstream { #[derive(Clone, Copy, Default)] pub struct CommitOptions { pub amend: bool, + pub signoff: bool, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -1209,6 +1210,10 @@ impl GitRepository for RealGitRepository { cmd.arg("--amend"); } + if options.signoff { + cmd.arg("--signoff"); + } + if let Some((name, email)) = name_and_email { cmd.arg("--author").arg(&format!("{name} <{email}>")); } diff --git a/crates/git_ui/src/commit_modal.rs b/crates/git_ui/src/commit_modal.rs index 15d0bec313..ac3d24e3eb 100644 --- a/crates/git_ui/src/commit_modal.rs +++ b/crates/git_ui/src/commit_modal.rs @@ -1,8 +1,8 @@ use crate::branch_picker::{self, BranchList}; use crate::git_panel::{GitPanel, commit_message_editor}; use git::repository::CommitOptions; -use git::{Amend, Commit, GenerateCommitMessage}; -use panel::{panel_button, panel_editor_style, panel_filled_button}; +use git::{Amend, Commit, GenerateCommitMessage, Signoff}; +use panel::{panel_button, panel_editor_style}; use ui::{ ContextMenu, KeybindingHint, PopoverMenu, PopoverMenuHandle, SplitButton, Tooltip, prelude::*, }; @@ -273,14 +273,51 @@ impl CommitModal { .child(Icon::new(IconName::ChevronDownSmall).size(IconSize::XSmall)), ), ) - .menu(move |window, cx| { - Some(ContextMenu::build(window, cx, |context_menu, _, _| { - context_menu - .when_some(keybinding_target.clone(), |el, keybinding_target| { - el.context(keybinding_target.clone()) - }) - .action("Amend", Amend.boxed_clone()) - })) + .menu({ + let git_panel_entity = self.git_panel.clone(); + move |window, cx| { + let git_panel = git_panel_entity.read(cx); + let amend_enabled = git_panel.amend_pending(); + let signoff_enabled = git_panel.signoff_enabled(); + let has_previous_commit = git_panel.head_commit(cx).is_some(); + + Some(ContextMenu::build(window, cx, |context_menu, _, _| { + context_menu + .when_some(keybinding_target.clone(), |el, keybinding_target| { + el.context(keybinding_target.clone()) + }) + .when(has_previous_commit, |this| { + this.toggleable_entry( + "Amend", + amend_enabled, + IconPosition::Start, + Some(Box::new(Amend)), + { + let git_panel = git_panel_entity.clone(); + move |window, cx| { + git_panel.update(cx, |git_panel, cx| { + git_panel.toggle_amend_pending(&Amend, window, cx); + }) + } + }, + ) + }) + .toggleable_entry( + "Signoff", + signoff_enabled, + IconPosition::Start, + Some(Box::new(Signoff)), + { + let git_panel = git_panel_entity.clone(); + move |window, cx| { + git_panel.update(cx, |git_panel, cx| { + git_panel.toggle_signoff_enabled(&Signoff, window, cx); + }) + } + }, + ) + })) + } }) .with_handle(self.commit_menu_handle.clone()) .anchor(Corner::TopRight) @@ -295,7 +332,7 @@ impl CommitModal { generate_commit_message, active_repo, is_amend_pending, - has_previous_commit, + is_signoff_enabled, ) = self.git_panel.update(cx, |git_panel, cx| { let (can_commit, tooltip) = git_panel.configure_commit_button(cx); let title = git_panel.commit_button_title(); @@ -303,10 +340,7 @@ impl CommitModal { let generate_commit_message = git_panel.render_generate_commit_message_button(cx); let active_repo = git_panel.active_repository.clone(); let is_amend_pending = git_panel.amend_pending(); - let has_previous_commit = active_repo - .as_ref() - .and_then(|repo| repo.read(cx).head_commit.as_ref()) - .is_some(); + let is_signoff_enabled = git_panel.signoff_enabled(); ( can_commit, tooltip, @@ -315,7 +349,7 @@ impl CommitModal { generate_commit_message, active_repo, is_amend_pending, - has_previous_commit, + is_signoff_enabled, ) }); @@ -396,126 +430,59 @@ impl CommitModal { .px_1() .gap_4() .children(close_kb_hint) - .when(is_amend_pending, |this| { - let focus_handle = focus_handle.clone(); - this.child( - panel_filled_button(commit_label) - .tooltip(move |window, cx| { - if can_commit { - Tooltip::for_action_in( - tooltip, - &Amend, - &focus_handle, - window, - cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } - }) - .disabled(!can_commit) - .on_click(cx.listener(move |this, _: &ClickEvent, window, cx| { - telemetry::event!("Git Amended", source = "Git Modal"); - this.git_panel.update(cx, |git_panel, cx| { - git_panel.set_amend_pending(false, cx); - git_panel.commit_changes( - CommitOptions { amend: true }, - window, - cx, - ); - }); - cx.emit(DismissEvent); - })), + .child(SplitButton::new( + ui::ButtonLike::new_rounded_left(ElementId::Name( + format!("split-button-left-{}", commit_label).into(), + )) + .layer(ui::ElevationIndex::ModalSurface) + .size(ui::ButtonSize::Compact) + .child( + div() + .child(Label::new(commit_label).size(LabelSize::Small)) + .mr_0p5(), ) - }) - .when(!is_amend_pending, |this| { - this.when(has_previous_commit, |this| { - this.child(SplitButton::new( - ui::ButtonLike::new_rounded_left(ElementId::Name( - format!("split-button-left-{}", commit_label).into(), - )) - .layer(ui::ElevationIndex::ModalSurface) - .size(ui::ButtonSize::Compact) - .child( - div() - .child(Label::new(commit_label).size(LabelSize::Small)) - .mr_0p5(), + .on_click(cx.listener(move |this, _: &ClickEvent, window, cx| { + telemetry::event!("Git Committed", source = "Git Modal"); + this.git_panel.update(cx, |git_panel, cx| { + git_panel.commit_changes( + CommitOptions { + amend: is_amend_pending, + signoff: is_signoff_enabled, + }, + window, + cx, ) - .on_click(cx.listener(move |this, _: &ClickEvent, window, cx| { - telemetry::event!("Git Committed", source = "Git Modal"); - this.git_panel.update(cx, |git_panel, cx| { - git_panel.commit_changes( - CommitOptions { amend: false }, - window, - cx, - ) - }); - cx.emit(DismissEvent); - })) - .disabled(!can_commit) - .tooltip({ - let focus_handle = focus_handle.clone(); - move |window, cx| { - if can_commit { - Tooltip::with_meta_in( - tooltip, - Some(&git::Commit), - "git commit", - &focus_handle.clone(), - window, - cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } - } - }), - self.render_git_commit_menu( - ElementId::Name( - format!("split-button-right-{}", commit_label).into(), - ), - Some(focus_handle.clone()), - ) - .into_any_element(), - )) - }) - .when(!has_previous_commit, |this| { - this.child( - panel_filled_button(commit_label) - .tooltip(move |window, cx| { - if can_commit { - Tooltip::with_meta_in( - tooltip, - Some(&git::Commit), - "git commit", - &focus_handle, - window, - cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } - }) - .disabled(!can_commit) - .on_click(cx.listener( - move |this, _: &ClickEvent, window, cx| { - telemetry::event!( - "Git Committed", - source = "Git Modal" - ); - this.git_panel.update(cx, |git_panel, cx| { - git_panel.commit_changes( - CommitOptions { amend: false }, - window, - cx, - ) - }); - cx.emit(DismissEvent); - }, - )), - ) - }) - }), + }); + cx.emit(DismissEvent); + })) + .disabled(!can_commit) + .tooltip({ + let focus_handle = focus_handle.clone(); + move |window, cx| { + if can_commit { + Tooltip::with_meta_in( + tooltip, + Some(&git::Commit), + format!( + "git commit{}{}", + if is_amend_pending { " --amend" } else { "" }, + if is_signoff_enabled { " --signoff" } else { "" } + ), + &focus_handle.clone(), + window, + cx, + ) + } else { + Tooltip::simple(tooltip, cx) + } + } + }), + self.render_git_commit_menu( + ElementId::Name(format!("split-button-right-{}", commit_label).into()), + Some(focus_handle.clone()), + ) + .into_any_element(), + )), ) } @@ -534,7 +501,14 @@ impl CommitModal { } telemetry::event!("Git Committed", source = "Git Modal"); self.git_panel.update(cx, |git_panel, cx| { - git_panel.commit_changes(CommitOptions { amend: false }, window, cx) + git_panel.commit_changes( + CommitOptions { + amend: false, + signoff: git_panel.signoff_enabled(), + }, + window, + cx, + ) }); cx.emit(DismissEvent); } @@ -559,7 +533,14 @@ impl CommitModal { telemetry::event!("Git Amended", source = "Git Modal"); self.git_panel.update(cx, |git_panel, cx| { git_panel.set_amend_pending(false, cx); - git_panel.commit_changes(CommitOptions { amend: true }, window, cx); + git_panel.commit_changes( + CommitOptions { + amend: true, + signoff: git_panel.signoff_enabled(), + }, + window, + cx, + ); }); cx.emit(DismissEvent); } diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 52bed2cc79..2397f51f82 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -25,7 +25,7 @@ use git::repository::{ UpstreamTrackingStatus, get_git_committer, }; use git::status::StageStatus; -use git::{Amend, ToggleStaged, repository::RepoPath, status::FileStatus}; +use git::{Amend, Signoff, ToggleStaged, repository::RepoPath, status::FileStatus}; use git::{ExpandCommitEditor, RestoreTrackedFiles, StageAll, TrashUntrackedFiles, UnstageAll}; use gpui::{ Action, Animation, AnimationExt as _, AsyncApp, AsyncWindowContext, Axis, ClickEvent, Corner, @@ -61,8 +61,8 @@ use std::{collections::HashSet, sync::Arc, time::Duration, usize}; use strum::{IntoEnumIterator, VariantNames}; use time::OffsetDateTime; use ui::{ - Checkbox, ContextMenu, ElevationIndex, PopoverMenu, Scrollbar, ScrollbarState, SplitButton, - Tooltip, prelude::*, + Checkbox, ContextMenu, ElevationIndex, IconPosition, Label, LabelSize, PopoverMenu, Scrollbar, + ScrollbarState, SplitButton, Tooltip, prelude::*, }; use util::{ResultExt, TryFutureExt, maybe}; @@ -174,6 +174,10 @@ pub enum Event { #[derive(Serialize, Deserialize)] struct SerializedGitPanel { width: Option, + #[serde(default)] + amend_pending: bool, + #[serde(default)] + signoff_enabled: bool, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -337,6 +341,7 @@ pub struct GitPanel { pending: Vec, pending_commit: Option>, amend_pending: bool, + signoff_enabled: bool, pending_serialization: Task>, pub(crate) project: Entity, scroll_handle: UniformListScrollHandle, @@ -512,6 +517,7 @@ impl GitPanel { pending: Vec::new(), pending_commit: None, amend_pending: false, + signoff_enabled: false, pending_serialization: Task::ready(None), single_staged_entry: None, single_tracked_entry: None, @@ -690,14 +696,38 @@ impl GitPanel { cx.notify(); } + fn serialization_key(workspace: &Workspace) -> Option { + workspace + .database_id() + .map(|id| i64::from(id).to_string()) + .or(workspace.session_id()) + .map(|id| format!("{}-{:?}", GIT_PANEL_KEY, id)) + } + fn serialize(&mut self, cx: &mut Context) { let width = self.width; + let amend_pending = self.amend_pending; + let signoff_enabled = self.signoff_enabled; + + let Some(serialization_key) = self + .workspace + .read_with(cx, |workspace, _| Self::serialization_key(workspace)) + .ok() + .flatten() + else { + return; + }; + self.pending_serialization = cx.background_spawn( async move { KEY_VALUE_STORE .write_kvp( - GIT_PANEL_KEY.into(), - serde_json::to_string(&SerializedGitPanel { width })?, + serialization_key, + serde_json::to_string(&SerializedGitPanel { + width, + amend_pending, + signoff_enabled, + })?, ) .await?; anyhow::Ok(()) @@ -1432,7 +1462,14 @@ impl GitPanel { .contains_focused(window, cx) { telemetry::event!("Git Committed", source = "Git Panel"); - self.commit_changes(CommitOptions { amend: false }, window, cx) + self.commit_changes( + CommitOptions { + amend: false, + signoff: self.signoff_enabled, + }, + window, + cx, + ) } else { cx.propagate(); } @@ -1444,19 +1481,21 @@ impl GitPanel { .focus_handle(cx) .contains_focused(window, cx) { - if self - .active_repository - .as_ref() - .and_then(|repo| repo.read(cx).head_commit.as_ref()) - .is_some() - { + if self.head_commit(cx).is_some() { if !self.amend_pending { self.set_amend_pending(true, cx); self.load_last_commit_message_if_empty(cx); } else { telemetry::event!("Git Amended", source = "Git Panel"); self.set_amend_pending(false, cx); - self.commit_changes(CommitOptions { amend: true }, window, cx); + self.commit_changes( + CommitOptions { + amend: true, + signoff: self.signoff_enabled, + }, + window, + cx, + ); } } } else { @@ -1464,21 +1503,21 @@ impl GitPanel { } } + pub fn head_commit(&self, cx: &App) -> Option { + self.active_repository + .as_ref() + .and_then(|repo| repo.read(cx).head_commit.as_ref()) + .cloned() + } + pub fn load_last_commit_message_if_empty(&mut self, cx: &mut Context) { if !self.commit_editor.read(cx).is_empty(cx) { return; } - let Some(active_repository) = self.active_repository.as_ref() else { - return; - }; - let Some(recent_sha) = active_repository - .read(cx) - .head_commit - .as_ref() - .map(|commit| commit.sha.to_string()) - else { + let Some(head_commit) = self.head_commit(cx) else { return; }; + let recent_sha = head_commit.sha.to_string(); let detail_task = self.load_commit_details(recent_sha, cx); cx.spawn(async move |this, cx| { if let Ok(message) = detail_task.await.map(|detail| detail.message) { @@ -1495,12 +1534,6 @@ impl GitPanel { .detach(); } - fn cancel(&mut self, _: &git::Cancel, _: &mut Window, cx: &mut Context) { - if self.amend_pending { - self.set_amend_pending(false, cx); - } - } - fn custom_or_suggested_commit_message( &self, window: &mut Window, @@ -3003,14 +3036,35 @@ impl GitPanel { .child(Icon::new(IconName::ChevronDownSmall).size(IconSize::XSmall)), ), ) - .menu(move |window, cx| { - Some(ContextMenu::build(window, cx, |context_menu, _, _| { - context_menu - .when_some(keybinding_target.clone(), |el, keybinding_target| { - el.context(keybinding_target.clone()) - }) - .action("Amend", Amend.boxed_clone()) - })) + .menu({ + let has_previous_commit = self.head_commit(cx).is_some(); + let amend = self.amend_pending(); + let signoff = self.signoff_enabled; + + move |window, cx| { + Some(ContextMenu::build(window, cx, |context_menu, _, _| { + context_menu + .when_some(keybinding_target.clone(), |el, keybinding_target| { + el.context(keybinding_target.clone()) + }) + .when(has_previous_commit, |this| { + this.toggleable_entry( + "Amend", + amend, + IconPosition::Start, + Some(Box::new(Amend)), + move |window, cx| window.dispatch_action(Box::new(Amend), cx), + ) + }) + .toggleable_entry( + "Signoff", + signoff, + IconPosition::Start, + Some(Box::new(Signoff)), + move |window, cx| window.dispatch_action(Box::new(Signoff), cx), + ) + })) + } }) .anchor(Corner::TopRight) } @@ -3187,7 +3241,6 @@ impl GitPanel { let editor_is_long = self.commit_editor.update(cx, |editor, cx| { editor.max_point(cx).row().0 >= MAX_PANEL_EDITOR_LINES as u32 }); - let has_previous_commit = head_commit.is_some(); let footer = v_flex() .child(PanelRepoFooter::new( @@ -3231,7 +3284,7 @@ impl GitPanel { h_flex() .gap_0p5() .children(enable_coauthors) - .child(self.render_commit_button(has_previous_commit, cx)), + .child(self.render_commit_button(cx)), ), ) .child( @@ -3280,14 +3333,12 @@ impl GitPanel { Some(footer) } - fn render_commit_button( - &self, - has_previous_commit: bool, - cx: &mut Context, - ) -> impl IntoElement { + fn render_commit_button(&self, cx: &mut Context) -> impl IntoElement { let (can_commit, tooltip) = self.configure_commit_button(cx); let title = self.commit_button_title(); let commit_tooltip_focus_handle = self.commit_editor.focus_handle(cx); + let amend = self.amend_pending(); + let signoff = self.signoff_enabled; div() .id("commit-wrapper") @@ -3296,165 +3347,86 @@ impl GitPanel { *hovered && !this.has_staged_changes() && !this.has_unstaged_conflicts(); cx.notify() })) - .when(self.amend_pending, { - |this| { - this.h_flex() - .gap_1() - .child( - panel_filled_button("Cancel") - .tooltip({ - let handle = commit_tooltip_focus_handle.clone(); - move |window, cx| { - Tooltip::for_action_in( - "Cancel amend", - &git::Cancel, - &handle, - window, - cx, - ) - } - }) - .on_click(move |_, window, cx| { - window.dispatch_action(Box::new(git::Cancel), cx); - }), - ) - .child( - panel_filled_button(title) - .tooltip({ - let handle = commit_tooltip_focus_handle.clone(); - move |window, cx| { - if can_commit { - Tooltip::for_action_in( - tooltip, &Amend, &handle, window, cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } - } - }) - .disabled(!can_commit || self.modal_open) - .on_click({ - let git_panel = cx.weak_entity(); - move |_, window, cx| { - telemetry::event!("Git Amended", source = "Git Panel"); - git_panel - .update(cx, |git_panel, cx| { - git_panel.set_amend_pending(false, cx); - git_panel.commit_changes( - CommitOptions { amend: true }, - window, - cx, - ); - }) - .ok(); - } - }), - ) - } - }) - .when(!self.amend_pending, |this| { - this.when(has_previous_commit, |this| { - this.child(SplitButton::new( - ui::ButtonLike::new_rounded_left(ElementId::Name( - format!("split-button-left-{}", title).into(), - )) - .layer(ui::ElevationIndex::ModalSurface) - .size(ui::ButtonSize::Compact) - .child( - div() - .child(Label::new(title).size(LabelSize::Small)) - .mr_0p5(), - ) - .on_click({ - let git_panel = cx.weak_entity(); - move |_, window, cx| { - telemetry::event!("Git Committed", source = "Git Panel"); - git_panel - .update(cx, |git_panel, cx| { - git_panel.commit_changes( - CommitOptions { amend: false }, - window, - cx, - ); - }) - .ok(); - } - }) - .disabled(!can_commit || self.modal_open) - .tooltip({ - let handle = commit_tooltip_focus_handle.clone(); - move |window, cx| { - if can_commit { - Tooltip::with_meta_in( - tooltip, - Some(&git::Commit), - "git commit", - &handle.clone(), - window, - cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } - } - }), - self.render_git_commit_menu( - ElementId::Name(format!("split-button-right-{}", title).into()), - Some(commit_tooltip_focus_handle.clone()), - cx, - ) - .into_any_element(), - )) - }) - .when(!has_previous_commit, |this| { - this.child( - panel_filled_button(title) - .tooltip(move |window, cx| { - if can_commit { - Tooltip::with_meta_in( - tooltip, - Some(&git::Commit), - "git commit", - &commit_tooltip_focus_handle, - window, - cx, - ) - } else { - Tooltip::simple(tooltip, cx) - } + .child(SplitButton::new( + ui::ButtonLike::new_rounded_left(ElementId::Name( + format!("split-button-left-{}", title).into(), + )) + .layer(ui::ElevationIndex::ModalSurface) + .size(ui::ButtonSize::Compact) + .child( + div() + .child(Label::new(title).size(LabelSize::Small)) + .mr_0p5(), + ) + .on_click({ + let git_panel = cx.weak_entity(); + move |_, window, cx| { + telemetry::event!("Git Committed", source = "Git Panel"); + git_panel + .update(cx, |git_panel, cx| { + git_panel.set_amend_pending(false, cx); + git_panel.commit_changes( + CommitOptions { amend, signoff }, + window, + cx, + ); }) - .disabled(!can_commit || self.modal_open) - .on_click({ - let git_panel = cx.weak_entity(); - move |_, window, cx| { - telemetry::event!("Git Committed", source = "Git Panel"); - git_panel - .update(cx, |git_panel, cx| { - git_panel.commit_changes( - CommitOptions { amend: false }, - window, - cx, - ); - }) - .ok(); - } - }), - ) + .ok(); + } }) - }) + .disabled(!can_commit || self.modal_open) + .tooltip({ + let handle = commit_tooltip_focus_handle.clone(); + move |window, cx| { + if can_commit { + Tooltip::with_meta_in( + tooltip, + Some(&git::Commit), + format!( + "git commit{}{}", + if amend { " --amend" } else { "" }, + if signoff { " --signoff" } else { "" } + ), + &handle.clone(), + window, + cx, + ) + } else { + Tooltip::simple(tooltip, cx) + } + } + }), + self.render_git_commit_menu( + ElementId::Name(format!("split-button-right-{}", title).into()), + Some(commit_tooltip_focus_handle.clone()), + cx, + ) + .into_any_element(), + )) } fn render_pending_amend(&self, cx: &mut Context) -> impl IntoElement { - div() - .p_2() + h_flex() + .py_1p5() + .px_2() + .gap_1p5() + .justify_between() .border_t_1() - .border_color(cx.theme().colors().border) + .border_color(cx.theme().colors().border.opacity(0.8)) .child( - Label::new( - "This will update your most recent commit. Cancel to make a new one instead.", - ) - .size(LabelSize::Small), + div() + .flex_grow() + .overflow_hidden() + .max_w(relative(0.85)) + .child( + Label::new("This will update your most recent commit.") + .size(LabelSize::Small) + .truncate(), + ), ) + .child(panel_button("Cancel").size(ButtonSize::Default).on_click( + cx.listener(|this, _, window, cx| this.toggle_amend_pending(&Amend, window, cx)), + )) } fn render_previous_commit(&self, cx: &mut Context) -> Option { @@ -4218,17 +4190,56 @@ impl GitPanel { cx.notify(); } + pub fn toggle_amend_pending( + &mut self, + _: &Amend, + _window: &mut Window, + cx: &mut Context, + ) { + self.set_amend_pending(!self.amend_pending, cx); + self.serialize(cx); + } + + pub fn signoff_enabled(&self) -> bool { + self.signoff_enabled + } + + pub fn set_signoff_enabled(&mut self, value: bool, cx: &mut Context) { + self.signoff_enabled = value; + self.serialize(cx); + cx.notify(); + } + + pub fn toggle_signoff_enabled( + &mut self, + _: &Signoff, + _window: &mut Window, + cx: &mut Context, + ) { + self.set_signoff_enabled(!self.signoff_enabled, cx); + } + pub async fn load( workspace: WeakEntity, mut cx: AsyncWindowContext, ) -> anyhow::Result> { - let serialized_panel = cx - .background_spawn(async move { KEY_VALUE_STORE.read_kvp(&GIT_PANEL_KEY) }) - .await - .context("loading git panel") - .log_err() + let serialized_panel = match workspace + .read_with(&cx, |workspace, _| Self::serialization_key(workspace)) + .ok() .flatten() - .and_then(|panel| serde_json::from_str::(&panel).log_err()); + { + Some(serialization_key) => cx + .background_spawn(async move { KEY_VALUE_STORE.read_kvp(&serialization_key) }) + .await + .context("loading git panel") + .log_err() + .flatten() + .map(|panel| serde_json::from_str::(&panel)) + .transpose() + .log_err() + .flatten(), + None => None, + }; workspace.update_in(&mut cx, |workspace, window, cx| { let panel = GitPanel::new(workspace, window, cx); @@ -4236,6 +4247,8 @@ impl GitPanel { if let Some(serialized_panel) = serialized_panel { panel.update(cx, |panel, cx| { panel.width = serialized_panel.width; + panel.amend_pending = serialized_panel.amend_pending; + panel.signoff_enabled = serialized_panel.signoff_enabled; cx.notify(); }) } @@ -4320,7 +4333,8 @@ impl Render for GitPanel { .on_action(cx.listener(Self::stage_range)) .on_action(cx.listener(GitPanel::commit)) .on_action(cx.listener(GitPanel::amend)) - .on_action(cx.listener(GitPanel::cancel)) + .on_action(cx.listener(GitPanel::toggle_amend_pending)) + .on_action(cx.listener(GitPanel::toggle_signoff_enabled)) .on_action(cx.listener(Self::stage_all)) .on_action(cx.listener(Self::unstage_all)) .on_action(cx.listener(Self::stage_selected)) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 9ff3823e0f..5f07bafe5b 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -1738,6 +1738,7 @@ impl GitStore { name.zip(email), CommitOptions { amend: options.amend, + signoff: options.signoff, }, cx, ) @@ -3488,6 +3489,7 @@ impl Repository { email: email.map(String::from), options: Some(proto::commit::CommitOptions { amend: options.amend, + signoff: options.signoff, }), }) .await diff --git a/crates/proto/proto/git.proto b/crates/proto/proto/git.proto index 6593062ed2..1d544b15ff 100644 --- a/crates/proto/proto/git.proto +++ b/crates/proto/proto/git.proto @@ -298,6 +298,7 @@ message Commit { message CommitOptions { bool amend = 1; + bool signoff = 2; } }