diff --git a/libra/src/command/commit.rs b/libra/src/command/commit.rs index 8caa16f98..faf46c34d 100644 --- a/libra/src/command/commit.rs +++ b/libra/src/command/commit.rs @@ -13,7 +13,7 @@ use mercury::internal::object::commit::Commit; use mercury::internal::object::tree::{Tree, TreeItem, TreeItemMode}; use mercury::internal::object::ObjectTrait; -use super::save_object; +use super::{format_commit_msg, save_object}; #[derive(Parser, Debug)] pub struct CommitArgs { @@ -38,7 +38,8 @@ pub async fn execute(args: CommitArgs) { /* Create & save commit objects */ let parents_commit_ids = get_parents_ids().await; - let commit = Commit::from_tree_id(tree.id, parents_commit_ids, args.message.as_str()); + // There must be a `blank line`(\n) before `message`, or remote unpack failed + let commit = Commit::from_tree_id(tree.id, parents_commit_ids, &format_commit_msg(&args.message, None)); // TODO default signature created in `from_tree_id`, wait `git config` to set correct user info diff --git a/libra/src/command/log.rs b/libra/src/command/log.rs index cdc1c89df..264446495 100644 --- a/libra/src/command/log.rs +++ b/libra/src/command/log.rs @@ -15,6 +15,8 @@ use std::collections::VecDeque; use std::str::FromStr; use mercury::hash::SHA1; use mercury::internal::object::commit::Commit; + +use super::parse_commit_msg; #[derive(Parser, Debug)] pub struct LogArgs { /// Limit the number of output @@ -103,7 +105,8 @@ pub async fn execute(args: LogArgs) { message }; message.push_str(&format!("\nAuthor: {}", commit.author)); - message.push_str(&format!("\n{}\n", commit.message)); + let (msg, _) = parse_commit_msg(&commit.message); + message.push_str(&format!("\n{}\n", msg)); #[cfg(unix)] { diff --git a/libra/src/command/mod.rs b/libra/src/command/mod.rs index 2b3378dcc..9116810e1 100644 --- a/libra/src/command/mod.rs +++ b/libra/src/command/mod.rs @@ -9,11 +9,11 @@ pub mod log; pub mod merge; pub mod pull; pub mod push; +pub mod remote; pub mod remove; pub mod restore; pub mod status; pub mod switch; -pub mod remote; use crate::internal::protocol::https_client::BasicAuth; use crate::utils::util; @@ -65,6 +65,52 @@ pub fn ask_basic_auth() -> BasicAuth { BasicAuth { username, password } } +/// Format commit message with GPG signature
+/// There must be a `blank line`(\n) before `message`, or remote unpack failed.
+/// If there is `GPG signature`, +/// `blank line` should be placed between `signature` and `message` +pub fn format_commit_msg(msg: &str, gpg_sig: Option<&str>) -> String { + match gpg_sig { + None => { + format!("\n{}", msg) + } + Some(gpg) => { + format!("{}\n\n{}", gpg, msg) + } + } +} +/// parse commit message +pub fn parse_commit_msg(msg_gpg: &str) -> (String, Option) { + const GPG_SIG_START: &str = "gpgsig -----BEGIN PGP SIGNATURE-----"; + const GPG_SIG_END: &str = "-----END PGP SIGNATURE-----"; + let gpg_start = msg_gpg.find(GPG_SIG_START); + let gpg_end = msg_gpg.find(GPG_SIG_END).map(|end| end + GPG_SIG_END.len()); + let gpg_sig = match (gpg_start, gpg_end) { + (Some(start), Some(end)) => { + if start < end { + Some(msg_gpg[start..end].to_string()) + } else { + None + } + } + _ => None, + }; + match gpg_sig { + Some(gpg) => { + // skip the leading '\n\n' (blank line) + let msg = msg_gpg[gpg_end.unwrap()..].to_string(); + assert!(msg.starts_with("\n\n"), "commit message format error"); + let msg = msg[2..].to_string(); + (msg, Some(gpg)) + } + None => { + assert!(msg_gpg.starts_with('\n'), "commit message format error"); + let msg = msg_gpg[1..].to_string(); // skip the leading '\n' (blank line) + (msg, None) + } + } +} + #[cfg(test)] mod test { use mercury::internal::object::commit::Commit; @@ -78,4 +124,19 @@ mod test { save_object(&object, &object.id).unwrap(); let _ = load_object::(&object.id).unwrap(); } + + #[test] + fn test_format_and_parse_commit_msg() { + let msg = "commit message"; + let gpg_sig = "gpgsig -----BEGIN PGP SIGNATURE-----\ncontent\n-----END PGP SIGNATURE-----"; + let msg_gpg = format_commit_msg(msg, Some(gpg_sig)); + let (msg_, gpg_sig_) = parse_commit_msg(&msg_gpg); + assert_eq!(msg, msg_); + assert_eq!(gpg_sig, gpg_sig_.unwrap()); + + let msg_gpg = format_commit_msg(msg, None); + let (msg_, gpg_sig_) = parse_commit_msg(&msg_gpg); + assert_eq!(msg, msg_); + assert_eq!(None, gpg_sig_); + } }