diff --git a/sdk/typescript/_bundled_plugin/scripts/generate_in_scope_files.py b/sdk/typescript/_bundled_plugin/scripts/generate_in_scope_files.py index b69c6b08..f2a224ac 100644 --- a/sdk/typescript/_bundled_plugin/scripts/generate_in_scope_files.py +++ b/sdk/typescript/_bundled_plugin/scripts/generate_in_scope_files.py @@ -105,6 +105,39 @@ def generate_in_scope_files(repository: Path, scope: str, output: Path) -> int: return write_inventory(output, rows) +def committed_changed_paths(repository: Path, base: str, head: str) -> list[tuple[Path, str]]: + result = subprocess.run( + [ + "git", + "-C", + str(repository), + "diff", + "--raw", + "-z", + "--diff-filter=ACMRD", + f"{base}..{head}", + ], + capture_output=True, + text=True, + check=True, + ) + fields = result.stdout.split("\0") + changed: list[tuple[Path, str]] = [] + index = 0 + while index < len(fields) - 1: + metadata = fields[index].split() + status = metadata[-1][0] + index += 1 + if status in {"C", "R"}: + index += 1 + path = fields[index] + index += 1 + selected_mode = metadata[0].removeprefix(":") if status == "D" else metadata[1] + if selected_mode != "120000": + changed.append((repository / path, status)) + return changed + + def generate_diff_in_scope_files( repository: Path, base: str, @@ -114,7 +147,7 @@ def generate_diff_in_scope_files( ) -> int: """Reuse the existing diff selection without generating previews or duplicate worklists.""" sys.path.insert(0, str(Path(__file__).resolve().parent)) - from generate_rank_input import git_changed_paths, path_is_excluded, run_git_changed_paths + from generate_rank_input import path_is_excluded, run_git_changed_paths from rank_preview import ( DEFAULT_PREVIEW_BYTES, TEXT_CODE_EXTENSIONS, @@ -138,7 +171,7 @@ def generate_diff_in_scope_files( if relative ) else: - changed = git_changed_paths(repository, base, head, mode) + changed = committed_changed_paths(repository, base, head) for path, status in changed: relative = path.relative_to(repository) diff --git a/sdk/typescript/tests-ts/compact-diff-scan.test.ts b/sdk/typescript/tests-ts/compact-diff-scan.test.ts index 87fcae93..cadcbe05 100644 --- a/sdk/typescript/tests-ts/compact-diff-scan.test.ts +++ b/sdk/typescript/tests-ts/compact-diff-scan.test.ts @@ -206,6 +206,54 @@ describe("compact diff scan", () => { ]); }); + test("omits committed symlinks from the revision inventory", () => { + const { root, repository } = createRepository(); + writeSource(repository, "src/handler.py", "value = 1\n"); + writeSource(repository, "src/deleted-link.py", "handler.py"); + git(repository, "add", "."); + const deletedLink = git(repository, "hash-object", "src/deleted-link.py"); + git( + repository, + "update-index", + "--cacheinfo", + `120000,${deletedLink},src/deleted-link.py`, + ); + git(repository, "commit", "-qm", "base"); + const base = git(repository, "rev-parse", "HEAD"); + + rmSync(join(repository, "src", "deleted-link.py")); + writeSource(repository, "src/handler.py", "value = 2\n"); + writeSource(repository, "src/added-link.py", "handler.py"); + git(repository, "add", "."); + const addedLink = git(repository, "hash-object", "src/added-link.py"); + git( + repository, + "update-index", + "--cacheinfo", + `120000,${addedLink},src/added-link.py`, + ); + git(repository, "commit", "-qm", "selected changes"); + const head = git(repository, "rev-parse", "HEAD"); + const output = join(root, "in-scope.txt"); + + const result = python( + "generate_in_scope_files.py", + "--repo", + repository, + "--scope", + ".", + "--diff-base", + base, + "--diff-head", + head, + "--out", + output, + ); + + expect(result.status, result.stderr).toBe(0); + expect(readFileSync(output, "utf8").trim()).toBe("src/handler.py"); + }); + test("includes staged, unstaged, and untracked working-tree changes", () => { const { root, repository } = createRepository(); writeSource(repository, "src/handler.py", "value = 1\n");