Repository navigation
Conversation
…d a symlink to a directory Git names a file by its real path. `git::Files::as_listed` used a path in the work tree as the run spells it, so a file that a search or an argument reaches through a symlink to a directory was never in the lists: "No staged files to lint", exit 0. The file search now tells `git::Files` which symlinks to directories it went through, and whether an argument named a file. Only then does `as_listed` resolve the directory of a path that is not listed as spelled, once for each directory and only for a file that is named like a listed one. A run that goes through no symlink resolves nothing, as before. The texts that `--staged` reads from the index are also given to the `import` rules and the type checker under the paths through those symlinks, so a file behind one reads what it imports as it will be committed. The module graph gets them after the search. `--fix` writes a file that two paths lead to from the path that git has. A staged file whose directory became a symlink is linted once.
Collaborator
Author
|
Updated 1:23 AM PT - Oct 25th, 2026
✅ @robobun, your commit 7af9f7faf8f69f6174f35b3ba24c747dfcba0054 passed in 🧪 To try this PR locally: bunx bun-pr 46857That installs a local version of the PR into your bun-46857 --bun |
Collaborator
Author
|
Status: draft. The new tests run on Windows for the first time in CI, so this stays a draft until those lanes pass. Reproduction. On main, with the script in the Notes of the description: in PR: #46857 |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related to #46583
Problem
bun lint --stagedprintsNo staged files to lintand exits 0 for a changed file behind a directory symlink.--changed(No changed files to lint) andbun fmtdo the same.Files::as_listed(src/lint/driver/git.rs:366) used the path as the run spells it.Fix
as_listedresolve a directory.--stagedgives its index texts to theimportrules and the type checker under those paths too.--fixwrites a file that has two paths from the path git has.test/cli/lint/git.test.ts(17 new tests, 14 fail on main). Alsolint.test.ts,config-files.test.ts,format.test.ts. Self-reviewed: 39 concerns raised, 36 addressed. Not done: aDisk::readfallback and two test rows (Notes).Background
bun lint,bun fmt: the first run, oxlint's order, Prettier's Tailwind plugin, native JavaScript plugins, a fasterBun.Glob#45043 fixed the same symptom for a link above the repository (reply). An ordinary run still resolves nothing.Disk::read.bun checkandbun build --checkread through it too.Downsides
bun fmtargument other than.adds 0 to 199realpathcalls for 2,000 files. Release text: +7,936 bytes.Notes
Who hits this. On main, three kinds of run miss the file:
No staged files to lint, exit 0.bun lintandbun fmt.No problems in 1 file.At the root without such a config, main reports the file under git's path and exits 1. Git has no entry for a path beyond a symlink (
git add packages/a/tests/shared/limits.tsfails withis beyond a symbolic link), so main's answer is git's answer. With this PR the flags narrow the run and do not change which paths it lints.Repro.
Mechanism.
Loader::linksandLoader::named_a_fileare filled by the file search, likesearched_from. The search already resolves each starting directory and each symlink it follows, so this costs no system call.Files::searchedtakes them before the run filters its files.Files::resolvesstarts as true. A search that went through no symlink and named no file turns it off. Thenas_listedreturns every path as it is, as main does for a path in the work tree.as_listedfirst takes a path that is listed or is an index entry as it is. For any other path it resolves the directory, once for each directory.hasonly asks for a file that is named like a listed one.already_readhas the index texts for code that follows imports. It now also has each text under the path through each symlink that the run went through, if that path leads to the same directory. The module graph gets the texts after the search (Graph::provide).Store::provideasserts in debug builds that no rule has read the store yet.bun fmtfollows no symlink in a search and resolves no argument, so any argument other than the working directory turnsresolveson.--fixfirst writes the results whose path is the one git has. A result with another path to the same file then finds the file written and is skipped, as in a run without the flag. There, the threads decide which path writes.What does not change.
bun lint,bun fmt: the first run, oxlint's order, Prettier's Tailwind plugin, native JavaScript plugins, a fasterBun.Glob#45043. Git has its own entry for such a link.bun fmtfollows no link in a search and, without.oxfmtrc.json, rejects a link as an argument.git add l/f.js && mv l d && ln -s d l,bun lint --staged lexits 1 with the staged text.git::Filesthat other code calls changes. The diff applies to the current head ofbun check: TypeScript 7.1 #46383 (5ce9919), andcargo check -p bun_lint_driver -p bun_lint_graphpasses on the result.Measurements. Debug (ASan) builds for the counts, release builds for instructions and size. Merge base 6292c65 against this PR, x86_64 Linux. Counts are for the
bunprocess without itsgitchildren: anLD_PRELOADcounter for libcrealpath, a ptrace counter for system calls,perf_event_openforinstructions:u, ASan's exit statistics for allocations. strace, perf, valgrind and bloaty are not installed in the build container.2,000 files in 200 directories,
--list-files --threads=1. "Shared" means that every directory has a file with the staged file's name.bun lint,bun fmt, no flagnewfstatat2,186 and 2,018lint --staged,fmt --staged,fmt --staged .,lint --staged src, one staged file, sharednewfstatat2,217 / 2,049 / 2,049 / 2,238,realpath1 / 0 / 0 / 1index.jsthat every directory hasnewfstatat2,428,realpath1fmt --staged src, sharedrealpath0,readlink10realpath199,readlink1,005fmt --staged src, the staged name is uniquerealpath0realpath0realpath0realpath0realpath0realpath5realpath2,001,readlink16,016realpath201,readlink1,616packages/arealpath2,newfstatat304, nothing listedrealpath2,newfstatat305, the file is listedlint --stagedwith nothing staged 18,140 and 18,139. With one staged file 18,306 and 18,308. With 201 staged files 18,519 and 18,522. The spread between runs is 4.bun lint-3.0 andbun fmt-0.2, with a main-to-main spread of 2.9 and 0.5.lint --staged-70.5,fmt --staged-67.9,lint --changed=HEAD-70.6. The name test inhasreplaces a hash of the whole path.--changedand for--staged --fix).size): text +7,936 bytes (107,663,568 to 107,671,504), data +0, bss +0.git::Files, one for each run, getsnames,resolvesandreal_directoriesand losestop.Loadergets two fields.Each clause and the tests that need it. One debug build for each line, with that clause deleted, then
test/cli/lint/git.test.ts.bun fmtreports its argumentsbun linthands the report togit::Filesas_listedresolveresolvesis off after a search without symlinksrealpathfor each listed file in a run through a symlinkhasasks only for a listed namerealpathfor each directory in a run through a symlinkWithout the order in
--fix, the test with two configs is not stable: the staged text was one of two, 3 against 3 times in 6 runs.Tests.
test/cli/lint/git.test.ts: 88 pass. On main 14 of the 17 new tests fail. The other 3 pass on main and pin states that a simpler fix breaks. Also run on the debug build:lint.test.ts(712 pass),config-files.test.ts(842),format.test.ts(1,627),bunfig.test.ts(406),native-rules.test.ts(193),rule-throws.test.ts(51),doctor.test.ts(28),rules.test.ts(23),prettier-rule.test.ts(18).cargo check -p bun_lint_driverforx86_64-pc-windows-msvc,aarch64-apple-darwinandaarch64-unknown-linux-musl. The test file merges with #46479 and #46439 without a conflict (git merge-file).Self-review: what is not done.
Disk::readand in its directory listings. It is the complete fix for the import side. This PR covers the symlinks that the run went through. An import through any other symlink reads the file on disk, as on main: a workspace link innode_modules, a link to a file, a second link in a run that names only a file.eslint.config.*row in the import test. It needs a stand-in plugin package. The row with a directory below the link as an argument takes the same path.Windows. The new tests run on Windows. They did not run there before the first push, so this PR is a draft until those lanes pass. Git checks a link out as a symlink there only with
core.symlinks=true. Other clones have a text file in its place and nothing to follow. If the working directory is not spelled as its final path (asubstdrive, a short name), the file is still missed, as on main for a link above the work tree.Left for later.
bun fmtdirectory argument, which would remove therealpathcalls in the table.bun test --changed: test --changed: bind test files to the module graph by position, not by path #46583.