@@ -43,60 +43,103 @@ pub fn get_filenames_diff_at_workdir(repo: &Repository) -> Result<UncommittedCha
4343 . renames_head_to_index ( false )
4444 . renames_index_to_workdir ( false ) ;
4545
46- // Retrieve the current status of the working directory and index
4746 let statuses = repo. statuses ( Some ( & mut options) ) ?;
4847 let mut changes = UncommittedChanges :: default ( ) ;
48+ let workdir = repo. workdir ( ) . expect ( "Bare repo not supported" ) ;
4949
50- // Iterate through each file entry in the status list
5150 for entry in statuses. iter ( ) {
52- let status = entry. status ( ) ;
53- let path = entry. path ( ) . unwrap_or ( "" ) . to_string ( ) ;
54-
55- // Skip unchanged files
56- if status. is_empty ( ) {
57- continue ;
58- }
59-
60- // Record staged changes (index vs HEAD)
61- if status. is_index_modified ( ) {
62- changes. staged . modified . push ( path. clone ( ) ) ;
63- }
64- if status. is_index_new ( ) {
65- changes. staged . added . push ( path. clone ( ) ) ;
66- }
67- if status. is_index_deleted ( ) {
68- changes. staged . deleted . push ( path. clone ( ) ) ;
69- }
70-
71- // Record unstaged changes (workdir vs index)
72- if status. is_wt_modified ( ) {
73- changes. unstaged . modified . push ( path. clone ( ) ) ;
74- }
75- if status. is_wt_new ( ) {
76- changes. unstaged . added . push ( path. clone ( ) ) ;
77- }
78- if status. is_wt_deleted ( ) {
79- changes. unstaged . deleted . push ( path. clone ( ) ) ;
51+ let rel_path = entry. path ( ) . unwrap_or ( "" ) ;
52+ let full_path = workdir. join ( rel_path) ;
53+
54+ // Expand directories
55+ let files = if full_path. is_dir ( ) {
56+ collect_files_for_status ( repo, workdir, rel_path)
57+ } else {
58+ vec ! [ rel_path. to_string( ) ]
59+ } ;
60+
61+ for file in files {
62+
63+ // Ask git for this file’s individual status
64+ let file_status = repo. status_file ( Path :: new ( & file) ) ?;
65+
66+ // Now you can safely check staged vs unstaged per file
67+ if file_status. is_index_modified ( ) {
68+ changes. staged . modified . push ( file. clone ( ) ) ;
69+ }
70+ if file_status. is_index_new ( ) {
71+ changes. staged . added . push ( file. clone ( ) ) ;
72+ }
73+ if file_status. is_index_deleted ( ) {
74+ changes. staged . deleted . push ( file. clone ( ) ) ;
75+ }
76+
77+ if file_status. is_wt_modified ( ) {
78+ changes. unstaged . modified . push ( file. clone ( ) ) ;
79+ }
80+ if file_status. is_wt_new ( ) {
81+ changes. unstaged . added . push ( file. clone ( ) ) ;
82+ }
83+ if file_status. is_wt_deleted ( ) {
84+ changes. unstaged . deleted . push ( file. clone ( ) ) ;
85+ }
8086 }
8187 }
8288
83- // Compute counts of deduplicated filenames
89+ // Counts
8490 changes. modified_count = deduplicate ( & changes. staged . modified , & changes. unstaged . modified ) ;
8591 changes. added_count = deduplicate ( & changes. staged . added , & changes. unstaged . added ) ;
8692 changes. deleted_count = deduplicate ( & changes. staged . deleted , & changes. unstaged . deleted ) ;
8793
88- // Set flags for change states
8994 changes. is_staged = !changes. staged . modified . is_empty ( )
9095 || !changes. staged . added . is_empty ( )
9196 || !changes. staged . deleted . is_empty ( ) ;
97+
9298 changes. is_unstaged = !changes. unstaged . modified . is_empty ( )
9399 || !changes. unstaged . added . is_empty ( )
94100 || !changes. unstaged . deleted . is_empty ( ) ;
101+
95102 changes. is_clean = !changes. is_staged && !changes. is_unstaged ;
96103
97104 Ok ( changes)
98105}
99106
107+ fn collect_files_for_status ( repo : & Repository , workdir : & Path , rel_path : & str ) -> Vec < String > {
108+ let full_path = workdir. join ( rel_path) ;
109+
110+ if full_path. exists ( ) {
111+ if full_path. is_file ( ) {
112+ return vec ! [ rel_path. to_string( ) ] ;
113+ } else if full_path. is_dir ( ) {
114+ let mut result = Vec :: new ( ) ;
115+ if let Ok ( entries) = std:: fs:: read_dir ( & full_path) {
116+ for entry in entries. flatten ( ) {
117+ let path = entry. path ( ) ;
118+ let child_rel = match path. strip_prefix ( workdir) {
119+ Ok ( p) => p. to_string_lossy ( ) . to_string ( ) ,
120+ Err ( _) => continue ,
121+ } ;
122+
123+ // Skip ignored files
124+ if repo. status_should_ignore ( Path :: new ( & child_rel) ) . unwrap_or ( false ) {
125+ continue ;
126+ }
127+
128+ if path. is_file ( ) {
129+ result. push ( child_rel) ;
130+ } else if path. is_dir ( ) {
131+ result. extend ( collect_files_for_status ( repo, workdir, & child_rel) ) ;
132+ }
133+ }
134+ }
135+ return result;
136+ }
137+ }
138+
139+ // If path does not exist (deleted file), just return the rel_path itself
140+ vec ! [ rel_path. to_string( ) ]
141+ }
142+
100143// Lists all files changed in a given commit compared to its parent
101144pub fn get_filenames_diff_at_oid ( repo : & Repository , oid : Oid ) -> Vec < FileChange > {
102145 let commit = repo. find_commit ( oid) . unwrap ( ) ;
0 commit comments