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_);
+ }
}