Repository navigation
fix(server): bound untracked files before review diffs #32463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ecf6ecd
654fbca
889e7c0
4ca6cc9
329a8ea
b250770
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,8 @@ const REVIEW_DIFF_PATCH_MAX_OUTPUT_BYTES = 120_000; | |
| const REVIEW_METADATA_MAX_OUTPUT_BYTES = 16 * 1024 * 1024; | ||
| // Temporary-index git add scales poorly (50k files took ~30 s); report incomplete totals above this cap. | ||
| const REVIEW_UNTRACKED_MAX_FILES = 5_000; | ||
| // git diff --numstat reads ~5 ms/MB of untracked text; larger files are left out of the review index. | ||
| const REVIEW_UNTRACKED_MAX_TOTAL_BYTES = 256n * 1024n * 1024n; | ||
| const REVIEW_DIFF_FILE_MAX_OUTPUT_BYTES = 1024 * 1024; | ||
| const REVIEW_BRANCH_COMMIT_LIMIT = 100; | ||
| const REVIEW_BRANCH_SUBJECT_LIMIT = 300; | ||
|
|
@@ -3169,9 +3171,23 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| (candidate) => | ||
| onlyPath === undefined || candidate === onlyPath || candidate === `${onlyPath}/`, | ||
| ); | ||
| if (paths.length === 0) return { env: undefined }; | ||
| if (paths.length === 0) return { env: undefined, omitted: false }; | ||
| if (paths.length > REVIEW_UNTRACKED_MAX_FILES) return null; | ||
| const env = yield* prepareReviewIndex(cwd, paths).pipe( | ||
| const boundedPaths: string[] = []; | ||
| let bytes = 0n; | ||
| for (const relativePath of paths) { | ||
| // lstat reads logical size without following symlinks. | ||
| const info = yield* fileSystem | ||
| .lstat(path.resolve(cwd, relativePath)) | ||
| .pipe(Effect.orElseSucceed(() => undefined)); | ||
| const size = info?.type === "File" || info?.type === "SymbolicLink" ? info.size : 0n; | ||
| if (bytes + size > REVIEW_UNTRACKED_MAX_TOTAL_BYTES) continue; | ||
| bytes += size; | ||
| boundedPaths.push(relativePath); | ||
| } | ||
| const omitted = boundedPaths.length < paths.length; | ||
| if (boundedPaths.length === 0) return { env: undefined, omitted }; | ||
| const env = yield* prepareReviewIndex(cwd, boundedPaths).pipe( | ||
| Effect.catchTags({ | ||
| PlatformError: (cause) => | ||
| Effect.fail( | ||
|
|
@@ -3185,7 +3201,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| ), | ||
| }), | ||
| ); | ||
| return { env }; | ||
| return { env, omitted }; | ||
| }); | ||
|
|
||
| const resolveReviewMergeBase = Effect.fn("resolveReviewMergeBase")(function* ( | ||
|
|
@@ -3262,12 +3278,12 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| : []; | ||
| // Empty status can mean untracked files are hidden; retain the full scan. | ||
| const untracked = yield* prepareUntrackedReviewIndex(cwd, pathspecs); | ||
| if (untracked === null) { | ||
| if (untracked === null || untracked.omitted) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve the unavailable state in the Changes row. When an untracked path is omitted,
|
||
| return yield* new GitCommandError({ | ||
| operation: "GitVcsDriver.readBranchChangeTotals", | ||
| command: "git ls-files", | ||
| cwd, | ||
| detail: "Too many untracked files to count.", | ||
| detail: "Untracked files exceed the review input limits.", | ||
| }); | ||
| } | ||
| const readNumstat = (ref: string) => | ||
|
|
@@ -3594,9 +3610,9 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| ); | ||
| return { ...patch, files: stat.files }; | ||
| }); | ||
| const [dirtyTrackedResult, baseResult] = yield* Effect.gen(function* () { | ||
| const [dirtyTrackedResult, baseResult, untrackedOmitted] = yield* Effect.gen(function* () { | ||
| const untracked = input.trackedOnly | ||
| ? { env: undefined } | ||
| ? { env: undefined, omitted: false } | ||
| : yield* prepareUntrackedReviewIndex(cwd, pathArgs, input.file?.path); | ||
| const metadataOnly = input.metadataOnly === true && !input.file && untracked !== null; | ||
| const [dirty, base] = | ||
|
|
@@ -3611,10 +3627,10 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| ], | ||
| { concurrency: 2 }, | ||
| ); | ||
| if (untracked !== null) return [dirty, base] as const; | ||
| if (untracked !== null) return [dirty, base, untracked.omitted] as const; | ||
| const incomplete = (ref: string | null, result: typeof dirty) => | ||
| ref === null ? result : { ...result, files: undefined, stdoutTruncated: true }; | ||
| return [incomplete(dirtyRef, dirty), incomplete(review.mergeBase, base)] as const; | ||
| return [incomplete(dirtyRef, dirty), incomplete(review.mergeBase, base), false] as const; | ||
| }).pipe(Effect.scoped); | ||
| const dirtyFiles = dirtyTrackedResult.files; | ||
| const baseFiles = baseResult.files; | ||
|
|
@@ -3636,6 +3652,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| ...(dirtyFiles === undefined ? {} : { files: dirtyFiles }), | ||
| diffHash: dirtyDiffHash, | ||
| truncated: dirtyTrackedResult.stdoutTruncated, | ||
| ...(untrackedOmitted ? { untrackedOmitted } : {}), | ||
| ...(input.metadataOnly && !input.file && dirtyFiles !== undefined | ||
| ? { patchesDeferred: true } | ||
| : {}), | ||
|
|
@@ -3650,6 +3667,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | |
| ...(baseFiles === undefined ? {} : { files: baseFiles }), | ||
| diffHash: baseDiffHash, | ||
| truncated: baseResult.stdoutTruncated, | ||
| ...(untrackedOmitted ? { untrackedOmitted } : {}), | ||
| ...(input.metadataOnly && !input.file && baseFiles !== undefined | ||
| ? { patchesDeferred: true } | ||
| : {}), | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.