@youtyan/code-viewer 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/code-viewer.js +160 -754
- package/package.json +1 -1
- package/web/app.js +66 -20
package/dist/code-viewer.js
CHANGED
|
@@ -996,11 +996,13 @@ function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MA
|
|
|
996
996
|
cache.delete(oldest);
|
|
997
997
|
}
|
|
998
998
|
}
|
|
999
|
+
function fileSignatureFromStats(stats) {
|
|
1000
|
+
const inode = stats.ino ?? 0;
|
|
1001
|
+
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
1002
|
+
}
|
|
999
1003
|
function worktreeFileSignature(path, cwd) {
|
|
1000
1004
|
try {
|
|
1001
|
-
|
|
1002
|
-
const inode = "ino" in stats ? stats.ino : 0;
|
|
1003
|
-
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
1005
|
+
return fileSignatureFromStats(lstatSync(join3(cwd, path)));
|
|
1004
1006
|
} catch {
|
|
1005
1007
|
return "state:missing";
|
|
1006
1008
|
}
|
|
@@ -1407,17 +1409,15 @@ var init_runtime = () => {};
|
|
|
1407
1409
|
|
|
1408
1410
|
// web-src/server/git.ts
|
|
1409
1411
|
import {
|
|
1410
|
-
closeSync,
|
|
1411
1412
|
existsSync,
|
|
1412
1413
|
lstatSync as lstatSync2,
|
|
1413
|
-
openSync,
|
|
1414
1414
|
readdirSync,
|
|
1415
1415
|
readFileSync,
|
|
1416
1416
|
readlinkSync,
|
|
1417
|
-
readSync,
|
|
1418
1417
|
realpathSync as realpathSync2,
|
|
1419
1418
|
statSync as statSync2
|
|
1420
1419
|
} from "node:fs";
|
|
1420
|
+
import { open, stat } from "node:fs/promises";
|
|
1421
1421
|
import { dirname as dirname3, join as join4, posix, relative as relative2 } from "node:path";
|
|
1422
1422
|
function normalizeBlameRef(ref, base) {
|
|
1423
1423
|
const rawRef = ref || "worktree";
|
|
@@ -1485,9 +1485,6 @@ function repoRootResult(cwd) {
|
|
|
1485
1485
|
return { kind: "outside" };
|
|
1486
1486
|
return { kind: "error", error: stderr || "git rev-parse failed" };
|
|
1487
1487
|
}
|
|
1488
|
-
function currentBranch(cwd) {
|
|
1489
|
-
return runGitRefLookup(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
1490
|
-
}
|
|
1491
1488
|
function currentBranchAsync(cwd) {
|
|
1492
1489
|
return runGitRefLookupAsync(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
1493
1490
|
}
|
|
@@ -1555,9 +1552,6 @@ async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
|
1555
1552
|
setTimedCacheEntry(repoStatusMapCache, cwd, { map }, now);
|
|
1556
1553
|
return map;
|
|
1557
1554
|
}
|
|
1558
|
-
function show(ref, path, cwd) {
|
|
1559
|
-
return run(["git", "show", `${ref}:${path}`], cwd);
|
|
1560
|
-
}
|
|
1561
1555
|
function showAsync(ref, path, cwd) {
|
|
1562
1556
|
return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
|
|
1563
1557
|
}
|
|
@@ -1849,12 +1843,6 @@ function parseRemoteWebUrl(remote) {
|
|
|
1849
1843
|
return `https://${httpUrl[1]}/${httpUrl[2]}`;
|
|
1850
1844
|
return null;
|
|
1851
1845
|
}
|
|
1852
|
-
function remoteWebUrl(cwd) {
|
|
1853
|
-
const res = run(["git", "remote", "get-url", "origin"], cwd);
|
|
1854
|
-
if (res.code !== 0)
|
|
1855
|
-
return null;
|
|
1856
|
-
return parseRemoteWebUrl(res.stdout.trim());
|
|
1857
|
-
}
|
|
1858
1846
|
async function remoteWebUrlAsync(cwd) {
|
|
1859
1847
|
const res = await runGitAsync(["git", "remote", "get-url", "origin"], cwd);
|
|
1860
1848
|
if (res.code !== 0)
|
|
@@ -1923,60 +1911,6 @@ function historyQueryArgs(query) {
|
|
|
1923
1911
|
shaTerm: /^[0-9a-f]{4,40}$/i.test(trimmed) ? trimmed : ""
|
|
1924
1912
|
};
|
|
1925
1913
|
}
|
|
1926
|
-
function commitHistory(cwd, options) {
|
|
1927
|
-
const ref = (options.ref || "HEAD").trim();
|
|
1928
|
-
if (!ref || ref.startsWith("-") || ref.includes("\x00"))
|
|
1929
|
-
return { commits: [], hasMore: false, error: "invalid ref" };
|
|
1930
|
-
const verified = run(["git", "rev-parse", "--verify", `${ref}^{commit}`], cwd);
|
|
1931
|
-
if (verified.code !== 0)
|
|
1932
|
-
return {
|
|
1933
|
-
commits: [],
|
|
1934
|
-
hasMore: false,
|
|
1935
|
-
...gitFailureResult(verified, "unknown ref")
|
|
1936
|
-
};
|
|
1937
|
-
const skip = Math.max(0, Math.floor(options.skip) || 0);
|
|
1938
|
-
const limit = Math.max(1, Math.min(Math.floor(options.limit) || 1, MAX_HISTORY_LIMIT));
|
|
1939
|
-
const { filterArgs, pathspec, shaTerm } = historyQueryArgs(options.query || "");
|
|
1940
|
-
const pathFilter = (options.path || "").trim();
|
|
1941
|
-
const pathArgs = [];
|
|
1942
|
-
if (pathFilter && !pathFilter.includes("\x00") && !pathFilter.startsWith("-")) {
|
|
1943
|
-
if (!pathFilter.endsWith("/"))
|
|
1944
|
-
pathArgs.push("--follow");
|
|
1945
|
-
pathArgs.push("--", pathFilter);
|
|
1946
|
-
}
|
|
1947
|
-
const res = run([
|
|
1948
|
-
"git",
|
|
1949
|
-
"log",
|
|
1950
|
-
"-z",
|
|
1951
|
-
`--skip=${skip}`,
|
|
1952
|
-
`--max-count=${limit + 1}`,
|
|
1953
|
-
`--format=${HISTORY_FORMAT}`,
|
|
1954
|
-
...filterArgs,
|
|
1955
|
-
verified.stdout.trim(),
|
|
1956
|
-
...pathspec,
|
|
1957
|
-
...pathArgs
|
|
1958
|
-
], cwd);
|
|
1959
|
-
if (res.code !== 0)
|
|
1960
|
-
return {
|
|
1961
|
-
commits: [],
|
|
1962
|
-
hasMore: false,
|
|
1963
|
-
...gitFailureResult(res, "git log failed")
|
|
1964
|
-
};
|
|
1965
|
-
let parsed = parseHistoryLog(res.stdout);
|
|
1966
|
-
if (shaTerm && skip === 0) {
|
|
1967
|
-
const bySha = run(["git", "rev-parse", "--verify", `${shaTerm}^{commit}`], cwd);
|
|
1968
|
-
const sha = bySha.code === 0 ? bySha.stdout.trim() : "";
|
|
1969
|
-
if (sha) {
|
|
1970
|
-
const single = run(["git", "log", "-z", "-1", `--format=${HISTORY_FORMAT}`, sha], cwd);
|
|
1971
|
-
if (single.code === 0) {
|
|
1972
|
-
const hit = parseHistoryLog(single.stdout);
|
|
1973
|
-
parsed = [...hit, ...parsed.filter((c) => c.sha !== sha)];
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
const hasMore = parsed.length > limit;
|
|
1978
|
-
return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
|
|
1979
|
-
}
|
|
1980
1914
|
async function commitHistoryAsync(cwd, options) {
|
|
1981
1915
|
const ref = (options.ref || "HEAD").trim();
|
|
1982
1916
|
if (!ref || ref.startsWith("-") || ref.includes("\x00"))
|
|
@@ -2031,50 +1965,6 @@ async function commitHistoryAsync(cwd, options) {
|
|
|
2031
1965
|
const hasMore = parsed.length > limit;
|
|
2032
1966
|
return { commits: hasMore ? parsed.slice(0, limit) : parsed, hasMore };
|
|
2033
1967
|
}
|
|
2034
|
-
function nameStatusResult(args, cwd) {
|
|
2035
|
-
const res = run([
|
|
2036
|
-
"git",
|
|
2037
|
-
"-c",
|
|
2038
|
-
"core.quotepath=false",
|
|
2039
|
-
"diff",
|
|
2040
|
-
"--no-color",
|
|
2041
|
-
"--no-ext-diff",
|
|
2042
|
-
"--find-renames",
|
|
2043
|
-
"--name-status",
|
|
2044
|
-
"-z",
|
|
2045
|
-
...args
|
|
2046
|
-
], cwd);
|
|
2047
|
-
if (res.code !== 0) {
|
|
2048
|
-
return {
|
|
2049
|
-
files: [],
|
|
2050
|
-
error: gitFailureMessage(res, "git diff --name-status failed")
|
|
2051
|
-
};
|
|
2052
|
-
}
|
|
2053
|
-
const parts = res.stdout.split("\x00");
|
|
2054
|
-
const files = [];
|
|
2055
|
-
for (let i = 0;i < parts.length; ) {
|
|
2056
|
-
const status = parts[i++];
|
|
2057
|
-
if (!status)
|
|
2058
|
-
break;
|
|
2059
|
-
const kind = status[0];
|
|
2060
|
-
if (kind === "R" || kind === "C") {
|
|
2061
|
-
const oldPath = parts[i++] || "";
|
|
2062
|
-
const path = parts[i++] || "";
|
|
2063
|
-
if (path)
|
|
2064
|
-
files.push({
|
|
2065
|
-
status: kind,
|
|
2066
|
-
old_path: oldPath,
|
|
2067
|
-
path,
|
|
2068
|
-
similarity: Number(status.slice(1)) || undefined
|
|
2069
|
-
});
|
|
2070
|
-
} else {
|
|
2071
|
-
const path = parts[i++] || "";
|
|
2072
|
-
if (path)
|
|
2073
|
-
files.push({ status: kind, path });
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
return { files };
|
|
2077
|
-
}
|
|
2078
1968
|
async function nameStatusResultAsync(args, cwd) {
|
|
2079
1969
|
const res = await runGitAsync([
|
|
2080
1970
|
"git",
|
|
@@ -2119,49 +2009,6 @@ async function nameStatusResultAsync(args, cwd) {
|
|
|
2119
2009
|
}
|
|
2120
2010
|
return { files };
|
|
2121
2011
|
}
|
|
2122
|
-
function numstatZResult(args, cwd) {
|
|
2123
|
-
const res = run([
|
|
2124
|
-
"git",
|
|
2125
|
-
"-c",
|
|
2126
|
-
"core.quotepath=false",
|
|
2127
|
-
"diff",
|
|
2128
|
-
"--no-color",
|
|
2129
|
-
"--no-ext-diff",
|
|
2130
|
-
"--find-renames",
|
|
2131
|
-
"--numstat",
|
|
2132
|
-
"-z",
|
|
2133
|
-
...args
|
|
2134
|
-
], cwd);
|
|
2135
|
-
if (res.code !== 0) {
|
|
2136
|
-
return {
|
|
2137
|
-
files: [],
|
|
2138
|
-
error: gitFailureMessage(res, "git diff --numstat failed")
|
|
2139
|
-
};
|
|
2140
|
-
}
|
|
2141
|
-
const parts = res.stdout.split("\x00");
|
|
2142
|
-
const files = [];
|
|
2143
|
-
for (let i = 0;i < parts.length; ) {
|
|
2144
|
-
const rec = parts[i++];
|
|
2145
|
-
if (!rec)
|
|
2146
|
-
break;
|
|
2147
|
-
const match = rec.match(/^(\S+)\t(\S+)\t(.*)$/);
|
|
2148
|
-
if (!match)
|
|
2149
|
-
break;
|
|
2150
|
-
const [, add, del, rest] = match;
|
|
2151
|
-
const binary = add === "-" && del === "-";
|
|
2152
|
-
const additions = binary ? 0 : Number(add) || 0;
|
|
2153
|
-
const deletions = binary ? 0 : Number(del) || 0;
|
|
2154
|
-
if (rest === "") {
|
|
2155
|
-
const oldPath = parts[i++] || "";
|
|
2156
|
-
const path = parts[i++] || "";
|
|
2157
|
-
if (path)
|
|
2158
|
-
files.push({ old_path: oldPath, path, additions, deletions, binary });
|
|
2159
|
-
} else {
|
|
2160
|
-
files.push({ path: rest, additions, deletions, binary });
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
return { files };
|
|
2164
|
-
}
|
|
2165
2012
|
async function numstatZResultAsync(args, cwd) {
|
|
2166
2013
|
const res = await runGitAsync([
|
|
2167
2014
|
"git",
|
|
@@ -2218,8 +2065,8 @@ function isGitInternalPath(path) {
|
|
|
2218
2065
|
function syntheticUncommittedBlameFromWorktree(cwd, path) {
|
|
2219
2066
|
const filePath = join4(cwd, path);
|
|
2220
2067
|
try {
|
|
2221
|
-
const
|
|
2222
|
-
if (!
|
|
2068
|
+
const stat2 = statSync2(filePath);
|
|
2069
|
+
if (!stat2.isFile())
|
|
2223
2070
|
return { lines: [], commits: {}, error: "not a file" };
|
|
2224
2071
|
const text = readFileSync(filePath, "utf8");
|
|
2225
2072
|
const normalized = text.replace(/\r\n/g, `
|
|
@@ -2255,98 +2102,6 @@ function syntheticUncommittedBlameFromWorktree(cwd, path) {
|
|
|
2255
2102
|
return { lines: [], commits: {}, error: "file not readable" };
|
|
2256
2103
|
}
|
|
2257
2104
|
}
|
|
2258
|
-
function blame(cwd, options) {
|
|
2259
|
-
const path = options.path;
|
|
2260
|
-
if (!path || path.includes("\x00") || path.startsWith("-")) {
|
|
2261
|
-
return { lines: [], commits: {}, error: "invalid path" };
|
|
2262
|
-
}
|
|
2263
|
-
const normalized = normalizeBlameRef(options.ref, options.base);
|
|
2264
|
-
const args = ["git", "blame", "--porcelain"];
|
|
2265
|
-
if (normalized.base === "HEAD") {
|
|
2266
|
-
if (normalized.ref.startsWith("-") || normalized.ref.includes("\x00"))
|
|
2267
|
-
return { lines: [], commits: {}, error: "invalid ref" };
|
|
2268
|
-
args.push(normalized.ref);
|
|
2269
|
-
}
|
|
2270
|
-
args.push("--", path);
|
|
2271
|
-
const res = run(args, cwd);
|
|
2272
|
-
if (res.code !== 0) {
|
|
2273
|
-
if (isCommandNotFoundResult("git", res)) {
|
|
2274
|
-
return {
|
|
2275
|
-
lines: [],
|
|
2276
|
-
commits: {},
|
|
2277
|
-
error: commandNotFoundDetail("git"),
|
|
2278
|
-
status: 503
|
|
2279
|
-
};
|
|
2280
|
-
}
|
|
2281
|
-
if (normalized.base === "worktree") {
|
|
2282
|
-
return syntheticUncommittedBlameFromWorktree(cwd, path);
|
|
2283
|
-
}
|
|
2284
|
-
return {
|
|
2285
|
-
lines: [],
|
|
2286
|
-
commits: {},
|
|
2287
|
-
error: res.stderr.trim() || "blame failed"
|
|
2288
|
-
};
|
|
2289
|
-
}
|
|
2290
|
-
const lines = [];
|
|
2291
|
-
const commits = {};
|
|
2292
|
-
const rawLines = res.stdout.split(`
|
|
2293
|
-
`);
|
|
2294
|
-
let i = 0;
|
|
2295
|
-
while (i < rawLines.length) {
|
|
2296
|
-
const headerLine = rawLines[i];
|
|
2297
|
-
if (!headerLine) {
|
|
2298
|
-
i++;
|
|
2299
|
-
continue;
|
|
2300
|
-
}
|
|
2301
|
-
const headerMatch = /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(headerLine);
|
|
2302
|
-
if (!headerMatch) {
|
|
2303
|
-
i++;
|
|
2304
|
-
continue;
|
|
2305
|
-
}
|
|
2306
|
-
const sha = headerMatch[1];
|
|
2307
|
-
const finalLine = Number(headerMatch[3]);
|
|
2308
|
-
i++;
|
|
2309
|
-
let commit = commits[sha];
|
|
2310
|
-
if (!commit) {
|
|
2311
|
-
commit = {
|
|
2312
|
-
sha,
|
|
2313
|
-
author: "",
|
|
2314
|
-
authorMail: "",
|
|
2315
|
-
authorTime: 0,
|
|
2316
|
-
summary: "",
|
|
2317
|
-
isUncommitted: sha === BLAME_ZERO_SHA
|
|
2318
|
-
};
|
|
2319
|
-
commits[sha] = commit;
|
|
2320
|
-
}
|
|
2321
|
-
while (i < rawLines.length && !rawLines[i].startsWith("\t")) {
|
|
2322
|
-
const metaLine = rawLines[i++];
|
|
2323
|
-
if (!metaLine)
|
|
2324
|
-
continue;
|
|
2325
|
-
const sp = metaLine.indexOf(" ");
|
|
2326
|
-
const key = sp >= 0 ? metaLine.slice(0, sp) : metaLine;
|
|
2327
|
-
const val = sp >= 0 ? metaLine.slice(sp + 1) : "";
|
|
2328
|
-
if (key === "author" && !commit.author)
|
|
2329
|
-
commit.author = val;
|
|
2330
|
-
else if (key === "author-mail" && !commit.authorMail)
|
|
2331
|
-
commit.authorMail = val.replace(/^</, "").replace(/>$/, "");
|
|
2332
|
-
else if (key === "author-time" && !commit.authorTime)
|
|
2333
|
-
commit.authorTime = Number(val) || 0;
|
|
2334
|
-
else if (key === "summary" && !commit.summary)
|
|
2335
|
-
commit.summary = val;
|
|
2336
|
-
}
|
|
2337
|
-
if (i < rawLines.length && rawLines[i].startsWith("\t"))
|
|
2338
|
-
i++;
|
|
2339
|
-
if (Number.isFinite(finalLine) && finalLine > 0) {
|
|
2340
|
-
lines.push({
|
|
2341
|
-
lineNo: finalLine,
|
|
2342
|
-
sha,
|
|
2343
|
-
isUncommitted: sha === BLAME_ZERO_SHA
|
|
2344
|
-
});
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
lines.sort((a, b) => a.lineNo - b.lineNo);
|
|
2348
|
-
return { lines, commits };
|
|
2349
|
-
}
|
|
2350
2105
|
async function blameAsync(cwd, options) {
|
|
2351
2106
|
const path = options.path;
|
|
2352
2107
|
if (!path || path.includes("\x00") || path.startsWith("-")) {
|
|
@@ -2439,16 +2194,6 @@ async function blameAsync(cwd, options) {
|
|
|
2439
2194
|
lines.sort((a, b) => a.lineNo - b.lineNo);
|
|
2440
2195
|
return { lines, commits };
|
|
2441
2196
|
}
|
|
2442
|
-
function untracked(cwd, path = "") {
|
|
2443
|
-
const args = ["git", "ls-files", "--others", "--exclude-standard"];
|
|
2444
|
-
if (path)
|
|
2445
|
-
args.push("--", `${path}/`);
|
|
2446
|
-
const res = run(args, cwd);
|
|
2447
|
-
if (res.code !== 0)
|
|
2448
|
-
return [];
|
|
2449
|
-
return res.stdout.split(`
|
|
2450
|
-
`).filter(Boolean).filter((entry) => !isToolInternalPath(entry));
|
|
2451
|
-
}
|
|
2452
2197
|
async function untrackedAsync(cwd, path = "") {
|
|
2453
2198
|
const args = ["git", "ls-files", "--others", "--exclude-standard"];
|
|
2454
2199
|
if (path)
|
|
@@ -2474,18 +2219,6 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
|
|
|
2474
2219
|
return "internal";
|
|
2475
2220
|
return omitDirNames.matches(name) ? "heavy" : undefined;
|
|
2476
2221
|
}
|
|
2477
|
-
function worktreeSubmodulePaths(cwd) {
|
|
2478
|
-
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2479
|
-
return new Set;
|
|
2480
|
-
const res = run(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2481
|
-
if (res.code !== 0)
|
|
2482
|
-
return new Set;
|
|
2483
|
-
return new Set(res.stdout.split(`
|
|
2484
|
-
`).map((line) => {
|
|
2485
|
-
const split = line.indexOf(" ");
|
|
2486
|
-
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
2487
|
-
}).filter(Boolean));
|
|
2488
|
-
}
|
|
2489
2222
|
async function worktreeSubmodulePathsAsync(cwd) {
|
|
2490
2223
|
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2491
2224
|
return new Set;
|
|
@@ -2522,8 +2255,8 @@ function resolveWorktreeSymlinkTarget(cwd, full) {
|
|
|
2522
2255
|
let symlink_target_type = "missing";
|
|
2523
2256
|
if (realpathWithinRepo(cwd, full, false) !== null) {
|
|
2524
2257
|
try {
|
|
2525
|
-
const
|
|
2526
|
-
symlink_target_type =
|
|
2258
|
+
const stat2 = statSync2(full);
|
|
2259
|
+
symlink_target_type = stat2.isDirectory() ? "tree" : stat2.isFile() ? "blob" : "missing";
|
|
2527
2260
|
} catch {
|
|
2528
2261
|
symlink_target_type = "missing";
|
|
2529
2262
|
}
|
|
@@ -2565,83 +2298,6 @@ function worktreeEntryFromDirent(cwd, base, dir, name, isDirectory, isSymlink, o
|
|
|
2565
2298
|
children_omitted_reason: omittedReason
|
|
2566
2299
|
} : baseEntry;
|
|
2567
2300
|
}
|
|
2568
|
-
function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2569
|
-
const base = normalizeTreePath(path);
|
|
2570
|
-
const root = join4(cwd, base);
|
|
2571
|
-
if (realpathWithinRepo(cwd, root, true) === null)
|
|
2572
|
-
return [];
|
|
2573
|
-
const omitDirNameSet = compileNamePatterns(omitDirNames);
|
|
2574
|
-
const excludeNameSet = compileNamePatterns(excludeNames);
|
|
2575
|
-
const submodulePaths = worktreeSubmodulePaths(cwd);
|
|
2576
|
-
let directEntries;
|
|
2577
|
-
try {
|
|
2578
|
-
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2579
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2580
|
-
} catch {
|
|
2581
|
-
return [];
|
|
2582
|
-
}
|
|
2583
|
-
if (!recursive)
|
|
2584
|
-
return directEntries;
|
|
2585
|
-
const fileEntries = [];
|
|
2586
|
-
let truncated = false;
|
|
2587
|
-
const pushRecursiveEntry = (entry) => {
|
|
2588
|
-
if (fileEntries.length >= WORKTREE_RECURSIVE_ENTRY_LIMIT) {
|
|
2589
|
-
if (!truncated) {
|
|
2590
|
-
fileEntries.push({
|
|
2591
|
-
name: "more...",
|
|
2592
|
-
path: "__code_viewer_truncated__",
|
|
2593
|
-
type: "tree",
|
|
2594
|
-
children_omitted: true,
|
|
2595
|
-
children_omitted_reason: "truncated"
|
|
2596
|
-
});
|
|
2597
|
-
truncated = true;
|
|
2598
|
-
}
|
|
2599
|
-
return false;
|
|
2600
|
-
}
|
|
2601
|
-
fileEntries.push(entry);
|
|
2602
|
-
return true;
|
|
2603
|
-
};
|
|
2604
|
-
const walk = (dir, prefix, depth) => {
|
|
2605
|
-
if (truncated)
|
|
2606
|
-
return;
|
|
2607
|
-
if (depth >= WORKTREE_RECURSIVE_DEPTH_LIMIT)
|
|
2608
|
-
return;
|
|
2609
|
-
let entries;
|
|
2610
|
-
try {
|
|
2611
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
2612
|
-
} catch {
|
|
2613
|
-
return;
|
|
2614
|
-
}
|
|
2615
|
-
for (const entry of entries) {
|
|
2616
|
-
if (excludeNameSet.matches(entry.name))
|
|
2617
|
-
continue;
|
|
2618
|
-
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2619
|
-
const full = join4(dir, entry.name);
|
|
2620
|
-
if (entry.isDirectory()) {
|
|
2621
|
-
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2622
|
-
if (omittedReason) {
|
|
2623
|
-
if (!pushRecursiveEntry({
|
|
2624
|
-
name: entry.name,
|
|
2625
|
-
path: entryPath,
|
|
2626
|
-
type: "tree",
|
|
2627
|
-
children_omitted: true,
|
|
2628
|
-
children_omitted_reason: omittedReason
|
|
2629
|
-
}))
|
|
2630
|
-
return;
|
|
2631
|
-
continue;
|
|
2632
|
-
}
|
|
2633
|
-
if (hasDotGitEntry(full))
|
|
2634
|
-
continue;
|
|
2635
|
-
walk(full, entryPath, depth + 1);
|
|
2636
|
-
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2637
|
-
if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
|
|
2638
|
-
return;
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
};
|
|
2642
|
-
walk(root, base, 0);
|
|
2643
|
-
return combineDirectAndRecursiveFiles(directEntries, fileEntries.sort((a, b) => a.path.localeCompare(b.path)));
|
|
2644
|
-
}
|
|
2645
2301
|
async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2646
2302
|
const base = normalizeTreePath(path);
|
|
2647
2303
|
const root = join4(cwd, base);
|
|
@@ -2739,32 +2395,13 @@ function parseLsTreeRecord(rec, allowedTypes) {
|
|
|
2739
2395
|
const match = rec.match(new RegExp(`^(\\d+)\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2740
2396
|
if (!match)
|
|
2741
2397
|
return null;
|
|
2742
|
-
const [, mode, type, entryPath] = match;
|
|
2743
|
-
return {
|
|
2744
|
-
name: entryPath.split("/").pop() || entryPath,
|
|
2745
|
-
path: entryPath,
|
|
2746
|
-
type,
|
|
2747
|
-
...mode === LS_TREE_SYMLINK_MODE ? { is_symlink: true } : {}
|
|
2748
|
-
};
|
|
2749
|
-
}
|
|
2750
|
-
function gitTreeEntries(ref, path, cwd, recursive) {
|
|
2751
|
-
const base = normalizeTreePath(path);
|
|
2752
|
-
const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
|
|
2753
|
-
if (recursive)
|
|
2754
|
-
args.push("-r");
|
|
2755
|
-
args.push("-z", "--full-tree", ref, "--");
|
|
2756
|
-
if (base)
|
|
2757
|
-
args.push(`${base}/`);
|
|
2758
|
-
const res = run(args, cwd);
|
|
2759
|
-
if (res.code !== 0)
|
|
2760
|
-
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2761
|
-
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2762
|
-
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
|
|
2763
|
-
if (recursive)
|
|
2764
|
-
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2765
|
-
else
|
|
2766
|
-
entries = sortTreeEntries(entries);
|
|
2767
|
-
return { code: 0, entries, stderr: "" };
|
|
2398
|
+
const [, mode, type, entryPath] = match;
|
|
2399
|
+
return {
|
|
2400
|
+
name: entryPath.split("/").pop() || entryPath,
|
|
2401
|
+
path: entryPath,
|
|
2402
|
+
type,
|
|
2403
|
+
...mode === LS_TREE_SYMLINK_MODE ? { is_symlink: true } : {}
|
|
2404
|
+
};
|
|
2768
2405
|
}
|
|
2769
2406
|
async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
|
|
2770
2407
|
const base = normalizeTreePath(path);
|
|
@@ -2792,27 +2429,6 @@ function combineDirectAndRecursiveFiles(directEntries, fileEntries) {
|
|
|
2792
2429
|
...fileEntries.filter((entry) => !seen.has(entry.path))
|
|
2793
2430
|
];
|
|
2794
2431
|
}
|
|
2795
|
-
function listTree(ref, path, cwd, options = {}) {
|
|
2796
|
-
const base = normalizeTreePath(path);
|
|
2797
|
-
if (ref === "worktree") {
|
|
2798
|
-
return {
|
|
2799
|
-
code: 0,
|
|
2800
|
-
entries: worktreeFilesystemEntries(cwd, base, !!options.recursive, options.omitDirNames, options.excludeNames),
|
|
2801
|
-
stderr: ""
|
|
2802
|
-
};
|
|
2803
|
-
}
|
|
2804
|
-
const direct = gitTreeEntries(ref, base, cwd, false);
|
|
2805
|
-
if (direct.code !== 0 || !options.recursive)
|
|
2806
|
-
return direct;
|
|
2807
|
-
const recursive = gitTreeEntries(ref, base, cwd, true);
|
|
2808
|
-
if (recursive.code !== 0)
|
|
2809
|
-
return recursive;
|
|
2810
|
-
return {
|
|
2811
|
-
code: 0,
|
|
2812
|
-
entries: combineDirectAndRecursiveFiles(direct.entries, recursive.entries),
|
|
2813
|
-
stderr: ""
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
2432
|
async function listTreeAsync(ref, path, cwd, options = {}) {
|
|
2817
2433
|
const base = normalizeTreePath(path);
|
|
2818
2434
|
if (ref === "worktree") {
|
|
@@ -2840,115 +2456,82 @@ async function listTreeResultAsync(ref, path, cwd, options = {}) {
|
|
|
2840
2456
|
return { entries: result.entries };
|
|
2841
2457
|
return { entries: [], ...gitFailureResult(result, "git ls-tree failed") };
|
|
2842
2458
|
}
|
|
2843
|
-
function
|
|
2844
|
-
return
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
let scan;
|
|
2853
|
-
if (fileExists) {
|
|
2854
|
-
try {
|
|
2855
|
-
scan = scanFileBinaryAndNewlines(full);
|
|
2856
|
-
} catch {
|
|
2857
|
-
return [];
|
|
2858
|
-
}
|
|
2859
|
-
} else {
|
|
2860
|
-
return [];
|
|
2861
|
-
}
|
|
2862
|
-
return [
|
|
2863
|
-
{
|
|
2864
|
-
path,
|
|
2865
|
-
status: "A",
|
|
2866
|
-
additions: scan.binary ? 0 : scan.newlines,
|
|
2867
|
-
deletions: 0,
|
|
2868
|
-
binary: scan.binary,
|
|
2869
|
-
untracked: true
|
|
2870
|
-
}
|
|
2871
|
-
];
|
|
2872
|
-
});
|
|
2873
|
-
}
|
|
2874
|
-
async function untrackedMetaAsync(cwd) {
|
|
2875
|
-
const paths = await untrackedAsync(cwd);
|
|
2876
|
-
return paths.flatMap((path) => {
|
|
2877
|
-
const full = join4(cwd, path);
|
|
2878
|
-
let fileExists = false;
|
|
2879
|
-
try {
|
|
2880
|
-
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
2881
|
-
} catch {
|
|
2882
|
-
fileExists = false;
|
|
2883
|
-
}
|
|
2884
|
-
let scan;
|
|
2885
|
-
if (fileExists) {
|
|
2886
|
-
try {
|
|
2887
|
-
scan = scanFileBinaryAndNewlines(full);
|
|
2888
|
-
} catch {
|
|
2889
|
-
return [];
|
|
2890
|
-
}
|
|
2891
|
-
} else {
|
|
2892
|
-
return [];
|
|
2893
|
-
}
|
|
2894
|
-
return [
|
|
2895
|
-
{
|
|
2896
|
-
path,
|
|
2897
|
-
status: "A",
|
|
2898
|
-
additions: scan.binary ? 0 : scan.newlines,
|
|
2899
|
-
deletions: 0,
|
|
2900
|
-
binary: scan.binary,
|
|
2901
|
-
untracked: true
|
|
2902
|
-
}
|
|
2903
|
-
];
|
|
2904
|
-
});
|
|
2459
|
+
function untrackedFileMeta(path, scan) {
|
|
2460
|
+
return {
|
|
2461
|
+
path,
|
|
2462
|
+
status: "A",
|
|
2463
|
+
additions: scan.binary ? 0 : scan.newlines,
|
|
2464
|
+
deletions: 0,
|
|
2465
|
+
binary: scan.binary,
|
|
2466
|
+
untracked: true
|
|
2467
|
+
};
|
|
2905
2468
|
}
|
|
2906
|
-
function
|
|
2907
|
-
const
|
|
2469
|
+
async function scanFileBinaryAndNewlinesAsync(full) {
|
|
2470
|
+
const handle = await open(full, "r");
|
|
2908
2471
|
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
2909
2472
|
let newlines = 0;
|
|
2910
2473
|
let inspected = 0;
|
|
2911
2474
|
try {
|
|
2912
2475
|
while (true) {
|
|
2913
|
-
const
|
|
2914
|
-
if (
|
|
2476
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
2477
|
+
if (bytesRead <= 0)
|
|
2915
2478
|
break;
|
|
2916
|
-
const binaryProbeBytes = Math.min(
|
|
2479
|
+
const binaryProbeBytes = Math.min(bytesRead, Math.max(0, 8192 - inspected));
|
|
2917
2480
|
for (let i = 0;i < binaryProbeBytes; i++) {
|
|
2918
2481
|
if (buffer[i] === 0)
|
|
2919
2482
|
return { binary: true, newlines: 0 };
|
|
2920
2483
|
}
|
|
2921
|
-
inspected +=
|
|
2922
|
-
for (let i = 0;i <
|
|
2484
|
+
inspected += bytesRead;
|
|
2485
|
+
for (let i = 0;i < bytesRead; i++) {
|
|
2923
2486
|
if (buffer[i] === 10)
|
|
2924
2487
|
newlines++;
|
|
2925
2488
|
}
|
|
2926
2489
|
}
|
|
2927
2490
|
} finally {
|
|
2928
|
-
|
|
2491
|
+
await handle.close();
|
|
2929
2492
|
}
|
|
2930
2493
|
return { binary: false, newlines };
|
|
2931
2494
|
}
|
|
2932
|
-
function
|
|
2933
|
-
const
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
const
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2495
|
+
async function untrackedMetaAsync(cwd) {
|
|
2496
|
+
const paths = await untrackedAsync(cwd);
|
|
2497
|
+
const previous = untrackedScanCache.get(cwd);
|
|
2498
|
+
const next = new Map;
|
|
2499
|
+
const results = new Array(paths.length).fill(null);
|
|
2500
|
+
let cursor = 0;
|
|
2501
|
+
const worker = async () => {
|
|
2502
|
+
while (cursor < paths.length) {
|
|
2503
|
+
const index = cursor++;
|
|
2504
|
+
const path = paths[index];
|
|
2505
|
+
const full = join4(cwd, path);
|
|
2506
|
+
let stats;
|
|
2507
|
+
try {
|
|
2508
|
+
stats = await stat(full);
|
|
2509
|
+
} catch {
|
|
2510
|
+
continue;
|
|
2511
|
+
}
|
|
2512
|
+
if (!stats.isFile())
|
|
2513
|
+
continue;
|
|
2514
|
+
const signature = fileSignatureFromStats(stats);
|
|
2515
|
+
const cached = previous?.get(path);
|
|
2516
|
+
let scan;
|
|
2517
|
+
if (cached && cached.signature === signature) {
|
|
2518
|
+
scan = cached.scan;
|
|
2519
|
+
} else {
|
|
2520
|
+
try {
|
|
2521
|
+
scan = await scanFileBinaryAndNewlinesAsync(full);
|
|
2522
|
+
} catch {
|
|
2523
|
+
continue;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
next.set(path, { signature, scan });
|
|
2527
|
+
results[index] = untrackedFileMeta(path, scan);
|
|
2528
|
+
}
|
|
2951
2529
|
};
|
|
2530
|
+
await Promise.all(Array.from({
|
|
2531
|
+
length: Math.min(UNTRACKED_SCAN_CONCURRENCY, Math.max(1, paths.length))
|
|
2532
|
+
}, () => worker()));
|
|
2533
|
+
untrackedScanCache.set(cwd, next);
|
|
2534
|
+
return results.filter((meta) => meta !== null);
|
|
2952
2535
|
}
|
|
2953
2536
|
async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
|
|
2954
2537
|
const ns = await nameStatusResultAsync(args, cwd);
|
|
@@ -2971,25 +2554,6 @@ async function fileMetaResultAsync(args, cwd, includeUntracked = false) {
|
|
|
2971
2554
|
files: includeUntracked ? files.concat(await untrackedMetaAsync(cwd)) : files
|
|
2972
2555
|
};
|
|
2973
2556
|
}
|
|
2974
|
-
function fileDiffText(args, path, cwd) {
|
|
2975
|
-
const paths = Array.isArray(path) ? path : [path];
|
|
2976
|
-
const res = run([
|
|
2977
|
-
"git",
|
|
2978
|
-
"-c",
|
|
2979
|
-
"core.quotepath=false",
|
|
2980
|
-
"diff",
|
|
2981
|
-
"--no-color",
|
|
2982
|
-
"--no-ext-diff",
|
|
2983
|
-
"--find-renames",
|
|
2984
|
-
...args,
|
|
2985
|
-
"--",
|
|
2986
|
-
...paths
|
|
2987
|
-
], cwd);
|
|
2988
|
-
if (isCommandNotFoundResult("git", res)) {
|
|
2989
|
-
return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
|
|
2990
|
-
}
|
|
2991
|
-
return res;
|
|
2992
|
-
}
|
|
2993
2557
|
async function fileDiffTextAsync(args, path, cwd) {
|
|
2994
2558
|
const paths = Array.isArray(path) ? path : [path];
|
|
2995
2559
|
const res = await runGitAsync([
|
|
@@ -3009,24 +2573,6 @@ async function fileDiffTextAsync(args, path, cwd) {
|
|
|
3009
2573
|
}
|
|
3010
2574
|
return res;
|
|
3011
2575
|
}
|
|
3012
|
-
function untrackedFileDiff(extras, path, cwd) {
|
|
3013
|
-
const res = run([
|
|
3014
|
-
"git",
|
|
3015
|
-
"-c",
|
|
3016
|
-
"core.quotepath=false",
|
|
3017
|
-
"diff",
|
|
3018
|
-
"--no-color",
|
|
3019
|
-
"--no-ext-diff",
|
|
3020
|
-
"--no-index",
|
|
3021
|
-
...extras,
|
|
3022
|
-
"/dev/null",
|
|
3023
|
-
path
|
|
3024
|
-
], cwd);
|
|
3025
|
-
if (isCommandNotFoundResult("git", res)) {
|
|
3026
|
-
return { ...res, stderr: commandNotFoundDetail("git"), status: 503 };
|
|
3027
|
-
}
|
|
3028
|
-
return res;
|
|
3029
|
-
}
|
|
3030
2576
|
async function untrackedFileDiffAsync(extras, path, cwd) {
|
|
3031
2577
|
const res = await runGitAsync([
|
|
3032
2578
|
"git",
|
|
@@ -3122,7 +2668,7 @@ function truncateToNHunks(diffText, n, maxLines = Number.POSITIVE_INFINITY) {
|
|
|
3122
2668
|
lineTruncated
|
|
3123
2669
|
};
|
|
3124
2670
|
}
|
|
3125
|
-
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000";
|
|
2671
|
+
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000", untrackedScanCache, UNTRACKED_SCAN_CONCURRENCY = 8;
|
|
3126
2672
|
var init_git = __esm(() => {
|
|
3127
2673
|
init_cache();
|
|
3128
2674
|
init_command_resolver();
|
|
@@ -3174,6 +2720,7 @@ var init_git = __esm(() => {
|
|
|
3174
2720
|
"obj"
|
|
3175
2721
|
];
|
|
3176
2722
|
repoStatusMapCache = new Map;
|
|
2723
|
+
untrackedScanCache = new Map;
|
|
3177
2724
|
});
|
|
3178
2725
|
|
|
3179
2726
|
// web-src/server/server-registry.ts
|
|
@@ -4417,16 +3964,11 @@ __export(exports_file_cli, {
|
|
|
4417
3964
|
safeWorktreePathFromRoot: () => safeWorktreePathFromRoot,
|
|
4418
3965
|
runFileCli: () => runFileCli,
|
|
4419
3966
|
readShowTextAsync: () => readShowTextAsync,
|
|
4420
|
-
readShowText: () => readShowText,
|
|
4421
3967
|
parseFileArgs: () => parseFileArgs,
|
|
4422
3968
|
buildFileShowReportAsync: () => buildFileShowReportAsync,
|
|
4423
|
-
buildFileShowReport: () => buildFileShowReport,
|
|
4424
3969
|
buildFileHistoryReportAsync: () => buildFileHistoryReportAsync,
|
|
4425
|
-
buildFileHistoryReport: () => buildFileHistoryReport,
|
|
4426
3970
|
buildFileDiffReportAsync: () => buildFileDiffReportAsync,
|
|
4427
|
-
buildFileDiffReport: () => buildFileDiffReport,
|
|
4428
3971
|
buildFileBlameReportAsync: () => buildFileBlameReportAsync,
|
|
4429
|
-
buildFileBlameReport: () => buildFileBlameReport,
|
|
4430
3972
|
FILE_HISTORY_HARD_CAP: () => FILE_HISTORY_HARD_CAP,
|
|
4431
3973
|
FILE_HELP: () => FILE_HELP,
|
|
4432
3974
|
FILE_DIFF_LINE_HARD_CAP: () => FILE_DIFF_LINE_HARD_CAP,
|
|
@@ -4643,14 +4185,14 @@ function parseFileArgs(argv) {
|
|
|
4643
4185
|
if (oldPathError)
|
|
4644
4186
|
return { ok: false, error: oldPathError };
|
|
4645
4187
|
}
|
|
4646
|
-
const
|
|
4647
|
-
if (
|
|
4188
|
+
const untracked = flags.has("--untracked");
|
|
4189
|
+
if (untracked && rawFrom !== undefined) {
|
|
4648
4190
|
return {
|
|
4649
4191
|
ok: false,
|
|
4650
4192
|
error: "--untracked cannot be combined with --from"
|
|
4651
4193
|
};
|
|
4652
4194
|
}
|
|
4653
|
-
if (
|
|
4195
|
+
if (untracked && to !== "worktree") {
|
|
4654
4196
|
return {
|
|
4655
4197
|
ok: false,
|
|
4656
4198
|
error: "--untracked requires --to worktree (or --to omitted)"
|
|
@@ -4694,7 +4236,7 @@ function parseFileArgs(argv) {
|
|
|
4694
4236
|
...oldPathRaw !== undefined ? { oldPath: oldPathRaw } : {},
|
|
4695
4237
|
from,
|
|
4696
4238
|
to,
|
|
4697
|
-
untracked
|
|
4239
|
+
untracked,
|
|
4698
4240
|
ignoreWs,
|
|
4699
4241
|
ignoreBlank,
|
|
4700
4242
|
mode: full ? "full" : "preview",
|
|
@@ -4726,19 +4268,6 @@ function formatBlameText(result) {
|
|
|
4726
4268
|
function formatHistoryText(commits) {
|
|
4727
4269
|
return commits.map((c) => `${c.sha.slice(0, 8)} ${c.when} ${c.author} ${c.subject}`);
|
|
4728
4270
|
}
|
|
4729
|
-
function buildFileBlameReport(root, command) {
|
|
4730
|
-
const result = blame(root, {
|
|
4731
|
-
path: command.path,
|
|
4732
|
-
ref: command.ref,
|
|
4733
|
-
base: command.base
|
|
4734
|
-
});
|
|
4735
|
-
return {
|
|
4736
|
-
path: command.path,
|
|
4737
|
-
ref: command.ref,
|
|
4738
|
-
base: command.base,
|
|
4739
|
-
result
|
|
4740
|
-
};
|
|
4741
|
-
}
|
|
4742
4271
|
async function buildFileBlameReportAsync(root, command) {
|
|
4743
4272
|
const result = await blameAsync(root, {
|
|
4744
4273
|
path: command.path,
|
|
@@ -4752,23 +4281,6 @@ async function buildFileBlameReportAsync(root, command) {
|
|
|
4752
4281
|
result
|
|
4753
4282
|
};
|
|
4754
4283
|
}
|
|
4755
|
-
function buildFileHistoryReport(root, command) {
|
|
4756
|
-
const result = commitHistory(root, {
|
|
4757
|
-
ref: command.ref,
|
|
4758
|
-
skip: command.skip,
|
|
4759
|
-
limit: command.limit,
|
|
4760
|
-
query: command.query,
|
|
4761
|
-
path: command.path
|
|
4762
|
-
});
|
|
4763
|
-
return {
|
|
4764
|
-
path: command.path,
|
|
4765
|
-
ref: command.ref,
|
|
4766
|
-
limit: command.limit,
|
|
4767
|
-
skip: command.skip,
|
|
4768
|
-
...command.query !== undefined ? { query: command.query } : {},
|
|
4769
|
-
result
|
|
4770
|
-
};
|
|
4771
|
-
}
|
|
4772
4284
|
async function buildFileHistoryReportAsync(root, command) {
|
|
4773
4285
|
const result = await commitHistoryAsync(root, {
|
|
4774
4286
|
ref: command.ref,
|
|
@@ -4786,8 +4298,8 @@ async function buildFileHistoryReportAsync(root, command) {
|
|
|
4786
4298
|
result
|
|
4787
4299
|
};
|
|
4788
4300
|
}
|
|
4789
|
-
function runBlame(root, command) {
|
|
4790
|
-
const report =
|
|
4301
|
+
async function runBlame(root, command) {
|
|
4302
|
+
const report = await buildFileBlameReportAsync(root, command);
|
|
4791
4303
|
if (command.json) {
|
|
4792
4304
|
console.log(JSON.stringify(report, null, 2));
|
|
4793
4305
|
} else {
|
|
@@ -4799,8 +4311,8 @@ function runBlame(root, command) {
|
|
|
4799
4311
|
process.exit(1);
|
|
4800
4312
|
}
|
|
4801
4313
|
}
|
|
4802
|
-
function runHistory(root, command) {
|
|
4803
|
-
const report =
|
|
4314
|
+
async function runHistory(root, command) {
|
|
4315
|
+
const report = await buildFileHistoryReportAsync(root, command);
|
|
4804
4316
|
if (command.json) {
|
|
4805
4317
|
console.log(JSON.stringify(report, null, 2));
|
|
4806
4318
|
} else if (report.result.commits.length === 0) {
|
|
@@ -4852,28 +4364,6 @@ function safeWorktreePathFromRoot(root, path) {
|
|
|
4852
4364
|
return null;
|
|
4853
4365
|
}
|
|
4854
4366
|
}
|
|
4855
|
-
function readShowText(root, command) {
|
|
4856
|
-
if (command.ref !== "worktree" && command.ref !== "") {
|
|
4857
|
-
return show(command.ref, command.path, root);
|
|
4858
|
-
}
|
|
4859
|
-
const full = safeWorktreePathFromRoot(root, command.path);
|
|
4860
|
-
if (!full) {
|
|
4861
|
-
return {
|
|
4862
|
-
code: 1,
|
|
4863
|
-
stdout: "",
|
|
4864
|
-
stderr: "file not found or forbidden"
|
|
4865
|
-
};
|
|
4866
|
-
}
|
|
4867
|
-
try {
|
|
4868
|
-
const stat = statSync3(full);
|
|
4869
|
-
if (!stat.isFile()) {
|
|
4870
|
-
return { code: 1, stdout: "", stderr: "not a file" };
|
|
4871
|
-
}
|
|
4872
|
-
return { code: 0, stdout: readFileSync4(full, "utf8"), stderr: "" };
|
|
4873
|
-
} catch {
|
|
4874
|
-
return { code: 1, stdout: "", stderr: "file not readable" };
|
|
4875
|
-
}
|
|
4876
|
-
}
|
|
4877
4367
|
async function readShowTextAsync(root, command) {
|
|
4878
4368
|
if (command.ref !== "worktree" && command.ref !== "") {
|
|
4879
4369
|
return showAsync(command.ref, command.path, root);
|
|
@@ -4887,8 +4377,8 @@ async function readShowTextAsync(root, command) {
|
|
|
4887
4377
|
};
|
|
4888
4378
|
}
|
|
4889
4379
|
try {
|
|
4890
|
-
const
|
|
4891
|
-
if (!
|
|
4380
|
+
const stat2 = statSync3(full);
|
|
4381
|
+
if (!stat2.isFile()) {
|
|
4892
4382
|
return { code: 1, stdout: "", stderr: "not a file" };
|
|
4893
4383
|
}
|
|
4894
4384
|
return { code: 0, stdout: readFileSync4(full, "utf8"), stderr: "" };
|
|
@@ -4896,33 +4386,6 @@ async function readShowTextAsync(root, command) {
|
|
|
4896
4386
|
return { code: 1, stdout: "", stderr: "file not readable" };
|
|
4897
4387
|
}
|
|
4898
4388
|
}
|
|
4899
|
-
function buildFileShowReport(root, command) {
|
|
4900
|
-
const res = readShowText(root, command);
|
|
4901
|
-
if (res.code !== 0) {
|
|
4902
|
-
const detail = res.stderr.trim() || `git show exited with code ${res.code}`;
|
|
4903
|
-
return {
|
|
4904
|
-
path: command.path,
|
|
4905
|
-
ref: command.ref,
|
|
4906
|
-
...command.start !== undefined ? { start: command.start } : {},
|
|
4907
|
-
...command.end !== undefined ? { end: command.end } : {},
|
|
4908
|
-
totalLines: 0,
|
|
4909
|
-
complete: false,
|
|
4910
|
-
text: "",
|
|
4911
|
-
error: detail
|
|
4912
|
-
};
|
|
4913
|
-
}
|
|
4914
|
-
const sliced = sliceLines(res.stdout, command.start, command.end);
|
|
4915
|
-
return {
|
|
4916
|
-
path: command.path,
|
|
4917
|
-
ref: command.ref,
|
|
4918
|
-
...command.start !== undefined ? { start: command.start } : {},
|
|
4919
|
-
...command.end !== undefined ? { end: command.end } : {},
|
|
4920
|
-
totalLines: sliced.total,
|
|
4921
|
-
complete: sliced.complete,
|
|
4922
|
-
text: sliced.lines.join(`
|
|
4923
|
-
`)
|
|
4924
|
-
};
|
|
4925
|
-
}
|
|
4926
4389
|
async function buildFileShowReportAsync(root, command) {
|
|
4927
4390
|
const res = await readShowTextAsync(root, command);
|
|
4928
4391
|
if (res.code !== 0) {
|
|
@@ -4950,8 +4413,8 @@ async function buildFileShowReportAsync(root, command) {
|
|
|
4950
4413
|
`)
|
|
4951
4414
|
};
|
|
4952
4415
|
}
|
|
4953
|
-
function runShow(root, command) {
|
|
4954
|
-
const report =
|
|
4416
|
+
async function runShow(root, command) {
|
|
4417
|
+
const report = await buildFileShowReportAsync(root, command);
|
|
4955
4418
|
if (report.error !== undefined) {
|
|
4956
4419
|
if (command.json) {
|
|
4957
4420
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -4974,64 +4437,6 @@ function buildFileDiffRangeArgs(from, to) {
|
|
|
4974
4437
|
refs.push(to);
|
|
4975
4438
|
return refs;
|
|
4976
4439
|
}
|
|
4977
|
-
function buildFileDiffReport(root, command) {
|
|
4978
|
-
const base = {
|
|
4979
|
-
path: command.path,
|
|
4980
|
-
...command.oldPath !== undefined ? { old_path: command.oldPath } : {},
|
|
4981
|
-
from: command.untracked ? "/dev/null" : command.from,
|
|
4982
|
-
to: command.to,
|
|
4983
|
-
untracked: command.untracked,
|
|
4984
|
-
ignore_ws: command.ignoreWs,
|
|
4985
|
-
ignore_blank: command.ignoreBlank,
|
|
4986
|
-
mode: command.mode,
|
|
4987
|
-
max_hunks: command.mode === "preview" ? command.maxHunks : null,
|
|
4988
|
-
max_lines: command.mode === "preview" ? command.maxLines : null,
|
|
4989
|
-
diff: "",
|
|
4990
|
-
hunk_count: 0,
|
|
4991
|
-
rendered_hunk_count: 0,
|
|
4992
|
-
line_count: 0,
|
|
4993
|
-
truncated: false,
|
|
4994
|
-
binary: false
|
|
4995
|
-
};
|
|
4996
|
-
if (!command.untracked && isSameWorktreeRange({ from: command.from, to: command.to })) {
|
|
4997
|
-
return base;
|
|
4998
|
-
}
|
|
4999
|
-
const extras = [];
|
|
5000
|
-
if (command.ignoreWs)
|
|
5001
|
-
extras.push("-w");
|
|
5002
|
-
if (command.ignoreBlank)
|
|
5003
|
-
extras.push("--ignore-blank-lines");
|
|
5004
|
-
let diffText = "";
|
|
5005
|
-
let errText = "";
|
|
5006
|
-
if (command.untracked) {
|
|
5007
|
-
const res = untrackedFileDiff(extras, command.path, root);
|
|
5008
|
-
diffText = res.stdout || "";
|
|
5009
|
-
if (res.code !== 0)
|
|
5010
|
-
errText = res.stderr.trim();
|
|
5011
|
-
} else {
|
|
5012
|
-
const args = [
|
|
5013
|
-
...extras,
|
|
5014
|
-
...buildFileDiffRangeArgs(command.from, command.to)
|
|
5015
|
-
];
|
|
5016
|
-
const paths = command.oldPath !== undefined ? [command.oldPath, command.path] : command.path;
|
|
5017
|
-
const res = fileDiffText(args, paths, root);
|
|
5018
|
-
diffText = res.stdout || "";
|
|
5019
|
-
if (res.code !== 0)
|
|
5020
|
-
errText = res.stderr.trim();
|
|
5021
|
-
}
|
|
5022
|
-
const truncated = command.mode === "preview" ? truncateToNHunks(diffText, command.maxHunks, command.maxLines) : truncateToNHunks(diffText, 1e9);
|
|
5023
|
-
const previewTruncated = command.mode === "preview" && (truncated.totalHunks > truncated.renderedHunks || truncated.lineTruncated);
|
|
5024
|
-
return {
|
|
5025
|
-
...base,
|
|
5026
|
-
diff: truncated.text,
|
|
5027
|
-
hunk_count: truncated.totalHunks,
|
|
5028
|
-
rendered_hunk_count: truncated.renderedHunks,
|
|
5029
|
-
line_count: truncated.lineCount,
|
|
5030
|
-
truncated: previewTruncated,
|
|
5031
|
-
binary: diffText.includes("Binary files"),
|
|
5032
|
-
...errText ? { error: errText } : {}
|
|
5033
|
-
};
|
|
5034
|
-
}
|
|
5035
4440
|
async function buildFileDiffReportAsync(root, command) {
|
|
5036
4441
|
const base = {
|
|
5037
4442
|
path: command.path,
|
|
@@ -5090,8 +4495,8 @@ async function buildFileDiffReportAsync(root, command) {
|
|
|
5090
4495
|
...errText ? { error: errText } : {}
|
|
5091
4496
|
};
|
|
5092
4497
|
}
|
|
5093
|
-
function runDiff(root, command) {
|
|
5094
|
-
const report =
|
|
4498
|
+
async function runDiff(root, command) {
|
|
4499
|
+
const report = await buildFileDiffReportAsync(root, command);
|
|
5095
4500
|
if (command.json) {
|
|
5096
4501
|
console.log(JSON.stringify(report, null, 2));
|
|
5097
4502
|
} else if (report.diff.length > 0) {
|
|
@@ -10049,18 +9454,18 @@ function formatRecentText(commits) {
|
|
|
10049
9454
|
}
|
|
10050
9455
|
return lines;
|
|
10051
9456
|
}
|
|
10052
|
-
function buildStatusReport(opts) {
|
|
9457
|
+
async function buildStatusReport(opts) {
|
|
10053
9458
|
const { root, ref, limit } = opts;
|
|
10054
|
-
const stagedResult =
|
|
10055
|
-
const changedResult =
|
|
10056
|
-
const worktreeFallbackResult = isMissingDiffBaseError(changedResult.error) ?
|
|
9459
|
+
const stagedResult = await fileMetaResultAsync(["--cached"], root, false);
|
|
9460
|
+
const changedResult = await fileMetaResultAsync(["HEAD"], root, true);
|
|
9461
|
+
const worktreeFallbackResult = isMissingDiffBaseError(changedResult.error) ? await fileMetaResultAsync([], root, true) : null;
|
|
10057
9462
|
const stagedFiles = stagedResult.files;
|
|
10058
9463
|
const changedFiles = changedResult.error ? worktreeFallbackResult ? mergeMissingByPath(worktreeFallbackResult.files, stagedFiles) : [] : mergeMissingByPath(changedResult.files, stagedFiles);
|
|
10059
9464
|
const changed = buildGroup(changedFiles, worktreeFallbackResult ? worktreeFallbackResult.error || stagedResult.error : changedResult.error || stagedResult.error);
|
|
10060
9465
|
const staged = buildGroup(stagedFiles, stagedResult.error);
|
|
10061
|
-
const history =
|
|
10062
|
-
const branch =
|
|
10063
|
-
const remote =
|
|
9466
|
+
const history = await commitHistoryAsync(root, { ref, skip: 0, limit });
|
|
9467
|
+
const branch = await currentBranchAsync(root);
|
|
9468
|
+
const remote = await remoteWebUrlAsync(root);
|
|
10064
9469
|
const registry = readServerRegistry(root);
|
|
10065
9470
|
const serverUrl = registry?.url ?? null;
|
|
10066
9471
|
const nextCommands = buildNextCommands(changed, staged, serverUrl);
|
|
@@ -10098,7 +9503,7 @@ function formatStatusReportText(report) {
|
|
|
10098
9503
|
return lines.join(`
|
|
10099
9504
|
`);
|
|
10100
9505
|
}
|
|
10101
|
-
function runStatusCli(argv) {
|
|
9506
|
+
async function runStatusCli(argv) {
|
|
10102
9507
|
const parsed = parseStatusArgs(argv);
|
|
10103
9508
|
if (parsed.ok === false) {
|
|
10104
9509
|
console.error(parsed.error);
|
|
@@ -10124,7 +9529,7 @@ function runStatusCli(argv) {
|
|
|
10124
9529
|
process.exit(1);
|
|
10125
9530
|
}
|
|
10126
9531
|
const root = resolveRepoRoot(cwd);
|
|
10127
|
-
const report = buildStatusReport({
|
|
9532
|
+
const report = await buildStatusReport({
|
|
10128
9533
|
root,
|
|
10129
9534
|
ref: command.ref,
|
|
10130
9535
|
limit: command.limit
|
|
@@ -14805,26 +14210,26 @@ function hasControlCharacter(value) {
|
|
|
14805
14210
|
|
|
14806
14211
|
// web-src/server/database/discovery.ts
|
|
14807
14212
|
import {
|
|
14808
|
-
closeSync
|
|
14213
|
+
closeSync,
|
|
14809
14214
|
existsSync as existsSync6,
|
|
14810
|
-
openSync
|
|
14811
|
-
readSync
|
|
14215
|
+
openSync,
|
|
14216
|
+
readSync,
|
|
14812
14217
|
realpathSync as realpathSync5,
|
|
14813
14218
|
statSync as statSync4
|
|
14814
14219
|
} from "node:fs";
|
|
14815
|
-
import { lstat, open, readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
14220
|
+
import { lstat, open as open2, readdir, readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
14816
14221
|
import { basename, join as join9, relative as relative4 } from "node:path";
|
|
14817
14222
|
function isSqliteFile(fullPath) {
|
|
14818
14223
|
try {
|
|
14819
|
-
const
|
|
14820
|
-
if (!
|
|
14224
|
+
const stat3 = statSync4(fullPath);
|
|
14225
|
+
if (!stat3.isFile() || stat3.size < 16)
|
|
14821
14226
|
return false;
|
|
14822
14227
|
const buf = Buffer.alloc(16);
|
|
14823
|
-
const fd =
|
|
14228
|
+
const fd = openSync(fullPath, "r");
|
|
14824
14229
|
try {
|
|
14825
|
-
|
|
14230
|
+
readSync(fd, buf, 0, 16, 0);
|
|
14826
14231
|
} finally {
|
|
14827
|
-
|
|
14232
|
+
closeSync(fd);
|
|
14828
14233
|
}
|
|
14829
14234
|
return buf.toString("utf8", 0, 16) === SQLITE_MAGIC;
|
|
14830
14235
|
} catch {
|
|
@@ -14833,10 +14238,10 @@ function isSqliteFile(fullPath) {
|
|
|
14833
14238
|
}
|
|
14834
14239
|
async function isSqliteFileAsync(fullPath) {
|
|
14835
14240
|
try {
|
|
14836
|
-
const fileStat = await
|
|
14241
|
+
const fileStat = await stat2(fullPath);
|
|
14837
14242
|
if (!fileStat.isFile() || fileStat.size < 16)
|
|
14838
14243
|
return false;
|
|
14839
|
-
const file = await
|
|
14244
|
+
const file = await open2(fullPath, "r");
|
|
14840
14245
|
try {
|
|
14841
14246
|
const buf = Buffer.alloc(16);
|
|
14842
14247
|
await file.read(buf, 0, 16, 0);
|
|
@@ -15300,7 +14705,7 @@ function cloneDockerDiscoveryResult(result) {
|
|
|
15300
14705
|
}
|
|
15301
14706
|
async function pathExistsAsync(path) {
|
|
15302
14707
|
try {
|
|
15303
|
-
await
|
|
14708
|
+
await stat2(path);
|
|
15304
14709
|
return true;
|
|
15305
14710
|
} catch {
|
|
15306
14711
|
return false;
|
|
@@ -17334,7 +16739,7 @@ function createDockerAdapterCache(maxEntries = DEFAULT_MAX_DOCKER_ADAPTER_CACHE,
|
|
|
17334
16739
|
}
|
|
17335
16740
|
}
|
|
17336
16741
|
return {
|
|
17337
|
-
async getOrOpenAsync(key,
|
|
16742
|
+
async getOrOpenAsync(key, open3) {
|
|
17338
16743
|
prune();
|
|
17339
16744
|
const cached = cache.get(key);
|
|
17340
16745
|
if (cached) {
|
|
@@ -17348,7 +16753,7 @@ function createDockerAdapterCache(maxEntries = DEFAULT_MAX_DOCKER_ADAPTER_CACHE,
|
|
|
17348
16753
|
closed: false,
|
|
17349
16754
|
promise: undefined
|
|
17350
16755
|
};
|
|
17351
|
-
pendingEntry.promise = Promise.resolve().then(
|
|
16756
|
+
pendingEntry.promise = Promise.resolve().then(open3).then((adapter) => {
|
|
17352
16757
|
if (pendingEntry.closed) {
|
|
17353
16758
|
adapter.close();
|
|
17354
16759
|
} else {
|
|
@@ -21646,27 +21051,27 @@ async function checkSqlite(cwd) {
|
|
|
21646
21051
|
const status = await describeSqliteDriver();
|
|
21647
21052
|
const rows = [sqliteStatusToRow(status)];
|
|
21648
21053
|
if (status.kind === "ok") {
|
|
21649
|
-
const
|
|
21650
|
-
if (
|
|
21054
|
+
const open3 = await trySnapshotDbOpen(cwd);
|
|
21055
|
+
if (open3.kind === "skipped") {
|
|
21651
21056
|
rows.push({
|
|
21652
21057
|
id: "sqlite.snapshot-open",
|
|
21653
21058
|
title: "Snapshot DB open smoke test",
|
|
21654
21059
|
status: "ok",
|
|
21655
21060
|
detail: "Skipped (snapshot DB will be created on first use)"
|
|
21656
21061
|
});
|
|
21657
|
-
} else if (
|
|
21062
|
+
} else if (open3.kind === "ok") {
|
|
21658
21063
|
rows.push({
|
|
21659
21064
|
id: "sqlite.snapshot-open",
|
|
21660
21065
|
title: "Snapshot DB open smoke test",
|
|
21661
21066
|
status: "ok",
|
|
21662
|
-
detail: `Opened ${
|
|
21067
|
+
detail: `Opened ${open3.path}`
|
|
21663
21068
|
});
|
|
21664
21069
|
} else {
|
|
21665
21070
|
rows.push({
|
|
21666
21071
|
id: "sqlite.snapshot-open",
|
|
21667
21072
|
title: "Snapshot DB open failed",
|
|
21668
21073
|
status: "error",
|
|
21669
|
-
detail:
|
|
21074
|
+
detail: open3.message,
|
|
21670
21075
|
hint: "The snapshot DB exists but could not be opened. Inspect file permissions, possible corruption, or another process holding the file."
|
|
21671
21076
|
});
|
|
21672
21077
|
}
|
|
@@ -21715,8 +21120,8 @@ function checkSnapshotStore(cwd) {
|
|
|
21715
21120
|
}
|
|
21716
21121
|
let dbDetail = dbPath;
|
|
21717
21122
|
try {
|
|
21718
|
-
const
|
|
21719
|
-
dbDetail = `${dbPath} (${
|
|
21123
|
+
const stat3 = statSync5(dbPath);
|
|
21124
|
+
dbDetail = `${dbPath} (${stat3.size.toLocaleString()} bytes)`;
|
|
21720
21125
|
} catch {
|
|
21721
21126
|
dbDetail = `${dbPath} (not created yet — created on first snapshot)`;
|
|
21722
21127
|
}
|
|
@@ -23330,7 +22735,8 @@ var init_journal2 = __esm(() => {
|
|
|
23330
22735
|
});
|
|
23331
22736
|
|
|
23332
22737
|
// web-src/server/search-service.ts
|
|
23333
|
-
import { existsSync as existsSync7,
|
|
22738
|
+
import { existsSync as existsSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
22739
|
+
import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
|
|
23334
22740
|
import { join as join18, relative as relative7 } from "node:path";
|
|
23335
22741
|
async function rgAvailableAsync(cwd) {
|
|
23336
22742
|
if (rgAvailableCache !== null)
|
|
@@ -23382,12 +22788,12 @@ function safeWorktreePath(env, path) {
|
|
|
23382
22788
|
function filterCallerPaths(env, paths) {
|
|
23383
22789
|
return paths.filter((path) => isSafePath(path) && !isGitInternalPath(path) && !isSkippableSearchPath(path, env.omitDirNames, env.excludeNames));
|
|
23384
22790
|
}
|
|
23385
|
-
function grepWorktreeFallback(env, query, max, paths) {
|
|
23386
|
-
const candidates = paths.length ? paths :
|
|
22791
|
+
async function grepWorktreeFallback(env, query, max, paths) {
|
|
22792
|
+
const candidates = paths.length ? paths : (await listTreeAsync("worktree", "", env.cwd, {
|
|
23387
22793
|
recursive: true,
|
|
23388
22794
|
omitDirNames: env.omitDirNames,
|
|
23389
22795
|
excludeNames: env.excludeNames
|
|
23390
|
-
}).entries.map((entry) => entry.path);
|
|
22796
|
+
})).entries.map((entry) => entry.path);
|
|
23391
22797
|
const matches = [];
|
|
23392
22798
|
for (const path of candidates) {
|
|
23393
22799
|
if (matches.length >= max)
|
|
@@ -23397,17 +22803,17 @@ function grepWorktreeFallback(env, query, max, paths) {
|
|
|
23397
22803
|
const full = safeWorktreePath(env, path);
|
|
23398
22804
|
if (!full)
|
|
23399
22805
|
continue;
|
|
23400
|
-
let
|
|
22806
|
+
let stat3;
|
|
23401
22807
|
try {
|
|
23402
|
-
|
|
22808
|
+
stat3 = await lstat2(full);
|
|
23403
22809
|
} catch {
|
|
23404
22810
|
continue;
|
|
23405
22811
|
}
|
|
23406
|
-
if (!
|
|
22812
|
+
if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > GREP_MAX_FILE_BYTES)
|
|
23407
22813
|
continue;
|
|
23408
22814
|
let data;
|
|
23409
22815
|
try {
|
|
23410
|
-
data =
|
|
22816
|
+
data = await readFile3(full);
|
|
23411
22817
|
} catch {
|
|
23412
22818
|
continue;
|
|
23413
22819
|
}
|
|
@@ -23447,7 +22853,7 @@ async function grepWorktreeAsync(env, req) {
|
|
|
23447
22853
|
matches: []
|
|
23448
22854
|
};
|
|
23449
22855
|
}
|
|
23450
|
-
const matches = grepWorktreeFallback(env, req.query, req.max, paths);
|
|
22856
|
+
const matches = await grepWorktreeFallback(env, req.query, req.max, paths);
|
|
23451
22857
|
return {
|
|
23452
22858
|
ref: "worktree",
|
|
23453
22859
|
engine: "fallback",
|
|
@@ -23553,7 +22959,7 @@ var init_search_service = __esm(() => {
|
|
|
23553
22959
|
});
|
|
23554
22960
|
|
|
23555
22961
|
// web-src/server/mcp.ts
|
|
23556
|
-
import { readFileSync as
|
|
22962
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
23557
22963
|
import { join as join19 } from "node:path";
|
|
23558
22964
|
function defaultMcpTools(options = {}) {
|
|
23559
22965
|
return [
|
|
@@ -24051,7 +23457,7 @@ function defaultMcpTools(options = {}) {
|
|
|
24051
23457
|
}
|
|
24052
23458
|
];
|
|
24053
23459
|
}
|
|
24054
|
-
function runStatusTool(input, defaultCwd) {
|
|
23460
|
+
async function runStatusTool(input, defaultCwd) {
|
|
24055
23461
|
const params = isPlainObject(input) ? input : {};
|
|
24056
23462
|
const cwdRaw = params.cwd;
|
|
24057
23463
|
const refRaw = params.ref;
|
|
@@ -24092,7 +23498,7 @@ function runStatusTool(input, defaultCwd) {
|
|
|
24092
23498
|
return { text: resolved.error, isError: true };
|
|
24093
23499
|
}
|
|
24094
23500
|
try {
|
|
24095
|
-
const report = buildStatusReport({ root: resolved.root, ref, limit });
|
|
23501
|
+
const report = await buildStatusReport({ root: resolved.root, ref, limit });
|
|
24096
23502
|
return {
|
|
24097
23503
|
text: JSON.stringify(report, null, 2),
|
|
24098
23504
|
isError: !!(report.changed.error || report.staged.error)
|
|
@@ -24365,14 +23771,14 @@ async function runFileDiffTool(input, defaultCwd) {
|
|
|
24365
23771
|
if (untrackedRaw !== undefined && typeof untrackedRaw !== "boolean") {
|
|
24366
23772
|
return { text: "untracked must be a boolean", isError: true };
|
|
24367
23773
|
}
|
|
24368
|
-
const
|
|
24369
|
-
if (
|
|
23774
|
+
const untracked = untrackedRaw === true;
|
|
23775
|
+
if (untracked && params.from !== undefined) {
|
|
24370
23776
|
return {
|
|
24371
23777
|
text: "untracked cannot be combined with from",
|
|
24372
23778
|
isError: true
|
|
24373
23779
|
};
|
|
24374
23780
|
}
|
|
24375
|
-
if (
|
|
23781
|
+
if (untracked && toParsed.ref !== "worktree") {
|
|
24376
23782
|
return {
|
|
24377
23783
|
text: "untracked requires to='worktree' (or omitted)",
|
|
24378
23784
|
isError: true
|
|
@@ -24420,7 +23826,7 @@ async function runFileDiffTool(input, defaultCwd) {
|
|
|
24420
23826
|
...oldPath !== undefined ? { oldPath } : {},
|
|
24421
23827
|
from: fromParsed.ref,
|
|
24422
23828
|
to: toParsed.ref,
|
|
24423
|
-
untracked
|
|
23829
|
+
untracked,
|
|
24424
23830
|
ignoreWs,
|
|
24425
23831
|
ignoreBlank,
|
|
24426
23832
|
mode,
|
|
@@ -25028,7 +24434,7 @@ var init_mcp = __esm(() => {
|
|
|
25028
24434
|
init_search_cli();
|
|
25029
24435
|
init_search_service();
|
|
25030
24436
|
init_status_cli();
|
|
25031
|
-
PACKAGE_VERSION = JSON.parse(
|
|
24437
|
+
PACKAGE_VERSION = JSON.parse(readFileSync7(join19(ROOT, "package.json"), "utf8")).version;
|
|
25032
24438
|
MCP_SERVER_INFO = {
|
|
25033
24439
|
name: "code-viewer",
|
|
25034
24440
|
title: "code-viewer",
|
|
@@ -25109,13 +24515,13 @@ var init_state_route = __esm(() => {
|
|
|
25109
24515
|
// web-src/server/preview.ts
|
|
25110
24516
|
var exports_preview = {};
|
|
25111
24517
|
import {
|
|
25112
|
-
closeSync as
|
|
24518
|
+
closeSync as closeSync2,
|
|
25113
24519
|
constants as constants3,
|
|
25114
24520
|
existsSync as existsSync8,
|
|
25115
|
-
lstatSync as
|
|
24521
|
+
lstatSync as lstatSync4,
|
|
25116
24522
|
mkdirSync as mkdirSync4,
|
|
25117
|
-
openSync as
|
|
25118
|
-
readFileSync as
|
|
24523
|
+
openSync as openSync2,
|
|
24524
|
+
readFileSync as readFileSync8,
|
|
25119
24525
|
realpathSync as realpathSync7,
|
|
25120
24526
|
renameSync,
|
|
25121
24527
|
statSync as statSync6,
|
|
@@ -25356,7 +24762,7 @@ function staticFile(pathname) {
|
|
|
25356
24762
|
const full = join20(WEB_ROOT, spec[0]);
|
|
25357
24763
|
if (!existsSync8(full))
|
|
25358
24764
|
return text("not found", 404);
|
|
25359
|
-
return new Response(
|
|
24765
|
+
return new Response(readFileSync8(full), {
|
|
25360
24766
|
headers: { "Content-Type": spec[1], "Cache-Control": "no-store" }
|
|
25361
24767
|
});
|
|
25362
24768
|
}
|
|
@@ -25631,11 +25037,11 @@ function worktreeFileMetadata(path, knownSize) {
|
|
|
25631
25037
|
if (!full)
|
|
25632
25038
|
return {};
|
|
25633
25039
|
try {
|
|
25634
|
-
const
|
|
25040
|
+
const stat3 = statSync6(full);
|
|
25635
25041
|
return {
|
|
25636
|
-
size: knownSize ??
|
|
25637
|
-
created_at: isoDate(
|
|
25638
|
-
updated_at: isoDate(
|
|
25042
|
+
size: knownSize ?? stat3.size,
|
|
25043
|
+
created_at: isoDate(stat3.birthtimeMs),
|
|
25044
|
+
updated_at: isoDate(stat3.mtimeMs)
|
|
25639
25045
|
};
|
|
25640
25046
|
} catch {
|
|
25641
25047
|
return {};
|
|
@@ -25656,10 +25062,10 @@ async function directoryMetadata(target, path) {
|
|
|
25656
25062
|
if (!full)
|
|
25657
25063
|
return {};
|
|
25658
25064
|
try {
|
|
25659
|
-
const
|
|
25065
|
+
const stat3 = statSync6(full);
|
|
25660
25066
|
return {
|
|
25661
|
-
created_at: isoDate(
|
|
25662
|
-
updated_at: isoDate(
|
|
25067
|
+
created_at: isoDate(stat3.birthtimeMs),
|
|
25068
|
+
updated_at: isoDate(stat3.mtimeMs)
|
|
25663
25069
|
};
|
|
25664
25070
|
} catch {
|
|
25665
25071
|
return {};
|
|
@@ -25704,7 +25110,7 @@ async function readReadme(target, dirPath) {
|
|
|
25704
25110
|
if (!full)
|
|
25705
25111
|
continue;
|
|
25706
25112
|
try {
|
|
25707
|
-
return { path, text:
|
|
25113
|
+
return { path, text: readFileSync8(full, "utf8") };
|
|
25708
25114
|
} catch {
|
|
25709
25115
|
continue;
|
|
25710
25116
|
}
|
|
@@ -26085,8 +25491,8 @@ async function handleFileDiff(url) {
|
|
|
26085
25491
|
}
|
|
26086
25492
|
function worktreeLineIndexSignature(full) {
|
|
26087
25493
|
try {
|
|
26088
|
-
const
|
|
26089
|
-
return `size:${
|
|
25494
|
+
const stat3 = statSync6(full);
|
|
25495
|
+
return `size:${stat3.size}|mtime:${stat3.mtimeMs}|ctime:${stat3.ctimeMs}|ino:${stat3.ino || 0}`;
|
|
26090
25496
|
} catch {
|
|
26091
25497
|
return null;
|
|
26092
25498
|
}
|
|
@@ -26101,10 +25507,10 @@ async function getWorktreeLineIndex(full) {
|
|
|
26101
25507
|
lineIndexCache.set(full, cached);
|
|
26102
25508
|
return cached.index;
|
|
26103
25509
|
}
|
|
26104
|
-
const
|
|
26105
|
-
if (
|
|
25510
|
+
const stat3 = statSync6(full);
|
|
25511
|
+
if (stat3.size > LINE_INDEX_MAX_FILE_BYTES)
|
|
26106
25512
|
return null;
|
|
26107
|
-
const index = await buildLineOffsetIndexFromStream(fileReadableStream(full),
|
|
25513
|
+
const index = await buildLineOffsetIndexFromStream(fileReadableStream(full), stat3.size);
|
|
26108
25514
|
lineIndexCache.delete(full);
|
|
26109
25515
|
lineIndexCache.set(full, { signature, index });
|
|
26110
25516
|
while (lineIndexCache.size > 32) {
|
|
@@ -26476,11 +25882,11 @@ async function handleUploadFiles(req) {
|
|
|
26476
25882
|
const written = [];
|
|
26477
25883
|
try {
|
|
26478
25884
|
for (const upload of uploads) {
|
|
26479
|
-
const fd =
|
|
25885
|
+
const fd = openSync2(upload.target, uploadOpenFlags(), 420);
|
|
26480
25886
|
try {
|
|
26481
25887
|
writeFileSync2(fd, new Uint8Array(await upload.file.arrayBuffer()));
|
|
26482
25888
|
} finally {
|
|
26483
|
-
|
|
25889
|
+
closeSync2(fd);
|
|
26484
25890
|
}
|
|
26485
25891
|
written.push(upload.target);
|
|
26486
25892
|
}
|
|
@@ -26600,7 +26006,7 @@ function moveMacPathIntoTrash(path) {
|
|
|
26600
26006
|
}
|
|
26601
26007
|
}
|
|
26602
26008
|
async function movePathToTrash(path) {
|
|
26603
|
-
|
|
26009
|
+
lstatSync4(path);
|
|
26604
26010
|
if (process.platform === "darwin") {
|
|
26605
26011
|
return moveMacPathIntoTrash(path);
|
|
26606
26012
|
}
|
|
@@ -27393,7 +26799,7 @@ var init_preview = __esm(async () => {
|
|
|
27393
26799
|
init_state_store();
|
|
27394
26800
|
init_worktree_watcher();
|
|
27395
26801
|
WEB_ROOT = join20(ROOT, "web");
|
|
27396
|
-
VERSION = JSON.parse(
|
|
26802
|
+
VERSION = JSON.parse(readFileSync8(join20(ROOT, "package.json"), "utf8")).version;
|
|
27397
26803
|
DEFAULT_ARGS = ["HEAD"];
|
|
27398
26804
|
WATCHED_ASSET_FILES = ["index.html", "style.css", "app.js"];
|
|
27399
26805
|
LINE_INDEX_MAX_FILE_BYTES = 256 * 1024 * 1024;
|
|
@@ -27679,7 +27085,7 @@ if (process.argv[2] === "agent-help") {
|
|
|
27679
27085
|
await runFileCli2(process.argv.slice(3));
|
|
27680
27086
|
} else if (process.argv[2] === "status") {
|
|
27681
27087
|
const { runStatusCli: runStatusCli2 } = await Promise.resolve().then(() => (init_status_cli(), exports_status_cli));
|
|
27682
|
-
runStatusCli2(process.argv.slice(3));
|
|
27088
|
+
await runStatusCli2(process.argv.slice(3));
|
|
27683
27089
|
} else if (process.argv[2] === "skill") {
|
|
27684
27090
|
const { runSkillCli: runSkillCli2 } = await Promise.resolve().then(() => (init_skill_cli(), exports_skill_cli));
|
|
27685
27091
|
runSkillCli2(process.argv.slice(3));
|