coco/core/git/
commit.rs

1//! Git Commit
2//!
3//! This module provides a way to execute a git commit command.
4use {
5    super::commit_info::commit_info,
6    crate::{
7        core::state::commit::{CommitInfo, ConventionalCommitMessage},
8        fail,
9    },
10    eyre::Result,
11    std::process::Command,
12};
13
14/// Commit the changes to the repository with the given commit message in the directory specified by
15/// `cwd`. If `cwd` is not provided, the current working directory is used.
16///
17/// Returns a `CommitResult` which is either a `Commit` or the error returned by the git command.
18pub fn commit(ccm: &ConventionalCommitMessage, cwd: Option<&str>) -> Result<CommitInfo> {
19    let message = ccm.raw_commit();
20    let cwd = cwd.unwrap_or("./");
21
22    let output = Command::new("git")
23        .arg("commit")
24        .arg("-m")
25        .arg(message)
26        .current_dir(cwd)
27        .output()
28        .expect("Failed to execute git commit");
29
30    // Return the Commit or the error message returned by the git command
31    if output.status.success() {
32        let commit_out = String::from_utf8_lossy(&output.stdout).to_string();
33        let (hash, _) = parse_commit_output(&commit_out)?;
34
35        // get the commit info (git show {hash})
36        commit_info(&hash, Some(cwd))
37    } else {
38        fail!("{}", String::from_utf8_lossy(&output.stderr).to_string())
39    }
40}
41
42/// parse the output of the `git commit` command into a String tuple containing the commit hash and
43/// the branch name where the commit was made.
44fn parse_commit_output(commit_out: &str) -> Result<(String, String)> {
45    for line in commit_out.lines() {
46        if line.starts_with('[') {
47            let parts: Vec<&str> = line.split_whitespace().collect();
48            if parts.len() >= 3 {
49                let branch = parts[0][1..].to_string();
50                let hash = parts[1].trim_end_matches(']').to_string();
51                return Ok((hash, branch));
52            }
53        }
54    }
55
56    fail!("Failed to parse commit output")
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_output_parsing() {
65        let output = [
66            "[master e63e7aa] the first line of the message!",
67            " 1 file changed, 0 insertions(+), 0 deletions(-)",
68            " create mode 100644 wqeqwex.txt",
69        ]
70        .join("\n");
71
72        let (hash, branch) = parse_commit_output(&output).unwrap();
73        assert_eq!(hash, "e63e7aa");
74        assert_eq!(branch, "master");
75    }
76}