Compare commits

...

1 Commits

Author SHA1 Message Date
Miguel Raz Guzmán Macedo
fc9c3456e7 draft: Add git commit --cleanup=strip/--cleanup=whitespace option 2025-11-20 11:49:30 -06:00
6 changed files with 89 additions and 62 deletions

View File

@@ -12,7 +12,7 @@ use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};
use parking_lot::Mutex;
use rope::Rope;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use smol::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
use std::ffi::{OsStr, OsString};
use std::process::{ExitStatus, Stdio};
@@ -137,10 +137,18 @@ impl Upstream {
}
}
#[derive(Clone, Copy, Debug, Default)]
pub enum CommitMessageCleanup {
#[default]
Strip,
Whitespace,
}
#[derive(Clone, Copy, Default)]
pub struct CommitOptions {
pub amend: bool,
pub signoff: bool,
pub cleanup: CommitMessageCleanup,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
@@ -1637,12 +1645,16 @@ impl GitRepository for RealGitRepository {
let git_binary_path = self.any_git_binary_path.clone();
let executor = self.executor.clone();
async move {
let cleanup_kind = match options.cleanup {
CommitMessageCleanup::Strip => "--cleanup=strip",
CommitMessageCleanup::Whitespace => "--cleanup=whitespace",
};
let mut cmd = new_smol_command(git_binary_path);
cmd.current_dir(&working_directory?)
.envs(env.iter())
.args(["commit", "--quiet", "-m"])
.arg(&message.to_string())
.arg("--cleanup=strip")
.arg(cleanup_kind)
.stdout(smol::process::Stdio::piped())
.stderr(smol::process::Stdio::piped());

View File

@@ -1,6 +1,5 @@
use crate::branch_picker::{self, BranchList};
use crate::git_panel::{GitPanel, commit_message_editor};
use git::repository::CommitOptions;
use git::{Amend, Commit, GenerateCommitMessage, Signoff};
use panel::{panel_button, panel_editor_style};
use project::DisableAiSettings;
@@ -439,10 +438,8 @@ impl CommitModal {
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,
},
is_amend_pending,
is_signoff_enabled,
window,
cx,
)
@@ -498,14 +495,7 @@ impl CommitModal {
}
telemetry::event!("Git Committed", source = "Git Modal");
self.git_panel.update(cx, |git_panel, cx| {
git_panel.commit_changes(
CommitOptions {
amend: false,
signoff: git_panel.signoff_enabled(),
},
window,
cx,
)
git_panel.commit_changes(false, git_panel.signoff_enabled(), window, cx)
});
cx.emit(DismissEvent);
}
@@ -530,14 +520,7 @@ 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,
signoff: git_panel.signoff_enabled(),
},
window,
cx,
);
git_panel.commit_changes(true, git_panel.signoff_enabled(), window, cx);
});
cx.emit(DismissEvent);
}

View File

