fix(snapshot): complete gitignore respect for previously tracked files (#22172)

This commit is contained in:
Dax
2026-04-12 14:05:46 -04:00
committed by GitHub
parent fa2c69f09c
commit 264418c0cd
2 changed files with 62 additions and 0 deletions
+27
View File
@@ -180,6 +180,7 @@ export namespace Snapshot {
// Filter out files that are now gitignored even if previously tracked // Filter out files that are now gitignored even if previously tracked
// Files may have been tracked before being gitignored, so we need to check // Files may have been tracked before being gitignored, so we need to check
// against the source project's current gitignore rules // against the source project's current gitignore rules
// Use --no-index to check purely against patterns (ignoring whether file is tracked)
const checkArgs = [ const checkArgs = [
...quote, ...quote,
"--git-dir", "--git-dir",
@@ -187,6 +188,7 @@ export namespace Snapshot {
"--work-tree", "--work-tree",
state.worktree, state.worktree,
"check-ignore", "check-ignore",
"--no-index",
"--", "--",
...all, ...all,
] ]
@@ -303,6 +305,7 @@ export namespace Snapshot {
"--work-tree", "--work-tree",
state.worktree, state.worktree,
"check-ignore", "check-ignore",
"--no-index",
"--", "--",
...files, ...files,
] ]
@@ -669,6 +672,30 @@ export namespace Snapshot {
} satisfies Row, } satisfies Row,
] ]
}) })
// Filter out files that are now gitignored
if (rows.length > 0) {
const files = rows.map((r) => r.file)
const checkArgs = [
...quote,
"--git-dir",
path.join(state.worktree, ".git"),
"--work-tree",
state.worktree,
"check-ignore",
"--no-index",
"--",
...files,
]
const check = yield* git(checkArgs, { cwd: state.directory })
if (check.code === 0) {
const ignored = new Set(check.text.trim().split("\n").filter(Boolean))
const filtered = rows.filter((r) => !ignored.has(r.file))
rows.length = 0
rows.push(...filtered)
}
}
const step = 100 const step = 100
const patch = (file: string, before: string, after: string) => const patch = (file: string, before: string, after: string) =>
formatPatch(structuredPatch(file, file, before, after, "", "", { context: Number.MAX_SAFE_INTEGER })) formatPatch(structuredPatch(file, file, before, after, "", "", { context: Number.MAX_SAFE_INTEGER }))
@@ -612,6 +612,41 @@ test("files tracked in snapshot but now gitignored are filtered out", async () =
}) })
}) })
test("gitignore updated between track calls filters from diff", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
// a.txt is already committed from bootstrap - track it in snapshot
const before = await Snapshot.track()
expect(before).toBeTruthy()
// Modify a.txt (so it appears in diff-files)
await Filesystem.write(`${tmp.path}/a.txt`, "modified content")
// Now add gitignore that would exclude a.txt
await Filesystem.write(`${tmp.path}/.gitignore`, "a.txt\n")
// Also modify b.txt which is not gitignored
await Filesystem.write(`${tmp.path}/b.txt`, "also modified")
// Second track - should not include a.txt even though it changed
const after = await Snapshot.track()
expect(after).toBeTruthy()
// Verify a.txt is NOT in the diff between snapshots
const diffs = await Snapshot.diffFull(before!, after!)
expect(diffs.some((x) => x.file === "a.txt")).toBe(false)
// But .gitignore should be in the diff
expect(diffs.some((x) => x.file === ".gitignore")).toBe(true)
// b.txt should be in the diff (not gitignored)
expect(diffs.some((x) => x.file === "b.txt")).toBe(true)
},
})
})
test("git info exclude changes", async () => { test("git info exclude changes", async () => {
await using tmp = await bootstrap() await using tmp = await bootstrap()
await Instance.provide({ await Instance.provide({