@@ -34,10 +34,34 @@ pub(super) fn recursive(
3434 delegate : & mut dyn Delegate ,
3535 out : & mut Outcome ,
3636 state : & mut State ,
37+ untracked_cache : Option < & walk:: untracked_cache:: Validated < ' _ > > ,
38+ untracked_cache_dir : Option < usize > ,
3739) -> Result < ( Action , bool ) , Error > {
3840 if ctx. should_interrupt . is_some_and ( |flag| flag. load ( Ordering :: Relaxed ) ) {
3941 return Err ( Error :: Interrupted ) ;
4042 }
43+ if let Some ( ( action, prevent_collapse) ) = untracked_cache
44+ . zip ( untracked_cache_dir)
45+ . filter ( |( cache, dir) | cache. is_dir_valid ( * dir, current) )
46+ . map ( |( cache, dir) | {
47+ recursive_from_untracked_cache (
48+ dir,
49+ may_collapse,
50+ current,
51+ current_bstr,
52+ current_info,
53+ ctx,
54+ opts,
55+ delegate,
56+ out,
57+ state,
58+ cache,
59+ )
60+ } )
61+ . transpose ( ) ?
62+ {
63+ return Ok ( ( action, prevent_collapse) ) ;
64+ }
4165 out. read_dir_calls += 1 ;
4266 let entries = gix_fs:: read_dir ( current, opts. precompose_unicode ) . map_err ( |err| Error :: ReadDir {
4367 path : current. to_owned ( ) ,
@@ -96,6 +120,15 @@ pub(super) fn recursive(
96120 delegate,
97121 out,
98122 state,
123+ untracked_cache,
124+ untracked_cache_dir. and_then ( |dir| {
125+ untracked_cache. and_then ( |cache| {
126+ let component = current_bstr
127+ . rfind_byte ( b'/' )
128+ . map_or ( current_bstr. as_bstr ( ) , |pos| current_bstr[ pos + 1 ..] . as_bstr ( ) ) ;
129+ cache. child_dir ( dir, component)
130+ } )
131+ } ) ,
99132 ) ?;
100133 prevent_collapse |= subdir_prevent_collapse;
101134 if action. is_break ( ) {
@@ -141,6 +174,126 @@ pub(super) fn recursive(
141174 Ok ( ( res, prevent_collapse) )
142175}
143176
177+ #[ allow( clippy:: too_many_arguments) ]
178+ fn recursive_from_untracked_cache (
179+ cache_dir : usize ,
180+ may_collapse : bool ,
181+ current : & mut PathBuf ,
182+ current_bstr : & mut BString ,
183+ current_info : classify:: Outcome ,
184+ ctx : & mut Context < ' _ > ,
185+ opts : Options < ' _ > ,
186+ delegate : & mut dyn Delegate ,
187+ out : & mut Outcome ,
188+ state : & mut State ,
189+ untracked_cache : & walk:: untracked_cache:: Validated < ' _ > ,
190+ ) -> Result < ( Action , bool ) , Error > {
191+ let Some ( cached) = untracked_cache. directory ( cache_dir) else {
192+ return Ok ( ( std:: ops:: ControlFlow :: Continue ( ( ) ) , false ) ) ;
193+ } ;
194+
195+ let mut num_entries = 0 ;
196+ let mark = state. mark ( may_collapse) ;
197+ let mut prevent_collapse = current_info. status == Status :: Tracked ;
198+
199+ for & subdir_idx in cached. sub_directories ( ) {
200+ let Some ( subdir) = untracked_cache. directory ( subdir_idx) else {
201+ continue ;
202+ } ;
203+ num_entries += 1 ;
204+ let prev_len = current_bstr. len ( ) ;
205+ if prev_len != 0 {
206+ current_bstr. push ( b'/' ) ;
207+ }
208+ current_bstr. extend_from_slice ( subdir. name ( ) ) ;
209+ current. push ( gix_path:: from_bstr ( subdir. name ( ) ) ) ;
210+
211+ let info = classify:: path (
212+ current,
213+ current_bstr,
214+ if prev_len == 0 { 0 } else { prev_len + 1 } ,
215+ Some ( entry:: Kind :: Directory ) ,
216+ || Some ( entry:: Kind :: Directory ) ,
217+ opts,
218+ ctx,
219+ ) ?;
220+ if can_recurse ( current_bstr. as_bstr ( ) , info, opts. for_deletion , false , delegate) {
221+ let subdir_may_collapse = state. may_collapse ( current) ;
222+ let ( action, subdir_prevent_collapse) = recursive (
223+ subdir_may_collapse,
224+ current,
225+ current_bstr,
226+ info,
227+ ctx,
228+ opts,
229+ delegate,
230+ out,
231+ state,
232+ Some ( untracked_cache) ,
233+ Some ( subdir_idx) ,
234+ ) ?;
235+ prevent_collapse |= subdir_prevent_collapse;
236+ if action. is_break ( ) {
237+ return Ok ( ( action, prevent_collapse) ) ;
238+ }
239+ } else if !state. held_for_directory_collapse ( current_bstr. as_bstr ( ) , info, & opts) {
240+ let action = emit_entry ( Cow :: Borrowed ( current_bstr. as_bstr ( ) ) , info, None , opts, out, delegate) ;
241+ if action. is_break ( ) {
242+ return Ok ( ( action, prevent_collapse) ) ;
243+ }
244+ }
245+ current_bstr. truncate ( prev_len) ;
246+ current. pop ( ) ;
247+ }
248+
249+ for file in cached. untracked_entries ( ) {
250+ num_entries += 1 ;
251+ let prev_len = current_bstr. len ( ) ;
252+ if prev_len != 0 {
253+ current_bstr. push ( b'/' ) ;
254+ }
255+ current_bstr. extend_from_slice ( file. as_ref ( ) ) ;
256+ current. push ( gix_path:: from_bstr ( file. as_bstr ( ) ) ) ;
257+ let current_path = current. clone ( ) ;
258+
259+ let info = classify:: path (
260+ current,
261+ current_bstr,
262+ if prev_len == 0 { 0 } else { prev_len + 1 } ,
263+ None ,
264+ || {
265+ std:: fs:: symlink_metadata ( & current_path)
266+ . ok ( )
267+ . map ( |ft| ft. file_type ( ) . into ( ) )
268+ } ,
269+ opts,
270+ ctx,
271+ ) ?;
272+ if !state. held_for_directory_collapse ( current_bstr. as_bstr ( ) , info, & opts) {
273+ let action = emit_entry ( Cow :: Borrowed ( current_bstr. as_bstr ( ) ) , info, None , opts, out, delegate) ;
274+ if action. is_break ( ) {
275+ return Ok ( ( action, prevent_collapse) ) ;
276+ }
277+ }
278+ current_bstr. truncate ( prev_len) ;
279+ current. pop ( ) ;
280+ }
281+
282+ let res = mark. reduce_held_entries (
283+ num_entries,
284+ state,
285+ & mut prevent_collapse,
286+ current,
287+ current_bstr. as_bstr ( ) ,
288+ current_info,
289+ opts,
290+ out,
291+ ctx,
292+ delegate,
293+ ) ;
294+ Ok ( ( res, prevent_collapse) )
295+ }
296+
144297pub ( super ) struct State {
145298 /// The entries to hold back until it's clear what to do with them.
146299 pub on_hold : Vec < Entry > ,
0 commit comments