diff --git a/src/ext/repository.rs b/src/ext/repository.rs index 24b66e9c..bdffd7a7 100644 --- a/src/ext/repository.rs +++ b/src/ext/repository.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; -use bstr::BStr; +use bstr::{BStr, ByteSlice}; use crate::{ stupid::Stupid, @@ -78,6 +78,13 @@ pub(crate) trait RepositoryExtended { /// [`gix::Repository::rev_parse_single()`] with StGit-specific error mapping. fn rev_parse_single_ex(&self, spec: &str) -> Result>; + + /// Get the comment symbol from git config. + /// + /// Returns the configured `core.commentChar` value, defaulting to "#" if not set. + /// Git's default is '#' and it also supports 'auto' which we treat as '#'. + /// Note: git allows this to be a string, not just a single character. + fn get_comment_symbol(&self) -> String; } /// Options for creating a git commit object. @@ -330,4 +337,17 @@ impl RepositoryExtended for gix::Repository { } }) } + + fn get_comment_symbol(&self) -> String { + let config = self.config_snapshot(); + config + // fetch the comment symbol from the config + .string("core.commentChar") + // convert the value to a string and trim it + .and_then(|value| value.to_str().ok().map(|s| s.trim().to_string())) + // filter out empty values and "auto" + .filter(|value_str| !value_str.is_empty() && value_str != "auto") + // if no value is found, return "#" + .unwrap_or("#".to_string()) + } } diff --git a/src/patch/edit/description.rs b/src/patch/edit/description.rs index bfb6aeb6..3c3f952e 100644 --- a/src/patch/edit/description.rs +++ b/src/patch/edit/description.rs @@ -9,6 +9,8 @@ use bstr::{BStr, BString, ByteSlice}; use crate::{ext::TimeExtended, patch::PatchName}; +const CUT_LINE: &str = "------------------------ >8 ------------------------"; + #[derive(Clone, PartialEq, Eq)] pub(super) struct DiffBuffer(pub(super) BString); @@ -45,30 +47,38 @@ pub(super) struct EditablePatchDescription { /// Instruction string presented to the user in the editable patch description file. /// /// Will be `None` when the description is read-back after user edits. - pub instruction: Option<&'static str>, + pub instruction: Option, /// Instructions for user regarding what can/cannot be done with the diff. /// /// This instruction is only needed/presented when the optional diff is provided. /// This field will be `None` when the description is read-back after user edits. - pub diff_instruction: Option<&'static str>, + pub diff_instruction: Option, /// Optional diff to present to the user in the editable patch description. /// /// Unlike all the other fields, the diff *does not* have to be valid UTF-8. pub diff: Option, -} -const CUT_LINE: &str = "# ------------------------ >8 ------------------------\n"; + /// Comment symbol to use for comment lines in the patch description. + /// + /// This is typically read from git's `core.commentChar` config. + pub comment_symbol: String, +} impl EditablePatchDescription { - /// Write user-editable patch description to the provided stream. - pub(super) fn write(&self, stream: &mut S) -> Result<()> { + /// Write user-editable patch description to the provided stream with custom comment string. + pub(super) fn write_with_comment_symbol( + &self, + stream: &mut S, + comment_symbol: &str, + ) -> Result<()> { let patchname = if let Some(patchname) = &self.patchname { patchname.as_ref() } else { "" }; + writeln!(stream, "Patch: {patchname}")?; if let Some(author) = self.author.as_ref() { let authdate = author.time.format(gix::date::time::format::ISO8601); @@ -83,21 +93,37 @@ impl EditablePatchDescription { } let message = self.message.trim_end_matches('\n'); write!(stream, "\n{message}\n")?; - if let Some(instruction) = self.instruction { - write!(stream, "\n{instruction}")?; + if let Some(instruction) = &self.instruction { + writeln!(stream)?; + for line in instruction.lines() { + writeln!(stream, "{} {}", comment_symbol, line)?; + } } else { writeln!(stream)?; } if let Some(diff) = self.diff.as_ref() { - if let Some(diff_instruction) = self.diff_instruction { - write!(stream, "{diff_instruction}")?; + if let Some(diff_instruction) = &self.diff_instruction { + for line in diff_instruction.lines() { + writeln!(stream, "{} {}", comment_symbol, line)?; + } } - stream.write_all(CUT_LINE.as_bytes())?; - writeln!(stream, "# Do not modify or remove the line above.")?; + writeln!(stream, "{} {}", comment_symbol, CUT_LINE)?; + writeln!( + stream, + "{} Do not modify or remove the line above.", + comment_symbol + )?; stream.write_all(diff.as_ref())?; } Ok(()) } + + /// Write user-editable patch description to the provided stream. + /// + /// Uses the comment symbol stored in the struct. + pub(super) fn write(&self, stream: &mut S) -> Result<()> { + self.write_with_comment_symbol(stream, &self.comment_symbol) + } } /// Patch details read-back after the user interactively edits a @@ -141,22 +167,29 @@ pub(super) struct EditedPatchDescription { pub diff: Option, } -impl TryFrom<&[u8]> for EditedPatchDescription { - type Error = anyhow::Error; - +impl EditedPatchDescription { /// Attempt to parse user-edited patch description. /// - /// Any lines starting with '#' are treated as comments and discarded, except for - /// the cut line which separates the headers and message from the diff content. + /// Any lines starting with '#' (or the comment_symbol) are treated as comments + /// and discarded, except for the cut line which separates the headers and + /// message from the diff content. /// - /// The "Patch", "Author", and "Date" headers, if present, must be the first three - /// lines of the message. This rigidity is done to allow the message, which follows - /// these headers, to potentially contain strings such as "Patch:". + /// The "Patch", "Author", and "Date" headers, if present, must be the first + /// three lines of the message. This rigidity is done to allow the message, + /// which follows these headers, to potentially contain strings such as + /// "Patch:". /// /// If all headers are absent and the trimmed message is empty, an error is - /// returned. Blanking-out the headers and message is thus a mechanism for the user - /// to abort the interactive edit. - fn try_from(buf: &[u8]) -> Result { + /// returned. Blanking-out the headers and message is thus a mechanism for the + /// user to abort the interactive edit. + pub(super) fn parse_with_comment_symbol(buf: &[u8], comment_symbol: &str) -> Result { + Self::parse_internal(buf, comment_symbol) + } + + fn parse_internal(buf: &[u8], comment_symbol: &str) -> Result { + let comment_bytes = comment_symbol.as_bytes(); + let cut_line = format!("{} {}", comment_symbol, CUT_LINE); + let cut_line_bytes = cut_line.as_bytes(); let mut raw_patchname: Option> = None; let mut raw_author: Option> = None; let mut raw_authdate: Option> = None; @@ -172,10 +205,10 @@ impl TryFrom<&[u8]> for EditedPatchDescription { .enumerate() { pos += line.len(); - if line.starts_with(CUT_LINE.as_bytes()) { + if line.starts_with(cut_line_bytes) { consume_diff = true; break; - } else if line.starts_with(b"#") { + } else if line.starts_with(comment_bytes) { continue; } @@ -256,7 +289,7 @@ impl TryFrom<&[u8]> for EditedPatchDescription { Some(if let Some(name_email) = maybe_author { let (name, email) = super::parse::parse_name_email(&name_email)?; let time = if let Some(Some(date_str)) = raw_authdate { - gix::date::Time::parse_time(&date_str).context("patch description date")? + gix::date::Time::parse_time(&date_str).context("parsing author date")? } else { gix::date::Time::now_local_or_utc() }; @@ -283,7 +316,7 @@ impl TryFrom<&[u8]> for EditedPatchDescription { let diff = if consume_diff { // Skip any comment lines after the cut line. for line in buf[pos..].split_inclusive(|&b| b == b'\n') { - if line.starts_with(b"#") { + if line.starts_with(comment_bytes) { pos += line.len(); } else { break; @@ -400,9 +433,10 @@ mod tests { time: gix::date::Time::new(987654321, -3600), }), message: "".to_string(), - instruction: Some("# Instruction\n"), + instruction: Some("Instruction\n".to_string()), diff_instruction: None, diff: None, + comment_symbol: "#".to_string(), }; let mut buf: Vec = vec![]; @@ -419,7 +453,8 @@ mod tests { # Instruction\n", ); - let edited = EditedPatchDescription::try_from(buf.as_slice()).unwrap(); + let edited = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), "#").unwrap(); compare_patch_descs(&edited, &editable); } @@ -434,9 +469,10 @@ mod tests { time: gix::date::Time::new(987654321, 21600), }), message: "Subject\n".to_string(), - instruction: Some("# Instruction\n"), + instruction: Some("Instruction\n".to_string()), diff_instruction: None, diff: None, + comment_symbol: "#".to_string(), }; let mut buf: Vec = vec![]; @@ -453,7 +489,8 @@ mod tests { # Instruction\n", ); - let edited_desc = EditedPatchDescription::try_from(buf.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), "#").unwrap(); compare_patch_descs(&edited_desc, &patch_desc); } @@ -475,9 +512,10 @@ mod tests { With-a-trailer: yes\n\ " .to_string(), - instruction: Some("# Instruction\n"), + instruction: Some("Instruction\n".to_string()), diff_instruction: None, diff: None, + comment_symbol: "#".to_string(), }; let mut buf: Vec = vec![]; @@ -499,7 +537,8 @@ mod tests { # Instruction\n", ); - let edited_desc = EditedPatchDescription::try_from(buf.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), "#").unwrap(); compare_patch_descs(&edited_desc, &patch_desc); } @@ -514,8 +553,8 @@ mod tests { time: gix::date::Time::new(987654321, 21600), }), message: "Subject\n".to_string(), - instruction: Some("# Instruction\n"), - diff_instruction: Some("# Diff instruction\n"), + instruction: Some("Instruction\n".to_string()), + diff_instruction: Some("Diff instruction\n".to_string()), diff: Some(DiffBuffer(BString::from( "\n\ Some stuff before first diff --git\n\ @@ -529,6 +568,7 @@ mod tests { +goodbye\n\ \\ No newline at end of file\n", ))), + comment_symbol: "#".to_string(), }; let mut buf: Vec = vec![]; @@ -559,7 +599,8 @@ mod tests { \\ No newline at end of file\n", ); - let edited_desc = EditedPatchDescription::try_from(buf.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), "#").unwrap(); compare_patch_descs(&edited_desc, &pd); } @@ -580,9 +621,10 @@ mod tests { With-a-trailer: yes\n\ " .to_string(), - instruction: Some("# Instruction\n"), + instruction: Some("Instruction\n".to_string()), diff_instruction: None, diff: None, + comment_symbol: "#".to_string(), }; let mut buf: Vec = vec![]; @@ -619,7 +661,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(edited.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), "#").unwrap(); compare_patch_descs(&edited_desc, &patch_desc); } @@ -635,7 +678,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); let expected = EditedPatchDescription { patchname: None, @@ -662,7 +706,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); let commented_time = gix::date::Time::new(987654321, 21600); assert!(edited_desc.author.is_some()); @@ -700,7 +745,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); let expected = EditedPatchDescription { patchname: Some(Some("patch".parse::().unwrap())), @@ -728,7 +774,9 @@ mod tests { \n\ # Instruction\n"; - assert!(EditedPatchDescription::try_from(description.as_slice()).is_err()); + assert!( + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").is_err() + ); } #[test] @@ -741,7 +789,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); assert_eq!(edited_desc.message, "Subject\n"); } @@ -758,7 +807,8 @@ mod tests { \n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); assert_eq!(edited_desc.message, "Subject\n"); } @@ -773,7 +823,8 @@ mod tests { Subject\n\ # Instruction\n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); assert_eq!(edited_desc.message, "Subject\n"); } @@ -787,7 +838,8 @@ mod tests { \n\ Subject"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); assert_eq!(edited_desc.message, "Subject\n"); } @@ -812,7 +864,8 @@ mod tests { # Instruction\n\ \n"; - let edited_desc = EditedPatchDescription::try_from(description.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(description.as_slice(), "#").unwrap(); assert_eq!( edited_desc.message, @@ -836,7 +889,7 @@ mod tests { b"# ------------------------ >8 ------------------------ \n", b"# ------------------------ >8 ------------------------\n \n", ] { - let result = EditedPatchDescription::try_from(description); + let result = EditedPatchDescription::parse_with_comment_symbol(description, "#"); assert!(result.is_err()); } } @@ -849,7 +902,8 @@ mod tests { Body1.\n\ Body2.\n"; - let edited_desc = EditedPatchDescription::try_from(edited.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), "#").unwrap(); assert!(edited_desc.patchname.is_none()); assert!(edited_desc.author.is_none()); @@ -869,7 +923,8 @@ mod tests { Body1.\n\ Body2.\n"; - let edited_desc = EditedPatchDescription::try_from(edited.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), "#").unwrap(); assert!(edited_desc.patchname.unwrap().is_none()); assert!(edited_desc.author.unwrap().is_none()); @@ -887,7 +942,8 @@ mod tests { ---\n\ Extra.\n"; - let edited_desc = EditedPatchDescription::try_from(edited.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), "#").unwrap(); assert!(edited_desc.patchname.is_none()); assert!(edited_desc.author.is_none()); @@ -917,7 +973,8 @@ mod tests { ---\n\ Extra.\n"; - let edited_desc = EditedPatchDescription::try_from(edited.as_slice()).unwrap(); + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), "#").unwrap(); assert!(edited_desc.patchname.unwrap().is_none()); assert!(edited_desc.author.unwrap().is_none()); @@ -932,4 +989,85 @@ mod tests { ); assert!(edited_desc.diff.is_none()); } + + #[test] + fn parse_with_custom_comment_symbol() { + let edited = b"\ + Patch: patch\n\ + Author: The Author \n\ + Date: 2001-04-19 10:25:21 +0600\n\ + \n\ + Subject\n\ + %% Comment\n\ + Body 1.\n\ + %% Instruction\n"; + + let comment_symbol = "%%"; + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(edited.as_slice(), comment_symbol) + .unwrap(); + + assert_eq!(edited_desc.message, "Subject\nBody 1.\n"); + } + #[test] + fn write_with_custom_comment_symbol() { + let pd = EditablePatchDescription { + patchname: Some("patch".parse::().unwrap()), + author: Some(gix::actor::Signature { + name: BString::from("The Author"), + email: BString::from("author@example.com"), + time: gix::date::Time::new(987654321, 21600), + }), + message: "Subject\n".to_string(), + instruction: Some("Instruction\n".to_string()), + diff_instruction: Some("Diff instruction\n".to_string()), + diff: Some(DiffBuffer(BString::from( + "\n\ + Some stuff before first diff --git\n\ + \n\ + diff --git a/foo.txt b/foo.txt\n\ + index ce01362..a21e91b 100644\n\ + --- a/foo.txt\n\ + +++ b/foo.txt\n\ + @@ -1 +1 @@\n\ + -hello\n\ + +goodbye\n\ + \\ No newline at end of file\n", + ))), + comment_symbol: "%%".to_string(), + }; + + let mut buf: Vec = vec![]; + pd.write(&mut buf).unwrap(); + + assert_eq!( + buf.to_str().unwrap(), + "Patch: patch\n\ + Author: The Author \n\ + Date: 2001-04-19 10:25:21 +0600\n\ + \n\ + Subject\n\ + \n\ + %% Instruction\n\ + %% Diff instruction\n\ + %% ------------------------ >8 ------------------------\n\ + %% Do not modify or remove the line above.\n\ + \n\ + Some stuff before first diff --git\n\ + \n\ + diff --git a/foo.txt b/foo.txt\n\ + index ce01362..a21e91b 100644\n\ + --- a/foo.txt\n\ + +++ b/foo.txt\n\ + @@ -1 +1 @@\n\ + -hello\n\ + +goodbye\n\ + \\ No newline at end of file\n", + ); + + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), "%%").unwrap(); + + compare_patch_descs(&edited_desc, &pd); + } } diff --git a/src/patch/edit/interactive.rs b/src/patch/edit/interactive.rs index f7c84a65..11a10719 100644 --- a/src/patch/edit/interactive.rs +++ b/src/patch/edit/interactive.rs @@ -14,15 +14,18 @@ use bstr::BString; use super::description::{EditablePatchDescription, EditedPatchDescription}; -pub(crate) static EDIT_INSTRUCTION: &str = "\ - # Please enter the message for your patch. Lines starting with\n\ - # '#' will be ignored. An empty message aborts the new patch.\n\ - # The patch name and author information may also be modified.\n"; - -pub(crate) static EDIT_INSTRUCTION_READ_ONLY_DIFF: &str = - "# The diff below is for reference. Any edits will be ignored.\n"; +pub(super) fn edit_instruction(comment_symbol: &str) -> String { + format!( + "Please enter the message for your patch. Lines starting with\n\ + '{}' will be ignored. An empty message aborts the new patch.\n\ + The patch name and author information may also be modified.\n", + comment_symbol + ) +} -pub(crate) static EDIT_INSTRUCTION_EDITABLE_DIFF: &str = "# The diff below may be edited.\n"; +pub(super) const EDIT_INSTRUCTION_READ_ONLY_DIFF: &str = + "The diff below is for reference. Any edits will be ignored.\n"; +pub(super) const EDIT_INSTRUCTION_EDITABLE_DIFF: &str = "The diff below may be edited.\n"; /// Default file name for interactively editable patch description. static EDIT_FILE_NAME: &str = ".stgit-edit.txt"; @@ -38,6 +41,9 @@ pub(super) fn edit_interactive( patch_desc: &EditablePatchDescription, config: &gix::config::Snapshot, ) -> Result { + use crate::ext::RepositoryExtended; + + let comment_symbol = config.repo.get_comment_symbol(); let filename = if patch_desc.diff.is_some() { EDIT_FILE_NAME_DIFF } else { @@ -47,11 +53,12 @@ pub(super) fn edit_interactive( { let file = File::create(filename)?; let mut stream = BufWriter::new(file); - patch_desc.write(&mut stream)?; + patch_desc.write_with_comment_symbol(&mut stream, &comment_symbol)?; } let buf = call_editor(filename, config)?; - let edited_desc = EditedPatchDescription::try_from(buf.as_slice())?; + let edited_desc = + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), &comment_symbol)?; Ok(edited_desc) } diff --git a/src/patch/edit/mod.rs b/src/patch/edit/mod.rs index df520fd3..c614b209 100644 --- a/src/patch/edit/mod.rs +++ b/src/patch/edit/mod.rs @@ -241,6 +241,7 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { let config = repo.config_snapshot(); let default_committer = repo.get_committer()?; + let comment_symbol = repo.get_comment_symbol(); let EditedPatchDescription { patchname: file_patchname, author: file_author, @@ -250,9 +251,12 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { if file_os.to_str() == Some("-") { let mut buf: Vec = Vec::with_capacity(8192); std::io::stdin().read_to_end(&mut buf)?; - EditedPatchDescription::try_from(buf.as_slice())? + EditedPatchDescription::parse_with_comment_symbol(buf.as_slice(), &comment_symbol)? } else { - EditedPatchDescription::try_from(std::fs::read(file_os)?.as_slice())? + EditedPatchDescription::parse_with_comment_symbol( + std::fs::read(file_os)?.as_slice(), + &comment_symbol, + )? } } else { EditedPatchDescription::default() // i.e. all Nones @@ -412,12 +416,12 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { let need_commit_msg_hook = !matches.get_flag("no-verify") && (need_interactive_edit || is_message_modified()); - let instruction = Some(interactive::EDIT_INSTRUCTION); - let diff_instruction = Some(if allow_diff_edit { - interactive::EDIT_INSTRUCTION_EDITABLE_DIFF + let instruction = Some(interactive::edit_instruction(&comment_symbol)); + let diff_instruction = if allow_diff_edit { + Some(interactive::EDIT_INSTRUCTION_EDITABLE_DIFF.to_string()) } else { - interactive::EDIT_INSTRUCTION_READ_ONLY_DIFF - }); + Some(interactive::EDIT_INSTRUCTION_READ_ONLY_DIFF.to_string()) + }; if allow_template_save && matches.contains_id("save-template") { let message = message.decode()?.to_string(); @@ -425,9 +429,10 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { patchname, author, message, - instruction, - diff_instruction, + instruction: instruction.clone(), + diff_instruction: diff_instruction.clone(), diff, + comment_symbol: comment_symbol.clone(), }; let path = matches.get_one::("save-template").unwrap().clone(); if path.to_str() == Some("-") { @@ -445,9 +450,10 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { patchname, author, message: message.decode()?.to_string(), - instruction, - diff_instruction, + instruction: instruction.clone(), + diff_instruction: diff_instruction.clone(), diff, + comment_symbol: comment_symbol.clone(), }; let EditedPatchDescription { @@ -509,8 +515,9 @@ impl<'a, 'repo> EditBuilder<'a, 'repo> { author, message: message.decode()?.to_string(), instruction, - diff_instruction, + diff_instruction: diff_instruction.clone(), diff, + comment_symbol, }; failed_patch_description.write(&mut stream)?; return Err(anyhow!( diff --git a/t/t3306-edit-comment-char.sh b/t/t3306-edit-comment-char.sh new file mode 100755 index 00000000..a336bf55 --- /dev/null +++ b/t/t3306-edit-comment-char.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='Test "stg edit" respects core.commentChar configuration' + +. ./test-lib.sh + +test_expect_success 'Initialize repo' ' + test_commit_bulk --message="p%s" 2 && + stg uncommit -n 2 && + stg pop -a +' + +test_expect_success 'Default comment char (#)' ' + stg push p1 && + stg edit --diff --save-template - p1 >output && + grep "^# Please enter the message for your patch" output && + grep "^# ------------------------ >8 ------------------------" output && + grep "^# Do not modify or remove the line above" output +' + +test_expect_success 'Custom comment char (;)' ' + test_config core.commentChar ";" && + stg edit --diff --save-template - p1 >output && + grep "^; Please enter the message for your patch" output && + grep "^; ------------------------ >8 ------------------------" output && + grep "^; Do not modify or remove the line above" output && + ! grep "^# Please enter the message" output +' + +test_expect_success 'Custom comment char (%)' ' + test_config core.commentChar "%" && + stg edit --diff --save-template - p1 >output && + grep "^% Please enter the message for your patch" output && + grep "^% ------------------------ >8 ------------------------" output && + grep "^% Do not modify or remove the line above" output && + ! grep "^# Please enter the message" output +' + +test_expect_success 'Comment char "auto" defaults to #' ' + test_config core.commentChar "auto" && + stg edit --diff --save-template - p1 >output && + grep "^# Please enter the message for your patch" output && + grep "^# ------------------------ >8 ------------------------" output && + grep "^# Do not modify or remove the line above" output +' + +test_expect_success 'Multi-character comment string' ' + test_config core.commentChar "//" && + stg edit --diff --save-template - p1 >output && + grep "^// Please enter the message for your patch" output && + grep "^// ------------------------ >8 ------------------------" output && + grep "^// Do not modify or remove the line above" output +' + +test_done