@@ -1538,14 +1538,7 @@ impl GitPanel {
.contains_focused(window, cx)
{
telemetry::event!("Git Committed", source = "Git Panel");
self.commit_changes(
CommitOptions {
amend: false,
signoff: self.signoff_enabled,
},
window,
cx,
)
self.commit_changes(false, self.signoff_enabled, window, cx)
} else {
cx.propagate();
}
@@ -1563,14 +1556,7 @@ impl GitPanel {
self.load_last_commit_message_if_empty(cx);
} else {
telemetry::event!("Git Amended", source = "Git Panel");
self.commit_changes(
CommitOptions {
amend: true,
signoff: self.signoff_enabled,
},
window,
cx,
);
self.commit_changes(true, self.signoff_enabled, window, cx);
}
}
} else {
@@ -1657,7 +1643,8 @@ impl GitPanel {
pub(crate) fn commit_changes(
&mut self,
options: CommitOptions,
amend: bool,
signoff: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -1696,7 +1683,7 @@ impl GitPanel {
let task = if self.has_staged_changes() {
// Repository serializes all git operations, so we can just send a commit immediately
let commit_task = active_repository.update(cx, |repo, cx| {
repo.commit(message.into(), None, options, askpass, cx)
repo.commit(message.into(), None, amend, signoff, askpass, cx)
});
cx.background_spawn(async move { commit_task.await? })
} else {
@@ -1708,7 +1695,7 @@ impl GitPanel {
.map(|status_entry| status_entry.repo_path.clone())
.collect::<Vec<_>>();
if changed_files.is_empty() && !options.amend {
if changed_files.is_empty() && !amend {
error_spawn("No changes to commit", window, cx);
return;
}
@@ -1718,7 +1705,7 @@ impl GitPanel {
cx.spawn(async move |_, cx| {
stage_task.await?;
let commit_task = active_repository.update(cx, |repo, cx| {
repo.commit(message.into(), None, options, askpass, cx)
repo.commit(message.into(), None, amend, signoff, askpass, cx)
})?;
commit_task.await?
})
@@ -1740,7 +1727,7 @@ impl GitPanel {
});
self.pending_commit = Some(task);
if options.amend {
if amend {
self.set_amend_pending(false, cx);
}
}
@@ -3646,11 +3633,7 @@ impl GitPanel {
telemetry::event!("Git Committed", source = "Git Panel");
git_panel
.update(cx, |git_panel, cx| {
git_panel.commit_changes(
CommitOptions { amend, signoff },
window,
cx,
);
git_panel.commit_changes(amend, signoff, window, cx);
})
.ok();
}

View File

@@ -6,6 +6,7 @@ pub mod pending_op;
use crate::{
ProjectEnvironment, ProjectItem, ProjectPath,
buffer_store::{BufferStore, BufferStoreEvent},
project_settings::{GitSettings, ProjectSettings},
worktree_store::{WorktreeStore, WorktreeStoreEvent},
};
use anyhow::{Context as _, Result, anyhow, bail};
@@ -55,7 +56,7 @@ use rpc::{
proto::{self, git_reset, split_repository_update},
};
use serde::Deserialize;
use settings::WorktreeId;
use settings::{Settings as _, WorktreeId};
use std::{
cmp::Ordering,
collections::{BTreeSet, HashSet, VecDeque},
@@ -2009,10 +2010,8 @@ impl GitStore {
repository_handle.commit(
message,
name.zip(email),
CommitOptions {
amend: options.amend,
signoff: options.signoff,
},
options.amend,
options.signoff,
askpass,
cx,
)
@@ -4266,21 +4265,41 @@ impl Repository {
&mut self,
message: SharedString,
name_and_email: Option<(SharedString, SharedString)>,
options: CommitOptions,
amend: bool,
signoff: bool,
askpass: AskPassDelegate,
_cx: &mut App,
cx: &mut App,
) -> oneshot::Receiver<Result<()>> {
let id = self.id;
let askpass_delegates = self.askpass_delegates.clone();
let askpass_id = util::post_inc(&mut self.latest_askpass_id);
self.send_job(Some("git commit".into()), move |git_repo, _cx| async move {
let project_path =
self.repo_path_to_project_path(&RepoPath::from_rel_path(RelPath::empty()), cx);
self.send_job(Some("git commit".into()), move |git_repo, cx| async move {
match git_repo {
RepositoryState::Local {
backend,
environment,
..
} => {
let cleanup = cx.update(|cx| {
ProjectSettings::get(
project_path
.as_ref()
.map(|project_path| settings::SettingsLocation {
worktree_id: project_path.worktree_id,
path: &project_path.path,
}),
cx,
)
.git
.commit_cleanup
})?;
let options = CommitOptions {
amend,
signoff,
cleanup,
};
backend
.commit(message, name_and_email, options, askpass, environment)
.await
@@ -4299,10 +4318,7 @@ impl Repository {
message: String::from(message),
name: name.map(String::from),
email: email.map(String::from),
options: Some(proto::commit::CommitOptions {
amend: options.amend,
signoff: options.signoff,
}),
options: Some(proto::commit::CommitOptions { amend, signoff }),
askpass_id,
})
.await

View File

@@ -4,6 +4,7 @@ use context_server::ContextServerCommand;
use dap::adapters::DebugAdapterName;
use fs::Fs;
use futures::StreamExt as _;
use git::repository::CommitMessageCleanup;
use gpui::{AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task};
use lsp::LanguageServerName;
use paths::{
@@ -352,6 +353,10 @@ pub struct GitSettings {
///
/// Default: file_name_first
pub path_style: GitPathStyle,
/// How commit cleanup is handled in the commit message.
///
/// Default: strip
pub commit_cleanup: CommitMessageCleanup,
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
@@ -522,6 +527,10 @@ impl Settings for ProjectSettings {
},
hunk_style: git.hunk_style.unwrap(),
path_style: git.path_style.unwrap().into(),
commit_cleanup: match git.commit_cleanup.unwrap_or_default() {
settings::CommitMessageCleanup::Strip => CommitMessageCleanup::Strip,
settings::CommitMessageCleanup::Whitespace => CommitMessageCleanup::Whitespace,
},
};
Self {
context_servers: project

View File

@@ -315,6 +315,10 @@ pub struct GitSettings {
///
/// Default: file_name_first
pub path_style: Option<GitPathStyle>,
/// How to cleanup a git commit message.
///
/// Default: strip
pub commit_cleanup: Option<CommitMessageCleanup>,
}
#[derive(
@@ -432,6 +436,26 @@ pub enum GitPathStyle {
FilePathFirst,
}
#[derive(
Copy,
Clone,
Debug,
PartialEq,
Default,
Serialize,
Deserialize,
JsonSchema,
MergeFrom,
strum::VariantArray,
strum::VariantNames,
)]
#[serde(rename_all = "snake_case")]
pub enum CommitMessageCleanup {
#[default]
Strip,
Whitespace,
}
#[skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct DiagnosticsSettingsContent {