From 6a69b1af49e1359625d45356094848d0c9c135ca Mon Sep 17 00:00:00 2001 From: lljbash Date: Tue, 13 May 2025 14:46:21 +0800 Subject: [PATCH] feat: use `git commit --verbose` template in external editor --- asyncgit/src/sync/commit.rs | 40 +++++++++++++++++++---- asyncgit/src/sync/mod.rs | 3 +- src/app.rs | 4 +-- src/popups/commit.rs | 64 ++++++++++--------------------------- src/strings.rs | 6 ---- src/tabs/status.rs | 6 +--- 6 files changed, 54 insertions(+), 69 deletions(-) diff --git a/asyncgit/src/sync/commit.rs b/asyncgit/src/sync/commit.rs index f4e0b194ea..63da5e0b80 100644 --- a/asyncgit/src/sync/commit.rs +++ b/asyncgit/src/sync/commit.rs @@ -182,18 +182,26 @@ pub fn tag_commit( } /// Loads the comment prefix from config & uses it to prettify commit messages +/// +/// Also removes any lines after the scissors line. pub fn commit_message_prettify( repo_path: &RepoPath, - message: String, + message: &str, ) -> Result { let comment_char = repo(repo_path)? .config()? .get_string("core.commentChar") .ok() .and_then(|char_string| char_string.chars().next()) - .unwrap_or('#') as u8; - - Ok(message_prettify(message, Some(comment_char))?) + .unwrap_or('#'); + let scissors_line = format!("{comment_char} ------------------------ >8 ------------------------"); + let message = message + .lines() + .take_while(|line| line != &scissors_line) + .collect::>() + .join("\n"); + + Ok(message_prettify(message, Some(comment_char as u8))?) } #[cfg(test)] @@ -468,7 +476,7 @@ mod tests { let message = commit_message_prettify( repo_path, - "#This is a test message\nTest".to_owned(), + "#This is a test message\nTest", )?; assert_eq!(message, "Test\n"); @@ -487,7 +495,27 @@ mod tests { let message = commit_message_prettify( repo_path, - ";This is a test message\nTest".to_owned(), + ";This is a test message\nTest", + )?; + + assert_eq!(message, "Test\n"); + + Ok(()) + } + + #[test] + fn test_scissors_line() -> Result<()> { + let (_td, repo) = repo_init_empty().unwrap(); + + let root = repo.path().parent().unwrap(); + let repo_path: &RepoPath = + &root.as_os_str().to_str().unwrap().into(); + + repo.config()?.set_str("core.commentChar", ";")?; + + let message = commit_message_prettify( + repo_path, + ";This is a test message\nTest\n; ------------------------ >8 ------------------------\nTest2\nTest3\nTest4", )?; assert_eq!(message, "Test\n"); diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index 09d4e6ef53..84706db2db 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -105,7 +105,8 @@ pub use tags::{ pub use tree::{tree_file_content, tree_files, TreeFile}; pub use utils::{ get_head, get_head_tuple, repo_dir, repo_open_error, - stage_add_all, stage_add_file, stage_addremoved, Head, + repo_work_dir, stage_add_all, stage_add_file, stage_addremoved, + Head, }; pub use git2::ResetType; diff --git a/src/app.rs b/src/app.rs index 68d2987c6f..3a2911542c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -359,9 +359,7 @@ impl App { Path::new(&path), ) } else { - let changes = - self.status_tab.get_files_changes()?; - self.commit_popup.show_editor(changes) + self.commit_popup.show_editor() }; if let Err(e) = result { diff --git a/src/popups/commit.rs b/src/popups/commit.rs index 008fc6f8a7..d7ce5f130c 100644 --- a/src/popups/commit.rs +++ b/src/popups/commit.rs @@ -18,7 +18,6 @@ use asyncgit::{ self, get_config_string, CommitId, HookResult, PrepareCommitMsgSource, RepoPathRef, RepoState, }, - StatusItem, StatusItemType, }; use crossterm::event::Event; use easy_cast::Cast; @@ -28,10 +27,11 @@ use ratatui::{ Frame, }; +use std::process::Command; use std::{ fmt::Write as _, fs::{read_to_string, File}, - io::{Read, Write}, + io::Read, path::PathBuf, str::FromStr, }; @@ -144,47 +144,18 @@ impl CommitPopup { } } - const fn item_status_char( - item_type: StatusItemType, - ) -> &'static str { - match item_type { - StatusItemType::Modified => "modified", - StatusItemType::New => "new file", - StatusItemType::Deleted => "deleted", - StatusItemType::Renamed => "renamed", - StatusItemType::Typechange => " ", - StatusItemType::Conflicted => "conflicted", - } - } - - pub fn show_editor( - &mut self, - changes: Vec, - ) -> Result<()> { - let file_path = sync::repo_dir(&self.repo.borrow())? - .join("COMMIT_EDITMSG"); + pub fn show_editor(&mut self) -> Result<()> { + let git_dir = sync::repo_dir(&self.repo.borrow())?; + let work_dir = sync::repo_work_dir(&self.repo.borrow())?; + let file_path = git_dir.join("COMMIT_EDITMSG"); - { - let mut file = File::create(&file_path)?; - file.write_fmt(format_args!( - "{}\n", - self.input.get_text() - ))?; - file.write_all( - strings::commit_editor_msg(&self.key_config) - .as_bytes(), - )?; - - file.write_all(b"\n#\n# Changes to be committed:")?; - - for change in changes { - let status_char = - Self::item_status_char(change.status); - let message = - format!("\n#\t{status_char}: {}", change.path); - file.write_all(message.as_bytes())?; - } - } + Command::new("git") + .arg("commit") + .arg("--verbose") + .env("EDITOR", "false") + .env("GIT_DIR", git_dir) + .env("GIT_WORK_TREE", work_dir) + .output()?; ExternalEditorPopup::open_file_in_editor( &self.repo.borrow(), @@ -199,7 +170,7 @@ impl CommitPopup { std::fs::remove_file(&file_path)?; message = - commit_message_prettify(&self.repo.borrow(), message)?; + commit_message_prettify(&self.repo.borrow(), &message)?; self.input.set_text(message); self.input.show()?; @@ -210,7 +181,7 @@ impl CommitPopup { let msg = self.input.get_text().to_string(); if matches!( - self.commit_with_msg(msg)?, + self.commit_with_msg(&msg)?, CommitResult::CommitDone ) { self.options @@ -227,10 +198,7 @@ impl CommitPopup { Ok(()) } - fn commit_with_msg( - &mut self, - msg: String, - ) -> Result { + fn commit_with_msg(&mut self, msg: &str) -> Result { // on exit verify should always be on let verify = self.verify; self.verify = true; diff --git a/src/strings.rs b/src/strings.rs index c4cff10f70..1cb87c70ec 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -131,12 +131,6 @@ pub fn commit_first_line_warning(count: usize) -> String { pub const fn branch_name_invalid() -> &'static str { "[invalid name]" } -pub fn commit_editor_msg(_key_config: &SharedKeyConfig) -> String { - r" -# Edit your commit message -# Lines starting with '#' will be ignored" - .to_string() -} pub fn stash_popup_title(_key_config: &SharedKeyConfig) -> String { "Stash".to_string() } diff --git a/src/tabs/status.rs b/src/tabs/status.rs index 40cf210786..0e21064cc4 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -21,7 +21,7 @@ use asyncgit::{ }, sync::{BranchCompare, CommitId}, AsyncDiff, AsyncGitNotification, AsyncStatus, DiffParams, - DiffType, PushType, StatusItem, StatusParams, + DiffType, PushType, StatusParams, }; use crossterm::event::Event; use itertools::Itertools; @@ -451,10 +451,6 @@ impl Status { Ok(()) } - pub fn get_files_changes(&self) -> Result> { - Ok(self.git_status_stage.last()?.items) - } - fn update_status(&mut self) -> Result<()> { let stage_status = self.git_status_stage.last()?; self.index.set_items(&stage_status.items)?;