1- use std:: path:: Path ;
1+ use std:: path:: { Path , PathBuf } ;
22use std:: process:: { Command , Stdio } ;
33
44pub struct GitConfig < ' a > {
@@ -96,10 +96,7 @@ pub fn updated_master_branch(
9696 Err ( "Cannot find any suitable upstream master branch" . to_owned ( ) )
9797}
9898
99- pub fn get_git_merge_base (
100- config : & GitConfig < ' _ > ,
101- git_dir : Option < & Path > ,
102- ) -> Result < String , String > {
99+ fn get_git_merge_base ( config : & GitConfig < ' _ > , git_dir : Option < & Path > ) -> Result < String , String > {
103100 let updated_master = updated_master_branch ( config, git_dir) ?;
104101 let mut git = Command :: new ( "git" ) ;
105102 if let Some ( git_dir) = git_dir {
@@ -108,6 +105,37 @@ pub fn get_git_merge_base(
108105 Ok ( output_result ( git. arg ( "merge-base" ) . arg ( & updated_master) . arg ( "HEAD" ) ) ?. trim ( ) . to_owned ( ) )
109106}
110107
108+ /// Resolves the closest merge commit by the given `author` and `target_paths`.
109+ ///
110+ /// If it fails to find the commit from upstream using `git merge-base`, fallbacks to HEAD.
111+ pub fn get_closest_merge_commit (
112+ git_dir : Option < & Path > ,
113+ config : & GitConfig < ' _ > ,
114+ target_paths : & [ PathBuf ] ,
115+ ) -> Result < String , String > {
116+ let mut git = Command :: new ( "git" ) ;
117+
118+ if let Some ( git_dir) = git_dir {
119+ git. current_dir ( git_dir) ;
120+ }
121+
122+ let merge_base = get_git_merge_base ( config, git_dir) . unwrap_or_else ( |_| "HEAD" . into ( ) ) ;
123+
124+ git. args ( [
125+ "rev-list" ,
126+ & format ! ( "--author={}" , config. git_merge_commit_email) ,
127+ "-n1" ,
128+ "--first-parent" ,
129+ & merge_base,
130+ ] ) ;
131+
132+ if !target_paths. is_empty ( ) {
133+ git. arg ( "--" ) . args ( target_paths) ;
134+ }
135+
136+ Ok ( output_result ( & mut git) ?. trim ( ) . to_owned ( ) )
137+ }
138+
111139/// Returns the files that have been modified in the current branch compared to the master branch.
112140/// The `extensions` parameter can be used to filter the files by their extension.
113141/// Does not include removed files.
@@ -117,7 +145,7 @@ pub fn get_git_modified_files(
117145 git_dir : Option < & Path > ,
118146 extensions : & [ & str ] ,
119147) -> Result < Option < Vec < String > > , String > {
120- let merge_base = get_git_merge_base ( config, git_dir ) ?;
148+ let merge_base = get_closest_merge_commit ( git_dir , config, & [ ] ) ?;
121149
122150 let mut git = Command :: new ( "git" ) ;
123151 if let Some ( git_dir) = git_dir {
0 commit comments