@@ -34,12 +34,31 @@ pub struct IndexFileResult {
3434
3535/// Walk a vault directory and collect all `.md` file paths.
3636///
37- /// Uses the `ignore` crate so `.gitignore` rules are respected automatically.
38- /// Additional exclude patterns (e.g. `.obsidian/`) are applied on top.
39- pub fn walk_vault ( path : & Path , exclude : & [ String ] ) -> Result < Vec < PathBuf > > {
40- let walker = WalkBuilder :: new ( path)
41- . standard_filters ( true ) // respect .gitignore, .ignore, etc.
42- . build ( ) ;
37+ /// When `respect_gitignore` is true, the `ignore` crate honors `.gitignore` /
38+ /// `.ignore` rules (within a git repo, per the crate's `require_git` default).
39+ /// Set it false to index files those VCS rules would skip; hidden entries
40+ /// (`.git/`, dotfiles) and the explicit `exclude` patterns are always skipped.
41+ pub fn walk_vault (
42+ path : & Path ,
43+ exclude : & [ String ] ,
44+ respect_gitignore : bool ,
45+ ) -> Result < Vec < PathBuf > > {
46+ let mut builder = WalkBuilder :: new ( path) ;
47+ if respect_gitignore {
48+ builder. standard_filters ( true ) ; // respect .gitignore, .ignore, hidden, etc.
49+ } else {
50+ // Stop honoring .gitignore / .ignore so VCS-ignored files get indexed,
51+ // but still skip hidden entries (.git/, dotfiles) and apply the
52+ // explicit exclude patterns below.
53+ builder
54+ . hidden ( true )
55+ . parents ( true )
56+ . git_ignore ( false )
57+ . git_global ( false )
58+ . git_exclude ( false )
59+ . ignore ( false ) ;
60+ }
61+ let walker = builder. build ( ) ;
4362
4463 let mut files = Vec :: new ( ) ;
4564 for entry in walker {
@@ -548,7 +567,7 @@ fn run_index_inner(
548567 }
549568
550569 // If rebuild, treat everything as new.
551- let files = walk_vault ( vault_path, & exclude) ?;
570+ let files = walk_vault ( vault_path, & exclude, config . respect_gitignore ) ?;
552571
553572 let ( new_files, changed_files, deleted_files) = if rebuild {
554573 // On rebuild we skip diffing — all files are "new".
@@ -753,7 +772,7 @@ mod tests {
753772 write_file ( root, "image.png" , "not markdown" ) ;
754773 write_file ( root, "readme.txt" , "text file" ) ;
755774
756- let files = walk_vault ( root, & [ ] ) . unwrap ( ) ;
775+ let files = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
757776 assert_eq ! ( files. len( ) , 3 , "expected 3 .md files, got {:?}" , files) ;
758777 for f in & files {
759778 assert_eq ! ( f. extension( ) . unwrap( ) , "md" ) ;
@@ -768,7 +787,7 @@ mod tests {
768787 write_file ( root, ".obsidian/workspace.md" , "obsidian internal" ) ;
769788 write_file ( root, ".obsidian/plugins/plugin.md" , "plugin data" ) ;
770789
771- let files = walk_vault ( root, & [ ".obsidian/" . to_string ( ) ] ) . unwrap ( ) ;
790+ let files = walk_vault ( root, & [ ".obsidian/" . to_string ( ) ] , true ) . unwrap ( ) ;
772791 assert_eq ! ( files. len( ) , 1 , "expected 1 file, got {:?}" , files) ;
773792 assert ! ( files[ 0 ] . ends_with( "note.md" ) ) ;
774793 }
@@ -789,7 +808,7 @@ mod tests {
789808 write_file ( root, "note.md" , "# Note" ) ;
790809 write_file ( root, "drafts/note.md" , "# Draft" ) ;
791810
792- let files = walk_vault ( root, & [ ] ) . unwrap ( ) ;
811+ let files = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
793812 assert_eq ! (
794813 files. len( ) ,
795814 1 ,
@@ -799,6 +818,48 @@ mod tests {
799818 assert ! ( files[ 0 ] . ends_with( "note.md" ) ) ;
800819 }
801820
821+ #[ test]
822+ fn test_walk_gitignore_toggle ( ) {
823+ let tmp = TempDir :: new ( ) . unwrap ( ) ;
824+ let root = tmp. path ( ) ;
825+
826+ // Git repo so the ignore crate honors .gitignore (require_git default).
827+ std:: process:: Command :: new ( "git" )
828+ . args ( [ "init" ] )
829+ . current_dir ( root)
830+ . output ( )
831+ . expect ( "git init failed" ) ;
832+
833+ write_file ( root, ".gitignore" , "drafts/\n " ) ;
834+ write_file ( root, "note.md" , "# Note" ) ;
835+ write_file ( root, "drafts/draft.md" , "# Draft" ) ;
836+
837+ // respect_gitignore = true: the gitignored dir is skipped.
838+ let respected = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
839+ assert_eq ! (
840+ respected. len( ) ,
841+ 1 ,
842+ "gitignored dir should be skipped, got {:?}" ,
843+ respected
844+ ) ;
845+ assert ! ( respected[ 0 ] . ends_with( "note.md" ) ) ;
846+
847+ // respect_gitignore = false: the gitignored file is indexed too.
848+ let ignored = walk_vault ( root, & [ ] , false ) . unwrap ( ) ;
849+ let names: Vec < String > = ignored
850+ . iter ( )
851+ . map ( |f| f. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . into_owned ( ) )
852+ . collect ( ) ;
853+ assert_eq ! (
854+ ignored. len( ) ,
855+ 2 ,
856+ "expected gitignored file to be included, got {:?}" ,
857+ ignored
858+ ) ;
859+ assert ! ( names. contains( & "note.md" . to_string( ) ) ) ;
860+ assert ! ( names. contains( & "draft.md" . to_string( ) ) ) ;
861+ }
862+
802863 #[ test]
803864 fn test_detect_new_files ( ) {
804865 let tmp = TempDir :: new ( ) . unwrap ( ) ;
@@ -807,7 +868,7 @@ mod tests {
807868 write_file ( root, "b.md" , "# B" ) ;
808869
809870 let store = Store :: open_memory ( ) . unwrap ( ) ;
810- let files = walk_vault ( root, & [ ] ) . unwrap ( ) ;
871+ let files = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
811872 let ( new, changed, deleted) = diff_vault ( & files, root, & store) . unwrap ( ) ;
812873
813874 assert_eq ! ( new. len( ) , 2 , "all files should be new" ) ;
@@ -835,7 +896,7 @@ mod tests {
835896 )
836897 . unwrap ( ) ;
837898
838- let files = walk_vault ( root, & [ ] ) . unwrap ( ) ;
899+ let files = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
839900 let ( new, changed, deleted) = diff_vault ( & files, root, & store) . unwrap ( ) ;
840901
841902 assert_eq ! ( new. len( ) , 0 ) ;
@@ -878,7 +939,7 @@ mod tests {
878939 )
879940 . unwrap ( ) ;
880941
881- let files = walk_vault ( root, & [ ] ) . unwrap ( ) ;
942+ let files = walk_vault ( root, & [ ] , true ) . unwrap ( ) ;
882943 let ( new, changed, deleted) = diff_vault ( & files, root, & store) . unwrap ( ) ;
883944
884945 assert_eq ! ( new. len( ) , 0 ) ;
0 commit comments