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