Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions changelog.d/unreleased/1712.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 1712
affected:
- src/CodeIndex/Indexer/Scanning/FileIndexer.cs
- tests/CodeIndex.Tests/FileIndexerTests.cs
---

## English

- **Per-directory `.cdidxignore` precedence is now covered by regression tests (#1712)** - child `.cdidxignore` files can further exclude paths or re-include files without leaking those rules into sibling directories.

## 日本語

- **ディレクトリごとの `.cdidxignore` 優先順位を回帰テストで固定しました (#1712)** - 子ディレクトリの `.cdidxignore` が追加除外や再 include を行っても、その規則が sibling ディレクトリへ漏れないことを確認します。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/1782.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 1782
affected:
- src/CodeIndex/Cli/IndexCommandRunner.Update.cs
- tests/CodeIndex.Tests/IndexCommandRunnerTests.cs
---

## English

- **Partial update ignore-file changes are now covered by regression tests (#1782)** - `--files` updates that include `.gitignore` or `.cdidxignore` fall back to a full scan so newly ignored stale rows are purged from the index.

## 日本語

- **部分更新で ignore file が変わった場合の挙動を回帰テストで固定しました (#1782)** - `.gitignore` や `.cdidxignore` を含む `--files` 更新は full scan に切り替わり、新たに ignore 対象になった古い行を index から削除します。
31 changes: 31 additions & 0 deletions tests/CodeIndex.Tests/FileIndexerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,37 @@ public void ScanFiles_SkipsBuiltInDirectoriesWithCaseInsensitiveNames()
}
}

[Fact]
public void ScanFiles_PerDirectoryCdidxIgnore_AppliesChildRulesWithoutLeakingToSiblings()
{
var tempDir = Path.Combine(Path.GetTempPath(), $"cdidx-per-dir-ignore-{Guid.NewGuid():N}");
try
{
Directory.CreateDirectory(Path.Combine(tempDir, "left"));
Directory.CreateDirectory(Path.Combine(tempDir, "right"));
File.WriteAllText(Path.Combine(tempDir, ".cdidxignore"), "*.generated.py\n");
File.WriteAllText(Path.Combine(tempDir, "root.generated.py"), "print('ignored root')\n");
File.WriteAllText(Path.Combine(tempDir, "left", ".cdidxignore"), "!keep.generated.py\nlocal.py\n");
File.WriteAllText(Path.Combine(tempDir, "left", "keep.generated.py"), "print('kept child')\n");
File.WriteAllText(Path.Combine(tempDir, "left", "local.py"), "print('ignored child')\n");
File.WriteAllText(Path.Combine(tempDir, "right", "keep.generated.py"), "print('ignored sibling')\n");
File.WriteAllText(Path.Combine(tempDir, "right", "plain.py"), "print('kept sibling')\n");

var files = new FileIndexer(tempDir)
.ScanFiles()
.Select(path => Path.GetRelativePath(tempDir, path).Replace('\\', '/'))
.OrderBy(path => path, StringComparer.Ordinal)
.ToList();

Assert.Equal(["left/keep.generated.py", "right/plain.py"], files);
}
finally
{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
}

[Fact]
public void ScanFilesDetailed_HardlinkedFiles_SkipsDuplicatePathWithWarning()
{
Expand Down
35 changes: 35 additions & 0 deletions tests/CodeIndex.Tests/IndexCommandRunnerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4529,6 +4529,41 @@ public void Run_FullScan_WithMalformedIgnoreRule_ReturnsSuccessWithWarningInstea
}
}

[Fact]
public void Run_UpdateMode_WhenIgnoreFileChanges_FallsBackToFullScanAndPurgesNowIgnoredRows()
{
var projectRoot = CreateTempProject();
try
{
File.WriteAllText(Path.Combine(projectRoot, "generated.py"), "print('generated')\n");
File.WriteAllText(Path.Combine(projectRoot, "keep.py"), "print('keep')\n");

var initialExitCode = IndexCommandRunner.Run([projectRoot, "--json"], _jsonOptions);
Assert.Equal(CommandExitCodes.Success, initialExitCode);

var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db");
Assert.Contains("generated.py", ReadIndexedPaths(dbPath));

File.WriteAllText(Path.Combine(projectRoot, ".gitignore"), "*.py\n!keep.py\n");

var (exitCode, json) = RunAndCaptureJson([projectRoot, "--files", ".gitignore", "--json"]);

Assert.Equal(CommandExitCodes.Success, exitCode);
Assert.Equal("success", json.GetProperty("status").GetString());
Assert.Equal("incremental", json.GetProperty("mode").GetString());

var indexedPaths = ReadIndexedPaths(dbPath);
Assert.DoesNotContain("generated.py", indexedPaths);
Assert.Contains("keep.py", indexedPaths);
Assert.Contains(".gitignore", indexedPaths);
}
finally
{
SqliteConnection.ClearAllPools();
DeleteDirectory(projectRoot);
}
}

[Fact]
public void Run_FullScan_SubdirectoryProjectRoot_UsesRepositoryIgnoreCaseConfigWhenTrue()
{
Expand Down
Loading