dsh-gitnexus-plus 0.0.0-stage → 0.1.1
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/LICENSE +38 -0
- package/README.md +483 -2
- package/cordis.patch.yml +18 -0
- package/lib/bin/gitnexus-coverage.d.ts +14 -0
- package/lib/bin/gitnexus-coverage.d.ts.map +1 -0
- package/lib/bin/gitnexus-coverage.js +171 -0
- package/lib/bin/gitnexus-coverage.js.map +1 -0
- package/lib/client.js +1642 -0
- package/lib/gitnexus/analyze.d.ts +148 -0
- package/lib/gitnexus/analyze.d.ts.map +1 -0
- package/lib/gitnexus/analyze.js +283 -0
- package/lib/gitnexus/analyze.js.map +1 -0
- package/lib/gitnexus/commands.d.ts +96 -0
- package/lib/gitnexus/commands.d.ts.map +1 -0
- package/lib/gitnexus/commands.js +132 -0
- package/lib/gitnexus/commands.js.map +1 -0
- package/lib/gitnexus/coverage.d.ts +340 -0
- package/lib/gitnexus/coverage.d.ts.map +1 -0
- package/lib/gitnexus/coverage.js +552 -0
- package/lib/gitnexus/coverage.js.map +1 -0
- package/lib/gitnexus/format.d.ts +16 -0
- package/lib/gitnexus/format.d.ts.map +1 -0
- package/lib/gitnexus/format.js +178 -0
- package/lib/gitnexus/format.js.map +1 -0
- package/lib/gitnexus/git-activity.d.ts +136 -0
- package/lib/gitnexus/git-activity.d.ts.map +1 -0
- package/lib/gitnexus/git-activity.js +345 -0
- package/lib/gitnexus/git-activity.js.map +1 -0
- package/lib/gitnexus/git.d.ts +70 -0
- package/lib/gitnexus/git.d.ts.map +1 -0
- package/lib/gitnexus/git.js +121 -0
- package/lib/gitnexus/git.js.map +1 -0
- package/lib/gitnexus/index-meta.d.ts +150 -0
- package/lib/gitnexus/index-meta.d.ts.map +1 -0
- package/lib/gitnexus/index-meta.js +156 -0
- package/lib/gitnexus/index-meta.js.map +1 -0
- package/lib/gitnexus/install.d.ts +97 -0
- package/lib/gitnexus/install.d.ts.map +1 -0
- package/lib/gitnexus/install.js +228 -0
- package/lib/gitnexus/install.js.map +1 -0
- package/lib/gitnexus/languages.d.ts +73 -0
- package/lib/gitnexus/languages.d.ts.map +1 -0
- package/lib/gitnexus/languages.js +217 -0
- package/lib/gitnexus/languages.js.map +1 -0
- package/lib/gitnexus/root.d.ts +68 -0
- package/lib/gitnexus/root.d.ts.map +1 -0
- package/lib/gitnexus/root.js +162 -0
- package/lib/gitnexus/root.js.map +1 -0
- package/lib/gitnexus/scan.d.ts +67 -0
- package/lib/gitnexus/scan.d.ts.map +1 -0
- package/lib/gitnexus/scan.js +247 -0
- package/lib/gitnexus/scan.js.map +1 -0
- package/lib/gitnexus/toolchain.d.ts +136 -0
- package/lib/gitnexus/toolchain.d.ts.map +1 -0
- package/lib/gitnexus/toolchain.js +251 -0
- package/lib/gitnexus/toolchain.js.map +1 -0
- package/lib/index.d.ts +79 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +211 -0
- package/lib/index.js.map +1 -0
- package/lib/routes/analyze.d.ts +35 -0
- package/lib/routes/analyze.d.ts.map +1 -0
- package/lib/routes/analyze.js +71 -0
- package/lib/routes/analyze.js.map +1 -0
- package/lib/routes/coverage.d.ts +44 -0
- package/lib/routes/coverage.d.ts.map +1 -0
- package/lib/routes/coverage.js +68 -0
- package/lib/routes/coverage.js.map +1 -0
- package/lib/routes/guard.d.ts +92 -0
- package/lib/routes/guard.d.ts.map +1 -0
- package/lib/routes/guard.js +187 -0
- package/lib/routes/guard.js.map +1 -0
- package/lib/routes/install.d.ts +27 -0
- package/lib/routes/install.d.ts.map +1 -0
- package/lib/routes/install.js +46 -0
- package/lib/routes/install.js.map +1 -0
- package/package.json +87 -4
- package/scripts/link-runtime.mjs +146 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git activity, correlated with the index.
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS ADDS THAT PLAIN `git log` DOES NOT
|
|
5
|
+
* -------------------------------------------
|
|
6
|
+
* GitNexus is built on git, and its manifest records a content hash for every file
|
|
7
|
+
* it indexed. Crossing recent git activity with that set answers a question neither
|
|
8
|
+
* tool answers alone:
|
|
9
|
+
*
|
|
10
|
+
* of the files people are actively changing right now,
|
|
11
|
+
* how many does the knowledge graph even see?
|
|
12
|
+
*
|
|
13
|
+
* A file that is edited weekly but absent from the index contributes nothing to
|
|
14
|
+
* `impact`, `context` or `query` — and nothing in GitNexus's own UI says so. That
|
|
15
|
+
* is the `indexed` flag on every row here.
|
|
16
|
+
*
|
|
17
|
+
* BOUNDS ARE PART OF THE CONTRACT
|
|
18
|
+
* -------------------------------
|
|
19
|
+
* `git log --numstat` on a large repository is unbounded work, so every dimension
|
|
20
|
+
* is capped and the result says when a cap was hit (`window.truncated`). A silently
|
|
21
|
+
* truncated activity list reads as "this is all the activity there was", which is
|
|
22
|
+
* the one thing it must not mean.
|
|
23
|
+
*/
|
|
24
|
+
import { execFile } from 'node:child_process';
|
|
25
|
+
import { promisify } from 'node:util';
|
|
26
|
+
import { normalizePathKey } from './index-meta.js';
|
|
27
|
+
const run = promisify(execFile);
|
|
28
|
+
/** Generous but finite: the log is informational, and it must not outlast the scan. */
|
|
29
|
+
const TIMEOUT_MS = 20_000;
|
|
30
|
+
/** Record separator (RS) opens each commit; unit separator (US) separates its fields. */
|
|
31
|
+
const RS = '\u001e';
|
|
32
|
+
const US = '\u001f';
|
|
33
|
+
const DEFAULTS = Object.freeze({
|
|
34
|
+
sinceDays: 90,
|
|
35
|
+
maxCommits: 300,
|
|
36
|
+
maxFiles: 200,
|
|
37
|
+
maxAuthors: 20,
|
|
38
|
+
maxBranches: 20,
|
|
39
|
+
});
|
|
40
|
+
async function git(repoPath, args, maxBuffer = 32 * 1024 * 1024) {
|
|
41
|
+
try {
|
|
42
|
+
const { stdout } = await run('git', args, {
|
|
43
|
+
cwd: repoPath,
|
|
44
|
+
timeout: TIMEOUT_MS,
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
maxBuffer,
|
|
47
|
+
});
|
|
48
|
+
return stdout;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// `maxBuffer` overflow still leaves usable prefix output on some platforms, but
|
|
52
|
+
// treating it as failure is honest: a partial parse would understate activity.
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether an author is a machine account.
|
|
58
|
+
*
|
|
59
|
+
* Worth separating because a bot's commit count and its file count disagree in a
|
|
60
|
+
* way a human's never does — measured on a real repository, `dependabot[bot]` was
|
|
61
|
+
* the second-busiest author with 80 commits against just 12 files. Left in the same
|
|
62
|
+
* list, it makes "who is active here" unreadable.
|
|
63
|
+
*/
|
|
64
|
+
function looksLikeBot(name, email) {
|
|
65
|
+
return (/\[bot\]$/i.test(name) ||
|
|
66
|
+
/(^|[-_.])bot@/i.test(email) ||
|
|
67
|
+
/^(dependabot|renovate|github-actions|greenkeeper)/i.test(name));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Bucket commit stamps into a time histogram.
|
|
71
|
+
*
|
|
72
|
+
* Daily buckets while the window is short enough to read, weekly once it is not:
|
|
73
|
+
* 90 daily columns in a sidebar is a smear, not a chart. The bucket width is
|
|
74
|
+
* returned so the axis can be labelled with the real unit instead of implying one.
|
|
75
|
+
*
|
|
76
|
+
* Empty buckets are materialised rather than skipped — a gap in a bar chart must
|
|
77
|
+
* read as "no commits that day", and inferring that from irregular spacing is
|
|
78
|
+
* exactly the misreading the chart exists to prevent.
|
|
79
|
+
*/
|
|
80
|
+
export function buildHistogram(stamps, sinceDays) {
|
|
81
|
+
if (stamps.length === 0)
|
|
82
|
+
return { bucketDays: 1, buckets: [] };
|
|
83
|
+
const bucketDays = sinceDays > 60 ? 7 : 1;
|
|
84
|
+
const sorted = [...stamps].sort((a, b) => (a.date < b.date ? -1 : 1));
|
|
85
|
+
const first = Date.parse(sorted[0].date);
|
|
86
|
+
const last = Date.parse(sorted[sorted.length - 1].date);
|
|
87
|
+
if (!Number.isFinite(first) || !Number.isFinite(last))
|
|
88
|
+
return { bucketDays, buckets: [] };
|
|
89
|
+
const dayMs = 86_400_000;
|
|
90
|
+
const byIndex = new Map();
|
|
91
|
+
for (const stamp of sorted) {
|
|
92
|
+
const at = Date.parse(stamp.date);
|
|
93
|
+
if (!Number.isFinite(at))
|
|
94
|
+
continue;
|
|
95
|
+
const index = Math.floor((at - first) / dayMs / bucketDays);
|
|
96
|
+
let bucket = byIndex.get(index);
|
|
97
|
+
if (bucket === undefined) {
|
|
98
|
+
const startMs = first + index * bucketDays * dayMs;
|
|
99
|
+
bucket = {
|
|
100
|
+
label: new Date(startMs).toISOString().slice(0, 10),
|
|
101
|
+
commits: 0,
|
|
102
|
+
insertions: 0,
|
|
103
|
+
deletions: 0,
|
|
104
|
+
};
|
|
105
|
+
byIndex.set(index, bucket);
|
|
106
|
+
}
|
|
107
|
+
bucket.commits += 1;
|
|
108
|
+
bucket.insertions += stamp.insertions;
|
|
109
|
+
bucket.deletions += stamp.deletions;
|
|
110
|
+
}
|
|
111
|
+
const count = Math.max(...byIndex.keys()) + 1;
|
|
112
|
+
const buckets = [];
|
|
113
|
+
for (let i = 0; i < count; i += 1) {
|
|
114
|
+
const existing = byIndex.get(i);
|
|
115
|
+
if (existing !== undefined) {
|
|
116
|
+
buckets.push(existing);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const startMs = first + i * bucketDays * dayMs;
|
|
120
|
+
buckets.push({
|
|
121
|
+
label: new Date(startMs).toISOString().slice(0, 10),
|
|
122
|
+
commits: 0,
|
|
123
|
+
insertions: 0,
|
|
124
|
+
deletions: 0,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return { bucketDays, buckets };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Read recent commits, authors, changed files and branches.
|
|
131
|
+
*
|
|
132
|
+
* Returns `null` when git cannot answer at all.
|
|
133
|
+
*/
|
|
134
|
+
export async function readGitActivity(repoPath, options = {}) {
|
|
135
|
+
// The window is expressed in days rather than as a git date expression so the
|
|
136
|
+
// result can be *labelled* without parsing one back out. A first revision passed
|
|
137
|
+
// the raw spec straight into the UI, which rendered "近 90 days ago 内".
|
|
138
|
+
const sinceDays = options.sinceDays ?? DEFAULTS.sinceDays;
|
|
139
|
+
const since = `${sinceDays} days ago`;
|
|
140
|
+
const maxCommits = options.maxCommits ?? DEFAULTS.maxCommits;
|
|
141
|
+
const maxFiles = options.maxFiles ?? DEFAULTS.maxFiles;
|
|
142
|
+
const maxAuthors = options.maxAuthors ?? DEFAULTS.maxAuthors;
|
|
143
|
+
const maxBranches = options.maxBranches ?? DEFAULTS.maxBranches;
|
|
144
|
+
const indexed = new Set([...(options.indexedPaths ?? [])].map((p) => normalizePathKey(p)));
|
|
145
|
+
const scannable = new Set([...(options.scannablePaths ?? [])].map((p) => normalizePathKey(p)));
|
|
146
|
+
const classified = indexed.size > 0 || scannable.size > 0;
|
|
147
|
+
/**
|
|
148
|
+
* Three-way, because "not in the index" is two different facts. A `.gitignore`
|
|
149
|
+
* or a `package-lock.json` is absent by design; a source file a fresh scan would
|
|
150
|
+
* pick up is absent because the index is behind. Reporting them as one number
|
|
151
|
+
* would make every repository look badly stale.
|
|
152
|
+
*/
|
|
153
|
+
const classify = (filePath) => {
|
|
154
|
+
const key = normalizePathKey(filePath);
|
|
155
|
+
if (indexed.has(key))
|
|
156
|
+
return 'indexed';
|
|
157
|
+
if (scannable.size > 0 && scannable.has(key))
|
|
158
|
+
return 'missing';
|
|
159
|
+
return 'excluded';
|
|
160
|
+
};
|
|
161
|
+
const log = await git(repoPath, [
|
|
162
|
+
'log',
|
|
163
|
+
`--since=${since}`,
|
|
164
|
+
`--max-count=${maxCommits}`,
|
|
165
|
+
'--numstat',
|
|
166
|
+
'--no-renames',
|
|
167
|
+
`--format=${RS}%H${US}%an${US}%ae${US}%aI${US}%s`,
|
|
168
|
+
]);
|
|
169
|
+
if (log === null)
|
|
170
|
+
return null;
|
|
171
|
+
const authors = [];
|
|
172
|
+
const files = new Map();
|
|
173
|
+
const authorMap = new Map();
|
|
174
|
+
let commitCount = 0;
|
|
175
|
+
let totalInsertions = 0;
|
|
176
|
+
let totalDeletions = 0;
|
|
177
|
+
let newestCommit = '';
|
|
178
|
+
let oldestCommit = '';
|
|
179
|
+
/** Per-commit stamps, for the time histogram. Bounded by `maxCommits`. */
|
|
180
|
+
const stamps = [];
|
|
181
|
+
for (const chunk of log.split(RS)) {
|
|
182
|
+
if (chunk.trim() === '')
|
|
183
|
+
continue;
|
|
184
|
+
const newline = chunk.indexOf('\n');
|
|
185
|
+
const header = newline === -1 ? chunk : chunk.slice(0, newline);
|
|
186
|
+
const body = newline === -1 ? '' : chunk.slice(newline + 1);
|
|
187
|
+
// `split` yields a plain array, so a short header really can leave these
|
|
188
|
+
// undefined even though the declared element type says otherwise.
|
|
189
|
+
const [sha, author, email, date, _subject] = header.split(US);
|
|
190
|
+
if (sha === undefined || date === undefined)
|
|
191
|
+
continue;
|
|
192
|
+
commitCount += 1;
|
|
193
|
+
if (newestCommit === '' || date > newestCommit)
|
|
194
|
+
newestCommit = date;
|
|
195
|
+
if (oldestCommit === '' || date < oldestCommit)
|
|
196
|
+
oldestCommit = date;
|
|
197
|
+
const key = `${author ?? '?'} <${email ?? '?'}>`;
|
|
198
|
+
let entry = authorMap.get(key);
|
|
199
|
+
if (entry === undefined) {
|
|
200
|
+
entry = {
|
|
201
|
+
name: author ?? '?',
|
|
202
|
+
email: email ?? '?',
|
|
203
|
+
commits: 0,
|
|
204
|
+
insertions: 0,
|
|
205
|
+
deletions: 0,
|
|
206
|
+
files: 0,
|
|
207
|
+
lastCommit: date,
|
|
208
|
+
fileSet: new Set(),
|
|
209
|
+
};
|
|
210
|
+
authorMap.set(key, entry);
|
|
211
|
+
}
|
|
212
|
+
entry.commits += 1;
|
|
213
|
+
if (date > entry.lastCommit)
|
|
214
|
+
entry.lastCommit = date;
|
|
215
|
+
let commitInsertions = 0;
|
|
216
|
+
let commitDeletions = 0;
|
|
217
|
+
for (const line of body.split('\n')) {
|
|
218
|
+
if (line.trim() === '')
|
|
219
|
+
continue;
|
|
220
|
+
const parts = line.split('\t');
|
|
221
|
+
if (parts.length < 3)
|
|
222
|
+
continue;
|
|
223
|
+
const [addedRaw, deletedRaw, ...pathParts] = parts;
|
|
224
|
+
const filePath = pathParts.join('\t');
|
|
225
|
+
if (filePath === '')
|
|
226
|
+
continue;
|
|
227
|
+
// `-` marks a binary file in --numstat; it counts as a change with no
|
|
228
|
+
// line delta, which is the truthful reading.
|
|
229
|
+
const added = Number.parseInt(addedRaw, 10);
|
|
230
|
+
const deleted = Number.parseInt(deletedRaw, 10);
|
|
231
|
+
const ins = Number.isFinite(added) ? added : 0;
|
|
232
|
+
const del = Number.isFinite(deleted) ? deleted : 0;
|
|
233
|
+
totalInsertions += ins;
|
|
234
|
+
totalDeletions += del;
|
|
235
|
+
commitInsertions += ins;
|
|
236
|
+
commitDeletions += del;
|
|
237
|
+
entry.insertions += ins;
|
|
238
|
+
entry.deletions += del;
|
|
239
|
+
entry.fileSet.add(filePath);
|
|
240
|
+
let file = files.get(filePath);
|
|
241
|
+
if (file === undefined) {
|
|
242
|
+
file = { path: filePath, changes: 0, insertions: 0, deletions: 0, lastChange: date };
|
|
243
|
+
files.set(filePath, file);
|
|
244
|
+
}
|
|
245
|
+
file.changes += 1;
|
|
246
|
+
file.insertions += ins;
|
|
247
|
+
file.deletions += del;
|
|
248
|
+
if (date > file.lastChange)
|
|
249
|
+
file.lastChange = date;
|
|
250
|
+
}
|
|
251
|
+
stamps.push({ date, insertions: commitInsertions, deletions: commitDeletions });
|
|
252
|
+
}
|
|
253
|
+
const toActivityFile = (file) => {
|
|
254
|
+
const verdict = classified ? classify(file.path) : 'excluded';
|
|
255
|
+
return { ...file, indexed: verdict === 'indexed', verdict };
|
|
256
|
+
};
|
|
257
|
+
const byChanges = [...files.values()]
|
|
258
|
+
.sort((a, b) => b.changes - a.changes || (a.path < b.path ? -1 : 1))
|
|
259
|
+
.slice(0, maxFiles)
|
|
260
|
+
.map(toActivityFile);
|
|
261
|
+
const byRecency = [...files.values()]
|
|
262
|
+
.sort((a, b) => (a.lastChange < b.lastChange ? 1 : a.lastChange > b.lastChange ? -1 : 0))
|
|
263
|
+
.slice(0, maxFiles)
|
|
264
|
+
.map(toActivityFile);
|
|
265
|
+
authors.push(...[...authorMap.values()]
|
|
266
|
+
.sort((a, b) => b.commits - a.commits)
|
|
267
|
+
.slice(0, maxAuthors)
|
|
268
|
+
.map(({ fileSet, ...rest }) => ({
|
|
269
|
+
...rest,
|
|
270
|
+
files: fileSet.size,
|
|
271
|
+
bot: looksLikeBot(rest.name, rest.email),
|
|
272
|
+
})));
|
|
273
|
+
const branchRaw = await git(repoPath, [
|
|
274
|
+
'for-each-ref',
|
|
275
|
+
`--count=${maxBranches}`,
|
|
276
|
+
'--sort=-committerdate',
|
|
277
|
+
`--format=%(refname:short)${US}%(committerdate:iso-strict)${US}%(authorname)${US}%(HEAD)`,
|
|
278
|
+
'refs/heads',
|
|
279
|
+
]);
|
|
280
|
+
const branches = [];
|
|
281
|
+
if (branchRaw !== null) {
|
|
282
|
+
for (const line of branchRaw.split('\n')) {
|
|
283
|
+
if (line.trim() === '')
|
|
284
|
+
continue;
|
|
285
|
+
const [name, date, author, head] = line.split(US);
|
|
286
|
+
if (name === undefined)
|
|
287
|
+
continue;
|
|
288
|
+
branches.push({
|
|
289
|
+
name,
|
|
290
|
+
lastCommitDate: date ?? '',
|
|
291
|
+
lastAuthor: author ?? '',
|
|
292
|
+
current: head === '*',
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const verdictCounts = { indexed: 0, excluded: 0, missing: 0 };
|
|
297
|
+
for (const path of files.keys()) {
|
|
298
|
+
verdictCounts[classified ? classify(path) : 'excluded'] += 1;
|
|
299
|
+
}
|
|
300
|
+
// Kept out of the `byChanges`/`byRecency` display caps on purpose. Those caps
|
|
301
|
+
// exist to bound a list a reader browses; "missing from the index" is the
|
|
302
|
+
// actionable subset, and a first revision lost it there — the summary reported
|
|
303
|
+
// "1 MISSING" and then listed nothing, because the file was not among the 200
|
|
304
|
+
// most-changed. A count with no rows is worse than no count.
|
|
305
|
+
const missingFiles = [...files.values()]
|
|
306
|
+
.filter((file) => classified && classify(file.path) === 'missing')
|
|
307
|
+
.sort((a, b) => b.changes - a.changes || (a.path < b.path ? -1 : 1))
|
|
308
|
+
.slice(0, 50)
|
|
309
|
+
.map(toActivityFile);
|
|
310
|
+
return {
|
|
311
|
+
window: {
|
|
312
|
+
sinceDays,
|
|
313
|
+
since,
|
|
314
|
+
maxCommits,
|
|
315
|
+
maxFiles,
|
|
316
|
+
// A capped log that does not say so reads as "this is all the activity there
|
|
317
|
+
// was" — the one conclusion the caller must not draw.
|
|
318
|
+
truncated: commitCount >= maxCommits,
|
|
319
|
+
oldestCommit: oldestCommit === '' ? null : oldestCommit,
|
|
320
|
+
newestCommit: newestCommit === '' ? null : newestCommit,
|
|
321
|
+
},
|
|
322
|
+
totals: {
|
|
323
|
+
commits: commitCount,
|
|
324
|
+
authors: authorMap.size,
|
|
325
|
+
bots: authors.filter((a) => a.bot).length,
|
|
326
|
+
files: files.size,
|
|
327
|
+
insertions: totalInsertions,
|
|
328
|
+
deletions: totalDeletions,
|
|
329
|
+
verdicts: verdictCounts,
|
|
330
|
+
indexKnown: classified,
|
|
331
|
+
// Kept under their original names so a caller written against the first
|
|
332
|
+
// revision keeps working; `verdicts` is the richer view.
|
|
333
|
+
filesTouchedThatAreIndexed: verdictCounts.indexed,
|
|
334
|
+
filesTouchedThatAreNotIndexed: verdictCounts.excluded + verdictCounts.missing,
|
|
335
|
+
},
|
|
336
|
+
authors,
|
|
337
|
+
byChanges,
|
|
338
|
+
byRecency,
|
|
339
|
+
missingFiles,
|
|
340
|
+
branches,
|
|
341
|
+
histogram: buildHistogram(stamps, sinceDays),
|
|
342
|
+
currentBranch: branches.find((b) => b.current)?.name ?? null,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=git-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-activity.js","sourceRoot":"","sources":["../../src/gitnexus/git-activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two git facts the panel needs, read without a shell.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not `exec`: a repository path is caller-supplied, and the only way
|
|
5
|
+
* to keep it from becoming an argument is to pass it as `cwd` and never as part of
|
|
6
|
+
* a command string. No shell is spawned, so nothing in the path can be
|
|
7
|
+
* interpreted.
|
|
8
|
+
*
|
|
9
|
+
* Both calls are bounded and both fail soft. A repository with no commits, a
|
|
10
|
+
* detached worktree, or no git at all is a normal state for an index that still
|
|
11
|
+
* has a valid manifest — the panel should say "unknown", not refuse to render.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* How far each side has moved from the other, as `rev-list --left-right --count`
|
|
15
|
+
* reports it: commits the indexed commit has that HEAD does not, and the reverse.
|
|
16
|
+
*/
|
|
17
|
+
export interface AheadBehind {
|
|
18
|
+
/** commits the index side has that HEAD does not */
|
|
19
|
+
behind: number;
|
|
20
|
+
/** commits HEAD has that the index side does not */
|
|
21
|
+
ahead: number;
|
|
22
|
+
}
|
|
23
|
+
export interface GitState {
|
|
24
|
+
/** git answered at all */
|
|
25
|
+
available: boolean;
|
|
26
|
+
/** full sha of HEAD */
|
|
27
|
+
headCommit: string | null;
|
|
28
|
+
/** current branch, or null when detached */
|
|
29
|
+
branch: string | null;
|
|
30
|
+
/** tracked modifications plus untracked files */
|
|
31
|
+
dirtyCount: number | null;
|
|
32
|
+
/** commits ahead/behind the indexed commit */
|
|
33
|
+
aheadBehind: AheadBehind | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read HEAD, branch and working-tree dirtiness.
|
|
37
|
+
*
|
|
38
|
+
* @returns the state, with every unanswerable field left `null`
|
|
39
|
+
*/
|
|
40
|
+
export declare function readGitState(repoPath: string, indexedCommit?: string | null): Promise<GitState>;
|
|
41
|
+
/** The states {@link classifyStaleness} can report. */
|
|
42
|
+
export type StalenessState = 'current' | 'behind' | 'diverged' | 'unknown';
|
|
43
|
+
/** What {@link classifyStaleness} judges: the indexed commit and the live git state. */
|
|
44
|
+
export interface StalenessInput {
|
|
45
|
+
lastCommit: string | null;
|
|
46
|
+
git: GitState;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Classify staleness the way `gitnexus status` does, so the panel and the CLI
|
|
50
|
+
* cannot disagree.
|
|
51
|
+
*
|
|
52
|
+
* READING `rev-list --left-right --count <indexed>...<HEAD>`
|
|
53
|
+
* ---------------------------------------------------------
|
|
54
|
+
* The output is `<left> <right>`: commits reachable from the first ref but not the
|
|
55
|
+
* second, then the reverse. With the indexed commit first:
|
|
56
|
+
*
|
|
57
|
+
* left = commits the INDEX has that HEAD does not → histories have split
|
|
58
|
+
* right = commits HEAD has that the index does not → how far the index lags
|
|
59
|
+
*
|
|
60
|
+
* So a linear fast-forward is `left = 0, right = N`: the index is simply N commits
|
|
61
|
+
* behind, and calling that "diverged" is wrong. Divergence requires `left > 0` —
|
|
62
|
+
* the indexed commit carries work that HEAD no longer reaches, which is when the
|
|
63
|
+
* distance between them stops measuring how stale the index is.
|
|
64
|
+
*
|
|
65
|
+
* A first revision had this exactly backwards, and the panel told an operator their
|
|
66
|
+
* index had diverged when it was one ordinary commit behind. The condition is
|
|
67
|
+
* asserted in both directions by `test/run.mjs` for that reason.
|
|
68
|
+
*/
|
|
69
|
+
export declare function classifyStaleness({ lastCommit, git }: StalenessInput): StalenessState;
|
|
70
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/gitnexus/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA6CH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,uBAAuB;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iDAAiD;IACjD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8CAA8C;IAC9C,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,MAAM,GAAG,IAAW,GAClC,OAAO,CAAC,QAAQ,CAAC,CA0CnB;AAED,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3E,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,cAAc,GAAG,cAAc,CAOrF"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two git facts the panel needs, read without a shell.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not `exec`: a repository path is caller-supplied, and the only way
|
|
5
|
+
* to keep it from becoming an argument is to pass it as `cwd` and never as part of
|
|
6
|
+
* a command string. No shell is spawned, so nothing in the path can be
|
|
7
|
+
* interpreted.
|
|
8
|
+
*
|
|
9
|
+
* Both calls are bounded and both fail soft. A repository with no commits, a
|
|
10
|
+
* detached worktree, or no git at all is a normal state for an index that still
|
|
11
|
+
* has a valid manifest — the panel should say "unknown", not refuse to render.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile } from 'node:child_process';
|
|
14
|
+
import { promisify } from 'node:util';
|
|
15
|
+
/**
|
|
16
|
+
* `execFile`, promisified.
|
|
17
|
+
*
|
|
18
|
+
* The assertion picks out the text-encoding result: no `encoding` is passed, so
|
|
19
|
+
* Node decodes stdout as UTF-8, and the `Buffer` arm the generic overload admits
|
|
20
|
+
* is one this call never takes.
|
|
21
|
+
*/
|
|
22
|
+
const run = promisify(execFile);
|
|
23
|
+
/** Short cap: `rev-parse` and `status` on a large repo are fast, and a hang here
|
|
24
|
+
* would block the whole panel for a value that is merely informative. */
|
|
25
|
+
const TIMEOUT_MS = 8000;
|
|
26
|
+
/**
|
|
27
|
+
* @returns trimmed stdout, or null on any failure
|
|
28
|
+
*/
|
|
29
|
+
async function git(repoPath, args) {
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await run('git', args, {
|
|
32
|
+
cwd: repoPath,
|
|
33
|
+
timeout: TIMEOUT_MS,
|
|
34
|
+
windowsHide: true,
|
|
35
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
36
|
+
});
|
|
37
|
+
return stdout;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read HEAD, branch and working-tree dirtiness.
|
|
45
|
+
*
|
|
46
|
+
* @returns the state, with every unanswerable field left `null`
|
|
47
|
+
*/
|
|
48
|
+
export async function readGitState(repoPath, indexedCommit = null) {
|
|
49
|
+
const head = await git(repoPath, ['rev-parse', 'HEAD']);
|
|
50
|
+
if (head === null) {
|
|
51
|
+
return {
|
|
52
|
+
available: false,
|
|
53
|
+
headCommit: null,
|
|
54
|
+
branch: null,
|
|
55
|
+
dirtyCount: null,
|
|
56
|
+
aheadBehind: null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const [branchRaw, statusRaw] = await Promise.all([
|
|
60
|
+
git(repoPath, ['rev-parse', '--abbrev-ref', 'HEAD']),
|
|
61
|
+
git(repoPath, ['status', '--porcelain']),
|
|
62
|
+
]);
|
|
63
|
+
let dirtyCount = null;
|
|
64
|
+
if (statusRaw !== null) {
|
|
65
|
+
dirtyCount = statusRaw.split('\n').filter((line) => line.trim() !== '').length;
|
|
66
|
+
}
|
|
67
|
+
// How far HEAD has moved from the indexed commit, when both are known. Counted
|
|
68
|
+
// in one direction only: what matters for staleness is whether the index is
|
|
69
|
+
// behind, and a diverged history is reported as such rather than counted.
|
|
70
|
+
let aheadBehind = null;
|
|
71
|
+
const indexed = typeof indexedCommit === 'string' && indexedCommit !== '' ? indexedCommit : null;
|
|
72
|
+
if (indexed !== null) {
|
|
73
|
+
const counts = await git(repoPath, ['rev-list', '--left-right', '--count', `${indexed}...HEAD`]);
|
|
74
|
+
if (counts !== null) {
|
|
75
|
+
const [behind, ahead] = counts.trim().split(/\s+/).map((n) => Number.parseInt(n, 10));
|
|
76
|
+
if (Number.isFinite(behind) && Number.isFinite(ahead))
|
|
77
|
+
aheadBehind = { behind, ahead };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
available: true,
|
|
82
|
+
headCommit: head.trim(),
|
|
83
|
+
branch: branchRaw === null ? null : branchRaw.trim() === 'HEAD' ? null : branchRaw.trim(),
|
|
84
|
+
dirtyCount,
|
|
85
|
+
aheadBehind,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Classify staleness the way `gitnexus status` does, so the panel and the CLI
|
|
90
|
+
* cannot disagree.
|
|
91
|
+
*
|
|
92
|
+
* READING `rev-list --left-right --count <indexed>...<HEAD>`
|
|
93
|
+
* ---------------------------------------------------------
|
|
94
|
+
* The output is `<left> <right>`: commits reachable from the first ref but not the
|
|
95
|
+
* second, then the reverse. With the indexed commit first:
|
|
96
|
+
*
|
|
97
|
+
* left = commits the INDEX has that HEAD does not → histories have split
|
|
98
|
+
* right = commits HEAD has that the index does not → how far the index lags
|
|
99
|
+
*
|
|
100
|
+
* So a linear fast-forward is `left = 0, right = N`: the index is simply N commits
|
|
101
|
+
* behind, and calling that "diverged" is wrong. Divergence requires `left > 0` —
|
|
102
|
+
* the indexed commit carries work that HEAD no longer reaches, which is when the
|
|
103
|
+
* distance between them stops measuring how stale the index is.
|
|
104
|
+
*
|
|
105
|
+
* A first revision had this exactly backwards, and the panel told an operator their
|
|
106
|
+
* index had diverged when it was one ordinary commit behind. The condition is
|
|
107
|
+
* asserted in both directions by `test/run.mjs` for that reason.
|
|
108
|
+
*/
|
|
109
|
+
export function classifyStaleness({ lastCommit, git }) {
|
|
110
|
+
if (!git.available || git.headCommit === null)
|
|
111
|
+
return 'unknown';
|
|
112
|
+
if (typeof lastCommit !== 'string' || lastCommit === '')
|
|
113
|
+
return 'unknown';
|
|
114
|
+
if (git.headCommit === lastCommit)
|
|
115
|
+
return 'current';
|
|
116
|
+
if (git.aheadBehind === null)
|
|
117
|
+
return 'unknown';
|
|
118
|
+
// `left > 0` means the indexed commit holds commits HEAD does not reach.
|
|
119
|
+
return git.aheadBehind.behind > 0 ? 'diverged' : 'behind';
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/gitnexus/git.ts"],"names":[],"mappings":"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"}
|