11use regex:: Regex ;
2+ use std:: path:: PathBuf ;
23use std:: { collections:: HashMap , process:: Command } ;
34/// ReadCommitMessageOptions represents the options for reading commit messages.
45/// Transparently, it is defined to be similar to the behavior of the git log command.
@@ -14,6 +15,34 @@ pub struct ReadCommitMessageOptions {
1415 pub to : Option < String > ,
1516}
1617
18+ /// Get the path to the COMMIT_EDITMSG file.
19+ ///
20+ /// Note that we use `git rev-parse --git-path COMMIT_EDITMSG` to resolve the path correctly.
21+ /// This is necessary because in a git worktree, `.git` is a file rather than a directory,
22+ /// and the actual git directory is stored elsewhere.
23+ pub fn edit_msg_path ( cwd : & str ) -> PathBuf {
24+ let output = Command :: new ( "git" )
25+ . current_dir ( cwd)
26+ . arg ( "rev-parse" )
27+ . arg ( "--git-path" )
28+ . arg ( "COMMIT_EDITMSG" )
29+ . output ( ) ;
30+
31+ let git_path = match output {
32+ Ok ( output) if output. status . success ( ) => {
33+ let path_str = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
34+ PathBuf :: from ( path_str)
35+ }
36+ _ => PathBuf :: from ( ".git" ) . join ( "COMMIT_EDITMSG" ) ,
37+ } ;
38+
39+ if git_path. is_absolute ( ) {
40+ git_path
41+ } else {
42+ PathBuf :: from ( cwd) . join ( git_path)
43+ }
44+ }
45+
1746/// Get commit messages from git.
1847pub fn read ( options : ReadCommitMessageOptions ) -> Vec < String > {
1948 // Configure revision range following the git spec.
@@ -138,6 +167,12 @@ pub fn parse_subject(subject: &str) -> (Option<String>, Option<String>, Option<S
138167mod tests {
139168 use super :: * ;
140169
170+ #[ test]
171+ fn test_edit_msg_path ( ) {
172+ let path = edit_msg_path ( "." ) ;
173+ assert ! ( path. to_str( ) . unwrap( ) . contains( "COMMIT_EDITMSG" ) ) ;
174+ }
175+
141176 #[ test]
142177 fn test_single_line_parse_commit_message ( ) {
143178 let input = "feat(cli): add dummy option" ;
0 commit comments