agy-powerline 1.2.0 → 1.4.0
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/CHANGELOG.md +72 -0
- package/README.fr.md +243 -0
- package/README.ja.md +144 -49
- package/README.md +160 -52
- package/README.zh-CN.md +144 -49
- package/package.json +5 -2
- package/src/cli/caps.js +2 -0
- package/src/cli/configure.js +70 -172
- package/src/cli/doctor.js +12 -0
- package/src/cli/export.js +82 -0
- package/src/cli/gradient.js +121 -0
- package/src/cli/import.js +273 -0
- package/src/cli/init.js +186 -7
- package/src/cli/layout.js +2 -0
- package/src/cli/preset.js +2 -0
- package/src/cli/repo-url.js +7 -0
- package/src/cli/theme.js +2 -0
- package/src/cli/uninstall.js +6 -2
- package/src/index.js +53 -1
- package/src/lib/catalog.js +54 -2
- package/src/lib/colors.js +144 -14
- package/src/lib/config-check.js +74 -0
- package/src/lib/config.js +89 -21
- package/src/lib/exec.js +140 -21
- package/src/lib/fit.js +7 -0
- package/src/lib/formatters.js +67 -0
- package/src/lib/git.js +385 -4
- package/src/lib/layouts/lines.js +175 -28
- package/src/lib/layouts/pills.js +232 -33
- package/src/lib/paths.js +73 -13
- package/src/lib/prompt.js +30 -0
- package/src/lib/run.js +2 -0
- package/src/lib/session.js +17 -3
- package/src/lib/shared.js +13 -2
- package/src/lib/themes.js +79 -5
- package/src/lib/usage.js +524 -0
- package/src/lib/widget-options.js +194 -0
- package/src/lib/widget-style.js +45 -0
- package/src/runtime/statusline.js +15 -1
package/src/lib/git.js
CHANGED
|
@@ -13,6 +13,15 @@ const SYNC_CACHE_NAME = ".git-sync-cache.json";
|
|
|
13
13
|
const SYNC_CACHE_MAX = 20;
|
|
14
14
|
const COMMIT = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
15
15
|
|
|
16
|
+
// A git call that runs past its time is skipped until a later refresh. Tests
|
|
17
|
+
// give git more time: on a busy CI machine a plain call can take longer.
|
|
18
|
+
const timeouts = { sync: 150, detail: 300 };
|
|
19
|
+
function setGitTimeouts(next) {
|
|
20
|
+
const previous = { ...timeouts };
|
|
21
|
+
Object.assign(timeouts, next);
|
|
22
|
+
return previous;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
// Only an absolute folder that exists can belong to the user's session. A
|
|
17
26
|
// relative path (a Windows path read on Linux, for one) resolves against
|
|
18
27
|
// wherever this process runs, and the upward walk would then report that
|
|
@@ -89,6 +98,84 @@ function gitHasDirtyMarkers(gitDir) {
|
|
|
89
98
|
});
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
function readText(file) {
|
|
102
|
+
try {
|
|
103
|
+
return fs.readFileSync(file, "utf8").trim();
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function pathExists(file) {
|
|
110
|
+
try {
|
|
111
|
+
fs.accessSync(file);
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const STEP_MAX = 100000;
|
|
119
|
+
|
|
120
|
+
// The repo controls these files: only a plain whole number in range counts.
|
|
121
|
+
function readStep(file) {
|
|
122
|
+
const text = readText(file);
|
|
123
|
+
const n = text !== null && /^\d{1,6}$/.test(text) ? Number(text) : 0;
|
|
124
|
+
return n >= 1 && n <= STEP_MAX ? n : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// What an unfinished rebase keeps, by backend: [folder, step file, total file].
|
|
128
|
+
const REBASE_STATES = [
|
|
129
|
+
["rebase-merge", "msgnum", "end"],
|
|
130
|
+
["rebase-apply", "next", "last"],
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
// Checked in this order after a rebase or `git am`; the first one found wins.
|
|
134
|
+
const OPERATION_MARKERS = [
|
|
135
|
+
["MERGE_HEAD", "merge"],
|
|
136
|
+
["CHERRY_PICK_HEAD", "cherry-pick"],
|
|
137
|
+
["REVERT_HEAD", "revert"],
|
|
138
|
+
["BISECT_LOG", "bisect"],
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
// The operation git is in the middle of in this worktree, from files only:
|
|
142
|
+
// { name, step, total, folder } or null. A linked worktree keeps these in its
|
|
143
|
+
// own git dir, which findGitDir() returns.
|
|
144
|
+
function readOperation(gitDir) {
|
|
145
|
+
for (const [folder, stepFile, totalFile] of REBASE_STATES) {
|
|
146
|
+
const dir = path.join(gitDir, folder);
|
|
147
|
+
let name = folder === "rebase-merge" || pathExists(path.join(dir, "rebasing")) ? "rebase" : "";
|
|
148
|
+
if (!name && pathExists(path.join(dir, "applying"))) {
|
|
149
|
+
name = "am";
|
|
150
|
+
}
|
|
151
|
+
if (!name || !pathExists(dir)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const step = readStep(path.join(dir, stepFile));
|
|
155
|
+
const total = readStep(path.join(dir, totalFile));
|
|
156
|
+
const counted = step !== null && total !== null;
|
|
157
|
+
return { name, step: counted ? step : null, total: counted ? total : null, folder };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const [file, name] of OPERATION_MARKERS) {
|
|
161
|
+
if (pathExists(path.join(gitDir, file))) {
|
|
162
|
+
return { name, step: null, total: null, folder: "" };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// A rebase detaches HEAD; the branch being rebased is named in head-name.
|
|
170
|
+
function rebasedBranch(gitDir, operation) {
|
|
171
|
+
if (operation?.name !== "rebase") {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
const ref = readText(path.join(gitDir, operation.folder, "head-name")) || "";
|
|
175
|
+
const branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : "";
|
|
176
|
+
return isSafeBranchName(branch) ? branch : "";
|
|
177
|
+
}
|
|
178
|
+
|
|
92
179
|
function getGitInfo(cwd) {
|
|
93
180
|
if (!cwd) {
|
|
94
181
|
return null;
|
|
@@ -102,12 +189,13 @@ function getGitInfo(cwd) {
|
|
|
102
189
|
}
|
|
103
190
|
|
|
104
191
|
const head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
|
|
192
|
+
const operation = readOperation(gitDir);
|
|
105
193
|
let branch = "";
|
|
106
194
|
|
|
107
195
|
if (head.startsWith("ref: ")) {
|
|
108
196
|
branch = head.replace(/^ref:\s+refs\/heads\//, "").replace(/^ref:\s+/, "");
|
|
109
197
|
} else if (head) {
|
|
110
|
-
branch = head.slice(0, 7);
|
|
198
|
+
branch = rebasedBranch(gitDir, operation) || head.slice(0, 7);
|
|
111
199
|
}
|
|
112
200
|
|
|
113
201
|
if (!branch) {
|
|
@@ -121,6 +209,11 @@ function getGitInfo(cwd) {
|
|
|
121
209
|
dirty: gitHasDirtyMarkers(gitDir),
|
|
122
210
|
ahead: sync.ahead,
|
|
123
211
|
behind: sync.behind,
|
|
212
|
+
operation: operation && {
|
|
213
|
+
name: operation.name,
|
|
214
|
+
step: operation.step,
|
|
215
|
+
total: operation.total,
|
|
216
|
+
},
|
|
124
217
|
};
|
|
125
218
|
} catch {
|
|
126
219
|
return null;
|
|
@@ -304,10 +397,11 @@ function getGitSync(gitDir, branch) {
|
|
|
304
397
|
const { execFileSync } = require("child_process");
|
|
305
398
|
const out = execFileSync(
|
|
306
399
|
"git",
|
|
307
|
-
|
|
400
|
+
// The branch's own upstream: during a rebase HEAD is detached and has none.
|
|
401
|
+
["rev-list", "--left-right", "--count", `${branch}...${branch}@{upstream}`, "--"],
|
|
308
402
|
{
|
|
309
403
|
cwd: path.dirname(gitDir),
|
|
310
|
-
timeout:
|
|
404
|
+
timeout: timeouts.sync,
|
|
311
405
|
stdio: ["ignore", "pipe", "ignore"],
|
|
312
406
|
encoding: "utf8",
|
|
313
407
|
// Antigravity may run the statusline without a console; without this,
|
|
@@ -329,7 +423,7 @@ function getGitSync(gitDir, branch) {
|
|
|
329
423
|
// A timeout or missing git is worth knowing about when ahead/behind
|
|
330
424
|
// counts never show.
|
|
331
425
|
if (err.code === "ETIMEDOUT" || err.signal === "SIGTERM") {
|
|
332
|
-
logWarning("GIT_TIMEOUT", { timeoutMs:
|
|
426
|
+
logWarning("GIT_TIMEOUT", { timeoutMs: timeouts.sync });
|
|
333
427
|
} else if (err.code === "ENOENT") {
|
|
334
428
|
logWarning("GIT_NOT_FOUND");
|
|
335
429
|
}
|
|
@@ -338,6 +432,279 @@ function getGitSync(gitDir, branch) {
|
|
|
338
432
|
return { ahead: 0, behind: 0 };
|
|
339
433
|
}
|
|
340
434
|
|
|
435
|
+
// Working-tree details for the gitChanges, gitFiles, gitSha, gitStash and
|
|
436
|
+
// gitWorktree widgets. SHA, stash and worktree come from files; file and line
|
|
437
|
+
// counts need `git status` / `git diff`, so those run only for the widgets
|
|
438
|
+
// that show them and are reused for STATUS_TTL_MS per repo and commit.
|
|
439
|
+
const DETAIL_NEEDS = {
|
|
440
|
+
gitChanges: "diff",
|
|
441
|
+
gitFiles: "status",
|
|
442
|
+
gitSha: "sha",
|
|
443
|
+
gitStash: "stash",
|
|
444
|
+
gitWorktree: "worktree",
|
|
445
|
+
};
|
|
446
|
+
const STATUS_CACHE_NAME = ".git-status-cache.json";
|
|
447
|
+
const STATUS_CACHE_MAX = 20;
|
|
448
|
+
const STATUS_TTL_MS = 5000;
|
|
449
|
+
|
|
450
|
+
// No optional locks: the bar must never hold the index lock and make the
|
|
451
|
+
// user's own git commands fail. The -c flags and the submodule skip keep git
|
|
452
|
+
// from starting programs named in the repo's own config.
|
|
453
|
+
const STATUS_ARGS = [
|
|
454
|
+
"--no-optional-locks",
|
|
455
|
+
"-c",
|
|
456
|
+
"core.fsmonitor=false",
|
|
457
|
+
"-c",
|
|
458
|
+
"core.untrackedCache=false",
|
|
459
|
+
"status",
|
|
460
|
+
"--porcelain=v2",
|
|
461
|
+
"-z",
|
|
462
|
+
"--ignore-submodules=all",
|
|
463
|
+
];
|
|
464
|
+
const DIFF_ARGS = [
|
|
465
|
+
"--no-optional-locks",
|
|
466
|
+
"-c",
|
|
467
|
+
"core.fsmonitor=false",
|
|
468
|
+
"diff",
|
|
469
|
+
"--shortstat",
|
|
470
|
+
"--no-ext-diff",
|
|
471
|
+
"--no-textconv",
|
|
472
|
+
"--ignore-submodules=all",
|
|
473
|
+
"HEAD",
|
|
474
|
+
"--",
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
// A repo opened from an archive controls its own .git/config, and git status
|
|
478
|
+
// and diff run the programs it names: filters on changed files, diff drivers,
|
|
479
|
+
// fsmonitor hooks, and anything an included file adds. A repo config with any
|
|
480
|
+
// of these is read for SHA, stash and worktree only; git itself isn't started.
|
|
481
|
+
const RISKY_CONFIG = /^\s*(\[\s*(filter|include|includeif|diff)\b|fsmonitor\s*=)/im;
|
|
482
|
+
|
|
483
|
+
const statusMemory = new Map();
|
|
484
|
+
|
|
485
|
+
function hasRiskyRepoConfig(gitDir, commonDir) {
|
|
486
|
+
const files = [path.join(commonDir, "config"), path.join(gitDir, "config.worktree")];
|
|
487
|
+
if (gitDir !== commonDir) {
|
|
488
|
+
files.push(path.join(commonDir, "config.worktree"));
|
|
489
|
+
}
|
|
490
|
+
return files.some((file) => {
|
|
491
|
+
try {
|
|
492
|
+
return RISKY_CONFIG.test(fs.readFileSync(file, "utf8"));
|
|
493
|
+
} catch {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// The branch checked out and its commit ("" before the first commit).
|
|
500
|
+
function readHead(gitDir, commonDir) {
|
|
501
|
+
let head;
|
|
502
|
+
try {
|
|
503
|
+
head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
|
|
504
|
+
} catch {
|
|
505
|
+
return { branch: "", commit: "" };
|
|
506
|
+
}
|
|
507
|
+
if (COMMIT.test(head)) {
|
|
508
|
+
return { branch: head.slice(0, 7), commit: head };
|
|
509
|
+
}
|
|
510
|
+
const ref = head.replace(/^ref:\s+/, "");
|
|
511
|
+
const branch = ref.replace(/^refs\/heads\//, "");
|
|
512
|
+
if (!ref.startsWith("refs/heads/") || !isSafeBranchName(branch)) {
|
|
513
|
+
return { branch: "", commit: "" };
|
|
514
|
+
}
|
|
515
|
+
const commit = readRef(commonDir, ref);
|
|
516
|
+
return { branch, commit: COMMIT.test(commit) ? commit : "" };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// One line per stash in the stash reflog.
|
|
520
|
+
function stashCount(commonDir) {
|
|
521
|
+
try {
|
|
522
|
+
const text = fs.readFileSync(path.join(commonDir, "logs", "refs", "stash"), "utf8");
|
|
523
|
+
return Math.min(text.split("\n").filter((line) => line.trim()).length, 99999);
|
|
524
|
+
} catch {
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// A linked worktree's git dir is <main>/.git/worktrees/<name>.
|
|
530
|
+
function worktreeName(gitDir, commonDir) {
|
|
531
|
+
if (gitDir === commonDir || path.basename(path.dirname(gitDir)) !== "worktrees") {
|
|
532
|
+
return "";
|
|
533
|
+
}
|
|
534
|
+
return path.basename(gitDir);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Porcelain v2 with -z: "1 XY ..." and "2 XY ..." (a rename, followed by its
|
|
538
|
+
// old path as its own entry), "u XY ..." for conflicts, "? path" for untracked.
|
|
539
|
+
function parseStatus(out) {
|
|
540
|
+
const files = { staged: 0, modified: 0, untracked: 0, conflicts: 0 };
|
|
541
|
+
const entries = String(out).split("\0");
|
|
542
|
+
for (let i = 0; i < entries.length; i++) {
|
|
543
|
+
const entry = entries[i];
|
|
544
|
+
const kind = entry[0];
|
|
545
|
+
if (kind === "1" || kind === "2") {
|
|
546
|
+
if (entry[2] !== ".") files.staged++;
|
|
547
|
+
if (entry[3] !== ".") files.modified++;
|
|
548
|
+
if (kind === "2") i++;
|
|
549
|
+
} else if (kind === "u") {
|
|
550
|
+
files.conflicts++;
|
|
551
|
+
} else if (kind === "?") {
|
|
552
|
+
files.untracked++;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return files;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// " 3 files changed, 128 insertions(+), 34 deletions(-)"; nothing when clean.
|
|
559
|
+
function parseShortstat(out) {
|
|
560
|
+
const text = String(out);
|
|
561
|
+
const number = (pattern) => parseInt(text.match(pattern)?.[1] ?? "0", 10) || 0;
|
|
562
|
+
return { added: number(/(\d+) insertions?\(\+\)/), removed: number(/(\d+) deletions?\(-\)/) };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function runGitDetail(cwd, args) {
|
|
566
|
+
try {
|
|
567
|
+
const { execFileSync } = require("child_process");
|
|
568
|
+
return execFileSync("git", args, {
|
|
569
|
+
cwd,
|
|
570
|
+
timeout: timeouts.detail,
|
|
571
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
572
|
+
encoding: "utf8",
|
|
573
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
574
|
+
windowsHide: true,
|
|
575
|
+
});
|
|
576
|
+
} catch (err) {
|
|
577
|
+
if (err.code === "ETIMEDOUT" || err.signal === "SIGTERM") {
|
|
578
|
+
logWarning("GIT_TIMEOUT", { timeoutMs: timeouts.detail });
|
|
579
|
+
} else if (err.code === "ENOENT") {
|
|
580
|
+
logWarning("GIT_NOT_FOUND");
|
|
581
|
+
}
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function statusCachePath() {
|
|
587
|
+
const sync = syncCachePath();
|
|
588
|
+
return sync ? path.join(path.dirname(sync), STATUS_CACHE_NAME) : null;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const DETAIL_FIELDS = {
|
|
592
|
+
files: ["staged", "modified", "untracked", "conflicts"],
|
|
593
|
+
changes: ["added", "removed"],
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// The cache file can be edited or torn like any file; only whole counts count.
|
|
597
|
+
function validDetail(field, cached) {
|
|
598
|
+
const v = cached?.v;
|
|
599
|
+
return (
|
|
600
|
+
typeof cached?.t === "number" &&
|
|
601
|
+
v !== null &&
|
|
602
|
+
typeof v === "object" &&
|
|
603
|
+
DETAIL_FIELDS[field].every(
|
|
604
|
+
(name) => Number.isInteger(v[name]) && v[name] >= 0 && v[name] <= 1e9
|
|
605
|
+
)
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// A fresh entry is used as is; when git fails, an older one for the same repo
|
|
610
|
+
// and commit is still better than nothing.
|
|
611
|
+
function cachedDetail(key, field, compute) {
|
|
612
|
+
const now = Date.now();
|
|
613
|
+
const file = statusCachePath();
|
|
614
|
+
let entry = statusMemory.get(key);
|
|
615
|
+
if (!entry?.[field] && file) {
|
|
616
|
+
entry = readSyncCache(file)[key];
|
|
617
|
+
}
|
|
618
|
+
const cached = validDetail(field, entry?.[field]) ? entry[field] : null;
|
|
619
|
+
if (cached && now - cached.t < STATUS_TTL_MS) {
|
|
620
|
+
return cached.v;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const value = compute();
|
|
624
|
+
if (value === null) {
|
|
625
|
+
return cached?.v ?? null;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const previous = entry && typeof entry === "object" && !Array.isArray(entry) ? entry : {};
|
|
629
|
+
const next = { ...previous, [field]: { t: now, v: value } };
|
|
630
|
+
statusMemory.set(key, next);
|
|
631
|
+
if (file) {
|
|
632
|
+
try {
|
|
633
|
+
const cache = readSyncCache(file);
|
|
634
|
+
delete cache[key];
|
|
635
|
+
cache[key] = next;
|
|
636
|
+
const kept = Object.fromEntries(Object.entries(cache).slice(-STATUS_CACHE_MAX));
|
|
637
|
+
writeFileAtomic(file, JSON.stringify(kept), { mode: PRIVATE_FILE_MODE });
|
|
638
|
+
} catch {
|
|
639
|
+
// Best effort: the next refresh runs git again.
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return value;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function getGitDetails(cwd, needs = {}) {
|
|
646
|
+
try {
|
|
647
|
+
const gitDir = findGitDir(cwd);
|
|
648
|
+
if (!gitDir) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
const commonDir = commonGitDir(gitDir);
|
|
652
|
+
const { branch, commit } = readHead(gitDir, commonDir);
|
|
653
|
+
const details = { branch };
|
|
654
|
+
|
|
655
|
+
if (needs.sha) {
|
|
656
|
+
details.sha = commit.slice(0, 7);
|
|
657
|
+
}
|
|
658
|
+
if (needs.stash) {
|
|
659
|
+
details.stash = stashCount(commonDir);
|
|
660
|
+
}
|
|
661
|
+
if (needs.worktree) {
|
|
662
|
+
details.worktree = worktreeName(gitDir, commonDir);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const wantsGit = needs.status || (needs.diff && commit);
|
|
666
|
+
if (wantsGit && hasRiskyRepoConfig(gitDir, commonDir)) {
|
|
667
|
+
logWarning("GIT_CONFIG_SKIPPED");
|
|
668
|
+
return details;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const key = `${gitDir}\0${commit || "unborn"}`;
|
|
672
|
+
if (needs.status) {
|
|
673
|
+
details.files = cachedDetail(key, "files", () => {
|
|
674
|
+
const out = runGitDetail(cwd, STATUS_ARGS);
|
|
675
|
+
return out === null ? null : parseStatus(out);
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
if (needs.diff && commit) {
|
|
679
|
+
details.changes = cachedDetail(key, "changes", () => {
|
|
680
|
+
const out = runGitDetail(cwd, DIFF_ARGS);
|
|
681
|
+
return out === null ? null : parseShortstat(out);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
return details;
|
|
685
|
+
} catch {
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Only what the configured widgets show is read.
|
|
691
|
+
function resolveGitDetails(data, config) {
|
|
692
|
+
const needs = {};
|
|
693
|
+
for (const widget of config?.widgets ?? []) {
|
|
694
|
+
if (DETAIL_NEEDS[widget?.id]) {
|
|
695
|
+
needs[DETAIL_NEEDS[widget.id]] = true;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (!Object.keys(needs).length) {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
return getGitDetails(firstText(data?.cwd, data?.workspace?.current_dir), needs);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function clearGitDetailsCache() {
|
|
705
|
+
statusMemory.clear();
|
|
706
|
+
}
|
|
707
|
+
|
|
341
708
|
// The bar uses vcs.branch and vcs.dirty from Antigravity's payload. Ahead and
|
|
342
709
|
// behind are not in it; they come from the local repository.
|
|
343
710
|
function vcsFromPayload(data) {
|
|
@@ -369,6 +736,7 @@ function resolveGit(data, config) {
|
|
|
369
736
|
...fromPayload,
|
|
370
737
|
ahead: same ? local.ahead : 0,
|
|
371
738
|
behind: same ? local.behind : 0,
|
|
739
|
+
operation: same ? local.operation : null,
|
|
372
740
|
};
|
|
373
741
|
}
|
|
374
742
|
|
|
@@ -378,6 +746,7 @@ function resolveGit(data, config) {
|
|
|
378
746
|
dirty: Boolean(local.dirty || fromPayload?.dirty),
|
|
379
747
|
ahead: local.ahead,
|
|
380
748
|
behind: local.behind,
|
|
749
|
+
operation: local.operation,
|
|
381
750
|
};
|
|
382
751
|
}
|
|
383
752
|
|
|
@@ -388,6 +757,7 @@ function resolveGit(data, config) {
|
|
|
388
757
|
module.exports = {
|
|
389
758
|
findGitDir,
|
|
390
759
|
getGitInfo,
|
|
760
|
+
readOperation,
|
|
391
761
|
getGitSync,
|
|
392
762
|
isSafeBranchName,
|
|
393
763
|
GIT_DIRTY_MARKERS,
|
|
@@ -395,4 +765,15 @@ module.exports = {
|
|
|
395
765
|
vcsFromPayload,
|
|
396
766
|
resolveGit,
|
|
397
767
|
SYNC_CACHE_NAME,
|
|
768
|
+
STATUS_CACHE_NAME,
|
|
769
|
+
STATUS_TTL_MS,
|
|
770
|
+
STATUS_ARGS,
|
|
771
|
+
DIFF_ARGS,
|
|
772
|
+
hasRiskyRepoConfig,
|
|
773
|
+
parseStatus,
|
|
774
|
+
parseShortstat,
|
|
775
|
+
getGitDetails,
|
|
776
|
+
resolveGitDetails,
|
|
777
|
+
clearGitDetailsCache,
|
|
778
|
+
setGitTimeouts,
|
|
398
779
|
};
|