@@ -8,7 +8,7 @@ use std::{
88use fs_err:: create_dir_all;
99
1010use globset:: Glob ;
11- use ignore:: WalkBuilder ;
11+ use ignore:: { WalkBuilder , overrides :: OverrideBuilder } ;
1212use rayon:: iter:: { ParallelBridge , ParallelIterator } ;
1313
1414use crate :: recipe:: parser:: { GlobVec , GlobWithSource } ;
@@ -142,6 +142,7 @@ pub(crate) struct CopyDir<'a> {
142142 use_git_global : bool ,
143143 use_condapackageignore : bool ,
144144 hidden : bool ,
145+ exclude_git_dirs : bool ,
145146 copy_options : CopyOptions ,
146147}
147148
@@ -159,6 +160,8 @@ impl<'a> CopyDir<'a> {
159160 use_condapackageignore : true ,
160161 // include hidden files by default
161162 hidden : false ,
163+ // exclude .git directories by default for path sources
164+ exclude_git_dirs : false ,
162165 copy_options : CopyOptions :: default ( ) ,
163166 }
164167 }
@@ -205,6 +208,11 @@ impl<'a> CopyDir<'a> {
205208 self
206209 }
207210
211+ pub fn exclude_git_dirs ( mut self , b : bool ) -> Self {
212+ self . exclude_git_dirs = b;
213+ self
214+ }
215+
208216 pub fn run ( self ) -> Result < CopyDirResult , SourceError > {
209217 // Create the to path because we're going to copy the contents only
210218 create_dir_all ( self . to_path ) ?;
@@ -225,6 +233,14 @@ impl<'a> CopyDir<'a> {
225233 // Always disable .ignore files - they should not affect source copying
226234 . ignore ( false )
227235 . hidden ( self . hidden ) ;
236+
237+ // Conditionally exclude .git directories for path sources only
238+ if self . exclude_git_dirs {
239+ let mut override_builder = OverrideBuilder :: new ( self . from_path ) ;
240+ override_builder. add ( "!.git/" ) . unwrap ( ) ;
241+ let overrides = override_builder. build ( ) . unwrap ( ) ;
242+ walk_builder. overrides ( overrides) ;
243+ }
228244 if self . use_condapackageignore {
229245 walk_builder. add_custom_ignore_filename ( ".condapackageignore" ) ;
230246 }
@@ -738,4 +754,38 @@ mod test {
738754 . exists( )
739755 ) ;
740756 }
757+
758+ #[ test]
759+ fn test_git_directory_exclusion ( ) {
760+ let tmp_dir = tempfile:: TempDir :: new ( ) . unwrap ( ) ;
761+ let src = tmp_dir. path ( ) . join ( "src" ) ;
762+ fs_err:: create_dir_all ( & src) . unwrap ( ) ;
763+
764+ // Create regular files and .git structure
765+ fs:: write ( src. join ( "main.rs" ) , "fn main() {}" ) . unwrap ( ) ;
766+ let git_obj = src. join ( ".git/objects/ab/1234567890abcdef" ) ;
767+ fs_err:: create_dir_all ( git_obj. parent ( ) . unwrap ( ) ) . unwrap ( ) ;
768+ fs:: write ( & git_obj, "fake git object" ) . unwrap ( ) ;
769+
770+ #[ cfg( unix) ]
771+ if git_obj. metadata ( ) . is_ok ( ) {
772+ use std:: os:: unix:: fs:: PermissionsExt ;
773+ let _ = fs_err:: set_permissions ( & git_obj, std:: fs:: Permissions :: from_mode ( 0o444 ) ) ;
774+ }
775+
776+ let dest = tmp_dir. path ( ) . join ( "dest" ) ;
777+ let result = super :: CopyDir :: new ( & src, & dest)
778+ . use_gitignore ( false )
779+ . exclude_git_dirs ( true )
780+ . run ( )
781+ . unwrap ( ) ;
782+
783+ assert ! ( dest. join( "main.rs" ) . exists( ) && !dest. join( ".git" ) . exists( ) ) ;
784+ assert ! (
785+ !result
786+ . copied_paths( )
787+ . iter( )
788+ . any( |p| p. components( ) . any( |c| c. as_os_str( ) == ".git" ) )
789+ ) ;
790+ }
741791}
0 commit comments