@@ -143,14 +143,13 @@ pub fn check_path_modifications(
143143
144144/// Returns true if any of the passed `paths` have changed since the `base` commit.
145145pub fn has_changed_since ( git_dir : & Path , base : & str , paths : & [ & str ] ) -> bool {
146- let mut git = Command :: new ( "git" ) ;
147- git . current_dir ( git_dir ) ;
146+ run_git_diff_index ( Some ( git_dir ) , |cmd| {
147+ cmd . args ( [ "--quiet" , base , "--" ] ) . args ( paths ) ;
148148
149- git. args ( [ "diff-index" , "--quiet" , base, "--" ] ) . args ( paths) ;
150-
151- // Exit code 0 => no changes
152- // Exit code 1 => some changes were detected
153- !git. status ( ) . expect ( "cannot run git diff-index" ) . success ( )
149+ // Exit code 0 => no changes
150+ // Exit code 1 => some changes were detected
151+ !cmd. status ( ) . expect ( "cannot run git diff-index" ) . success ( )
152+ } )
154153}
155154
156155/// Returns the latest upstream commit that modified `target_paths`, or `None` if no such commit
@@ -267,31 +266,49 @@ pub fn get_git_modified_files(
267266 return Err ( "No upstream commit was found" . to_string ( ) ) ;
268267 } ;
269268
270- let mut git = Command :: new ( "git" ) ;
271- if let Some ( git_dir) = git_dir {
272- git. current_dir ( git_dir) ;
273- }
274- let files = output_result ( git. args ( [ "diff-index" , "--name-status" , merge_base. trim ( ) ] ) ) ?
275- . lines ( )
276- . filter_map ( |f| {
277- let ( status, name) = f. trim ( ) . split_once ( char:: is_whitespace) . unwrap ( ) ;
278- if status == "D" {
279- None
280- } else if Path :: new ( name) . extension ( ) . map_or ( extensions. is_empty ( ) , |ext| {
281- // If there is no extension, we allow the path if `extensions` is empty
282- // If there is an extension, we allow it if `extension` is empty or it contains the
283- // extension.
284- extensions. is_empty ( ) || extensions. contains ( & ext. to_str ( ) . unwrap ( ) )
285- } ) {
286- Some ( name. to_owned ( ) )
287- } else {
288- None
289- }
290- } )
291- . collect ( ) ;
269+ let files = run_git_diff_index ( git_dir, |cmd| {
270+ output_result ( cmd. args ( [ "--name-status" , merge_base. trim ( ) ] ) )
271+ } ) ?
272+ . lines ( )
273+ . filter_map ( |f| {
274+ let ( status, name) = f. trim ( ) . split_once ( char:: is_whitespace) . unwrap ( ) ;
275+ if status == "D" {
276+ None
277+ } else if Path :: new ( name) . extension ( ) . map_or ( extensions. is_empty ( ) , |ext| {
278+ // If there is no extension, we allow the path if `extensions` is empty
279+ // If there is an extension, we allow it if `extension` is empty or it contains the
280+ // extension.
281+ extensions. is_empty ( ) || extensions. contains ( & ext. to_str ( ) . unwrap ( ) )
282+ } ) {
283+ Some ( name. to_owned ( ) )
284+ } else {
285+ None
286+ }
287+ } )
288+ . collect ( ) ;
292289 Ok ( files)
293290}
294291
292+ /// diff-index can return outdated information, because it does not update the git index.
293+ /// This function uses `update-index` to update the index first, and then provides `func` with a
294+ /// command prepared to run `git diff-index`.
295+ fn run_git_diff_index < F , T > ( git_dir : Option < & Path > , func : F ) -> T
296+ where
297+ F : FnOnce ( & mut Command ) -> T ,
298+ {
299+ let git = || {
300+ let mut git = Command :: new ( "git" ) ;
301+ if let Some ( git_dir) = git_dir {
302+ git. current_dir ( git_dir) ;
303+ }
304+ git
305+ } ;
306+
307+ // We ignore the exit code, as it errors out when some files are modified.
308+ let _ = output_result ( git ( ) . args ( [ "update-index" , "--refresh" , "-q" ] ) ) ;
309+ func ( git ( ) . arg ( "diff-index" ) )
310+ }
311+
295312/// Returns the files that haven't been added to git yet.
296313pub fn get_git_untracked_files ( git_dir : Option < & Path > ) -> Result < Option < Vec < String > > , String > {
297314 let mut git = Command :: new ( "git" ) ;
0 commit comments