@@ -42,6 +42,11 @@ fn driver_exe() -> String {
4242    exe
4343} 
4444
45+ fn  assure_is_empty ( dir :  impl  AsRef < Path > )  -> std:: io:: Result < ( ) >  { 
46+     assert_eq ! ( std:: fs:: read_dir( dir) ?. count( ) ,  0 ) ; 
47+     Ok ( ( ) ) 
48+ } 
49+ 
4550#[ test]  
4651fn  submodules_are_instantiated_as_directories ( )  -> crate :: Result  { 
4752    let  mut  opts = opts_from_probe ( ) ; 
@@ -57,11 +62,6 @@ fn submodules_are_instantiated_as_directories() -> crate::Result {
5762    Ok ( ( ) ) 
5863} 
5964
60- fn  assure_is_empty ( dir :  impl  AsRef < Path > )  -> std:: io:: Result < ( ) >  { 
61-     assert_eq ! ( std:: fs:: read_dir( dir) ?. count( ) ,  0 ) ; 
62-     Ok ( ( ) ) 
63- } 
64- 
6565#[ test]  
6666fn  accidental_writes_through_symlinks_are_prevented_if_overwriting_is_forbidden ( )  { 
6767    let  mut  opts = opts_from_probe ( ) ; 
@@ -125,7 +125,7 @@ fn writes_through_symlinks_are_prevented_even_if_overwriting_is_allowed() {
125125                if  cfg!( windows)  {  "A-dir\\ a"  }  else {  "A-dir/a"  } , 
126126                "A-file" , 
127127                "FAKE-DIR" , 
128-                 if  cfg! ( windows )   {   "fake-file"   }  else  {   " FAKE-FILE"  } 
128+                 " FAKE-FILE"
129129            ] ) , 
130130        ) ; 
131131        assert ! ( outcome. collisions. is_empty( ) ) ; 
@@ -257,6 +257,30 @@ fn symlinks_become_files_if_disabled() -> crate::Result {
257257    Ok ( ( ) ) 
258258} 
259259
260+ #[ test]  
261+ fn  dangling_symlinks_can_be_created ( )  -> crate :: Result  { 
262+     let  opts = opts_from_probe ( ) ; 
263+     if  !opts. fs . symlink  { 
264+         eprintln ! ( "Skipping dangling symlink test on filesystem that doesn't support it" ) ; 
265+         return  Ok ( ( ) ) ; 
266+     } 
267+ 
268+     let  ( _source_tree,  destination,  _index,  outcome)  =
269+         checkout_index_in_tmp_dir ( opts. clone ( ) ,  "make_dangling_symlink" ) ?; 
270+     let  worktree_files = dir_structure ( & destination) ; 
271+     let  worktree_files_stripped = stripped_prefix ( & destination,  & worktree_files) ; 
272+ 
273+     assert_eq ! ( worktree_files_stripped,  paths( [ "dangling" ] ) ) ; 
274+     let  symlink_path = & worktree_files[ 0 ] ; 
275+     assert ! ( symlink_path
276+         . symlink_metadata( ) 
277+         . expect( "dangling symlink is on disk" ) 
278+         . is_symlink( ) ) ; 
279+     assert_eq ! ( std:: fs:: read_link( symlink_path) ?,  Path :: new( "non-existing-target" ) ) ; 
280+     assert ! ( outcome. collisions. is_empty( ) ) ; 
281+     Ok ( ( ) ) 
282+ } 
283+ 
260284#[ test]  
261285fn  allow_or_disallow_symlinks ( )  -> crate :: Result  { 
262286    let  mut  opts = opts_from_probe ( ) ; 
@@ -303,12 +327,7 @@ fn keep_going_collects_results() {
303327                . iter( ) 
304328                . map( |r| r. path. to_path_lossy( ) . into_owned( ) ) 
305329                . collect:: <Vec <_>>( ) , 
306-             paths( if  cfg!( unix)  { 
307-                 [ ".gitattributes" ,  "dir/content" ] 
308-             }  else { 
309-                 // not actually a symlink anymore, even though symlinks are supported but git think differently. 
310-                 [ "dir/content" ,  "dir/sub-dir/symlink" ] 
311-             } ) 
330+             paths( [ ".gitattributes" ,  "dir/content" ] ) 
312331        ) ; 
313332    } 
314333
@@ -322,11 +341,15 @@ fn keep_going_collects_results() {
322341    }  else  { 
323342        assert_eq ! ( 
324343            stripped_prefix( & destination,  & dir_structure( & destination) ) , 
325-             paths( if  cfg!( unix)  { 
326-                 Box :: new( [ "dir/sub-dir/symlink" ,  "empty" ,  "executable" ] . into_iter( ) )  as  Box <dyn Iterator <Item  = & str >>
327-             }  else { 
328-                 Box :: new( [ "empty" ,  "executable" ] . into_iter( ) ) 
329-             } ) , 
344+             paths( [ 
345+                 if  cfg!( unix)  { 
346+                     "dir/sub-dir/symlink" 
347+                 }  else { 
348+                     "dir\\ sub-dir\\ symlink" 
349+                 } , 
350+                 "empty" , 
351+                 "executable" , 
352+             ] ) , 
330353            "some files could not be created" 
331354        ) ; 
332355    } 
@@ -550,8 +573,10 @@ fn probe_gitoxide_dir() -> crate::Result<gix_fs::Capabilities> {
550573} 
551574
552575fn  opts_from_probe ( )  -> gix_worktree_state:: checkout:: Options  { 
576+     static  CAPABILITIES :  Lazy < gix_fs:: Capabilities >  = Lazy :: new ( || probe_gitoxide_dir ( ) . unwrap ( ) ) ; 
577+ 
553578    gix_worktree_state:: checkout:: Options  { 
554-         fs :  probe_gitoxide_dir ( ) . unwrap ( ) , 
579+         fs :  * CAPABILITIES , 
555580        destination_is_initially_empty :  true , 
556581        thread_limit :  gix_features:: parallel:: num_threads ( None ) . into ( ) , 
557582        ..Default :: default ( ) 
0 commit comments