1
- use std:: path:: Path ;
1
+ use std:: path:: { Path , PathBuf } ;
2
2
use std:: process:: { Command , Stdio } ;
3
3
4
4
pub struct GitConfig < ' a > {
@@ -96,10 +96,7 @@ pub fn updated_master_branch(
96
96
Err ( "Cannot find any suitable upstream master branch" . to_owned ( ) )
97
97
}
98
98
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 > {
103
100
let updated_master = updated_master_branch ( config, git_dir) ?;
104
101
let mut git = Command :: new ( "git" ) ;
105
102
if let Some ( git_dir) = git_dir {
@@ -108,6 +105,37 @@ pub fn get_git_merge_base(
108
105
Ok ( output_result ( git. arg ( "merge-base" ) . arg ( & updated_master) . arg ( "HEAD" ) ) ?. trim ( ) . to_owned ( ) )
109
106
}
110
107
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
+
111
139
/// Returns the files that have been modified in the current branch compared to the master branch.
112
140
/// The `extensions` parameter can be used to filter the files by their extension.
113
141
/// Does not include removed files.
@@ -117,7 +145,7 @@ pub fn get_git_modified_files(
117
145
git_dir : Option < & Path > ,
118
146
extensions : & [ & str ] ,
119
147
) -> 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, & [ ] ) ?;
121
149
122
150
let mut git = Command :: new ( "git" ) ;
123
151
if let Some ( git_dir) = git_dir {
0 commit comments