fix: v2.3.1 — correct version, say why a plugin update failed, gitignored files in Files
- v2.3.0's tree reported 2.2.1, so every install saw an update forever; package.json, the lockfile, openapi.yaml, the CLAUDE.md marker and the snapshot now read 2.3.1 - A failed plugin-update step records the tail of the command's stdout and stderr plus its exit code or signal (failureText); plugin-bootstrap.js prints its refusal on stdout, which the verdict used to drop. Update all uses the same text - updates.test.js never applies an update: it skips when the checkout is behind a release (apply ran npm run setup and pruned dev dependencies) - Files tree lists gitignored files and folders, named in orange (addIgnored in git-diff.js); change-tree rows indent in whole steps
This commit is contained in:
+47
-5
@@ -857,11 +857,45 @@ async function treeStatus(dir) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to one level what git does not list there — ignored files and folders
|
||||
* (and an empty folder, which git never lists either) — marked `ignored`,
|
||||
* read off the disk because `ls-files --ignored` cannot list INSIDE an
|
||||
* ignored folder. Returns the folder names it added.
|
||||
*/
|
||||
async function addIgnored(dir, sub, dirNames, files) {
|
||||
const added = new Set();
|
||||
let dirents;
|
||||
try {
|
||||
const root = await fs.promises.realpath(dir);
|
||||
const real = await fs.promises.realpath(path.join(root, sub));
|
||||
const r = path.relative(root, real);
|
||||
// A symlinked folder pointing out of the workspace lists nothing.
|
||||
if (r === ".." || r.startsWith(`..${path.sep}`) || path.isAbsolute(r)) return added;
|
||||
dirents = await fs.promises.readdir(real, { withFileTypes: true });
|
||||
} catch {
|
||||
return added;
|
||||
}
|
||||
const prefix = sub ? `${sub}/` : "";
|
||||
const listed = new Set(files.map((f) => f.name));
|
||||
for (const d of dirents) {
|
||||
if (d.name === ".git" || dirNames.has(d.name) || listed.has(d.name)) continue;
|
||||
if (d.isDirectory()) {
|
||||
dirNames.add(d.name);
|
||||
added.add(d.name);
|
||||
} else {
|
||||
files.push({ name: d.name, path: `${prefix}${d.name}`, dir: false, ignored: true });
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/** A folded level's entries, each carrying its status (see `treeStatus`). */
|
||||
function treeEntries(prefix, dirNames, files, st) {
|
||||
function treeEntries(prefix, dirNames, files, st, ignoredDirs = new Set()) {
|
||||
return [
|
||||
...[...dirNames].sort().map((name) => {
|
||||
const p = `${prefix}${name}`;
|
||||
if (ignoredDirs.has(name)) return { name, path: p, dir: true, ignored: true };
|
||||
return { name, path: p, dir: true, ...(st.dirChanged(p) ? { changed: true } : {}) };
|
||||
}),
|
||||
...files
|
||||
@@ -875,7 +909,8 @@ function treeEntries(prefix, dirNames, files, st) {
|
||||
|
||||
/**
|
||||
* One directory level of the working tree, for the file explorer: tracked
|
||||
* files plus untracked-but-not-ignored ones (`TREE_LS`), folded into
|
||||
* files plus untracked-but-not-ignored ones (`TREE_LS`) and, marked
|
||||
* `ignored`, what git ignores there (`addIgnored`), folded into
|
||||
* directories at this level — a tree that lists a repo's every file at once is
|
||||
* both slow to render and not what "expand a folder" means. A changed file
|
||||
* carries its porcelain `status`, a folder holding one `changed: true`.
|
||||
@@ -898,7 +933,8 @@ async function listTree(dir, { subdir = "" } = {}) {
|
||||
if (slash === -1) files.push({ name: rest, path: p, dir: false });
|
||||
else dirs.add(rest.slice(0, slash));
|
||||
}
|
||||
const entries = treeEntries(prefix, dirs, files, st);
|
||||
const ignored = await addIgnored(dir, clean, dirs, files);
|
||||
const entries = treeEntries(prefix, dirs, files, st, ignored);
|
||||
return {
|
||||
path: clean,
|
||||
entries: entries.slice(0, MAX_PATHS),
|
||||
@@ -995,7 +1031,7 @@ function onlyBlock(hunk, block, reverse) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Every level of the explorer tree (tracked + untracked-not-ignored) from ONE `git ls-files`, keyed by directory
|
||||
* Every level of the explorer tree (tracked + untracked-not-ignored, plus each level's ignored entries) from ONE `git ls-files`, keyed by directory
|
||||
* path ("" is the root), each level shaped and capped exactly like
|
||||
* `listTree`'s. "Expand all folders" used to be one request — one ls-files —
|
||||
* per folder, which on a large repo is thousands of git processes.
|
||||
@@ -1019,8 +1055,14 @@ async function listTreeAll(dir) {
|
||||
}
|
||||
const levels = {};
|
||||
let truncated = cut;
|
||||
// An ignored folder gets no level of its own: it stays shut (node_modules).
|
||||
const ignored = new Map(
|
||||
await Promise.all(
|
||||
[...byDir].map(async ([d, l]) => [d, await addIgnored(dir, d, l.dirs, l.files)])
|
||||
)
|
||||
);
|
||||
for (const [d, { dirs, files }] of byDir) {
|
||||
const entries = treeEntries(d ? `${d}/` : "", dirs, files, st);
|
||||
const entries = treeEntries(d ? `${d}/` : "", dirs, files, st, ignored.get(d));
|
||||
if (entries.length > MAX_PATHS) truncated = true;
|
||||
levels[d] = entries.slice(0, MAX_PATHS);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user