* Get all files visible to git (tracked + untracked but not ignored). * Respects .gitignore at all levels (root, subdirectories) and descends into * embedded (nested, non-submodule) git repos. Returns null on failure * (non-git project) so callers can fall back to a filesystem walk.
(rootDir: string)
| 764 | * (non-git project) so callers can fall back to a filesystem walk. |
| 765 | */ |
| 766 | function getGitVisibleFiles(rootDir: string): Set<string> | null { |
| 767 | try { |
| 768 | // Check if the project directory is gitignored by a parent repo. |
| 769 | // When rootDir lives inside a parent git repo that ignores it, |
| 770 | // `git ls-files` returns nothing — fall back to filesystem walk. |
| 771 | const gitRoot = execFileSync( |
| 772 | 'git', |
| 773 | ['rev-parse', '--show-toplevel'], |
| 774 | { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true } |
| 775 | ).trim(); |
| 776 | |
| 777 | if (path.resolve(gitRoot) !== path.resolve(rootDir)) { |
| 778 | try { |
| 779 | // git check-ignore exits 0 if the path IS ignored, 1 if not |
| 780 | execFileSync( |
| 781 | 'git', |
| 782 | ['check-ignore', '-q', path.resolve(rootDir)], |
| 783 | { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true } |
| 784 | ); |
| 785 | // Directory is gitignored by parent repo — fall back to filesystem walk |
| 786 | return null; |
| 787 | } catch { |
| 788 | // Not ignored — safe to use git ls-files |
| 789 | } |
| 790 | } |
| 791 | |
| 792 | const files = new Set<string>(); |
| 793 | const embeddedRoots = new Set<string>(); |
| 794 | collectGitFiles(rootDir, '', files, embeddedRoots, loadIncludeIgnoredMatcher(rootDir)); |
| 795 | // Apply built-in default ignores uniformly — to tracked files too, since |
| 796 | // committing a dependency/build dir doesn't make it project code. A |
| 797 | // `.gitignore` negation (e.g. `!vendor/`) is the explicit opt-in. (issue #407) |
| 798 | // Files inside an EMBEDDED repo are matched against that repo's own rules, |
| 799 | // not the parent's: the parent's .gitignore hides the child repo from git, |
| 800 | // not from the index. (#514) |
| 801 | const ig = buildScopeIgnore(rootDir, embeddedRoots); |
| 802 | return new Set([...files].filter((f) => !ig.ignores(f))); |
| 803 | } catch { |
| 804 | return null; |
| 805 | } |
| 806 | } |
| 807 | |
| 808 | /** |
| 809 | * Result of git-based change detection. |
no test coverage detected