@ok-ggwp/git-gloss 0.5.0 → 0.7.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/README.md +35 -9
- package/dist/ary/desk.js +112 -0
- package/dist/ary/jobs.js +132 -0
- package/dist/ary/page-job.js +23 -0
- package/dist/ary/picker.js +8 -0
- package/dist/ary/server.js +41 -107
- package/dist/batch.js +184 -0
- package/dist/cli.js +2 -1
- package/dist/commands/add.js +28 -2
- package/dist/commands/ary.js +3 -0
- package/dist/commands/carry.js +3 -1
- package/dist/commands/lint.js +99 -0
- package/dist/commands/review.js +10 -5
- package/dist/commands/reviews.js +64 -16
- package/dist/commands/show.js +11 -5
- package/dist/commands/sync.js +35 -12
- package/dist/commands/versions.js +52 -8
- package/dist/commands/why.js +8 -3
- package/dist/config.js +22 -11
- package/dist/findings.js +349 -0
- package/dist/floor.js +37 -10
- package/dist/git.js +151 -27
- package/dist/html/client.js +82 -11
- package/dist/html/data.js +38 -11
- package/dist/html/style.css +16 -0
- package/dist/loader.js +22 -3
- package/dist/plain.js +2 -1
- package/dist/reviews.js +35 -13
- package/dist/schema.js +2 -0
- package/dist/validate.js +4 -2
- package/package.json +2 -2
- package/skills/gloss/SKILL.md +15 -6
package/dist/commands/sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
|
-
import { git, gitTry, GitError, NOTES_REF } from '../git.js';
|
|
3
|
+
import { git, gitTry, GitError, gitCleanup, NETWORK_TIMEOUT, NOTES_REF, rethrowTimeout } from '../git.js';
|
|
4
4
|
import { carryNew } from '../origins.js';
|
|
5
5
|
import { REVIEWS_REF } from '../reviews.js';
|
|
6
6
|
import { carry } from './carry.js';
|
|
@@ -22,7 +22,8 @@ function createdAt(blob) {
|
|
|
22
22
|
const value = JSON.parse(git(['cat-file', 'blob', blob]))?.created_at;
|
|
23
23
|
return typeof value === 'string' ? value : null;
|
|
24
24
|
}
|
|
25
|
-
catch {
|
|
25
|
+
catch (err) {
|
|
26
|
+
rethrowTimeout(err);
|
|
26
27
|
return null;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -50,12 +51,34 @@ function shareable(remote, pushing) {
|
|
|
50
51
|
// code (or an update-ref onto our own notes ref) touches it.
|
|
51
52
|
function checkTracking(remote, ref, tracking) {
|
|
52
53
|
if (gitTry(['rev-parse', '-q', '--verify', `${tracking}^{commit}`]) === null) {
|
|
53
|
-
|
|
54
|
+
gitCleanup(['update-ref', '-d', tracking]);
|
|
54
55
|
throw new GitError(`${remote}'s ${ref} is not a notes commit; ignoring it (nothing was changed here)`);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
// `init`'s refspec is a glob (refs/notes/gloss*), so a remote that once published
|
|
59
|
+
// refs/notes/gloss/x leaves refs/notes/remotes/<remote>/gloss/x behind, and git
|
|
60
|
+
// then cannot create refs/notes/remotes/<remote>/gloss once the remote has that
|
|
61
|
+
// ref (a ref cannot be both a file and a directory): every fetch fails until
|
|
62
|
+
// `git remote prune`. We are about to fetch the ref into `tracking`, so
|
|
63
|
+
// whatever sits below it is stale and cannot be valid together with it. Only
|
|
64
|
+
// that namespace is touched, in one batched update-ref.
|
|
65
|
+
function clearBlockingTracking(tracking) {
|
|
66
|
+
const stale = (gitTry(['for-each-ref', '--format=%(refname)', `${tracking}/`]) ?? '').split('\n').filter((r) => r.startsWith(`${tracking}/`));
|
|
67
|
+
if (stale.length)
|
|
68
|
+
git(['update-ref', '--stdin'], stale.map((r) => `delete ${r}\n`).join(''));
|
|
69
|
+
}
|
|
57
70
|
function fetchTracking(remote, ref, tracking) {
|
|
58
|
-
|
|
71
|
+
try {
|
|
72
|
+
clearBlockingTracking(tracking);
|
|
73
|
+
git(['fetch', '-q', remote, `+${ref}:${tracking}`], undefined, {}, NETWORK_TIMEOUT);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
rethrowTimeout(err);
|
|
77
|
+
if (err instanceof GitError && /cannot lock ref|Unable to create .*\.lock|some local refs could not be updated/.test(err.message)) {
|
|
78
|
+
throw new GitError(`${err.message}\n a stale remote-tracking ref may be in the way; try: git remote prune ${remote}`);
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
59
82
|
checkTracking(remote, ref, tracking);
|
|
60
83
|
}
|
|
61
84
|
const PUSH_REF = 'refs/notes/gloss-sync-tmp';
|
|
@@ -63,7 +86,7 @@ const PUSH_REF = 'refs/notes/gloss-sync-tmp';
|
|
|
63
86
|
// someone else's push and the round should be retried.
|
|
64
87
|
function round(remote, pushing, carried) {
|
|
65
88
|
const tracking = `refs/notes/remotes/${remote}/gloss`;
|
|
66
|
-
const remoteHas = gitTry(['ls-remote', '--exit-code', remote, NOTES_REF]) !== null;
|
|
89
|
+
const remoteHas = gitTry(['ls-remote', '--exit-code', remote, NOTES_REF], undefined, NETWORK_TIMEOUT) !== null;
|
|
67
90
|
if (remoteHas)
|
|
68
91
|
fetchTracking(remote, NOTES_REF, tracking);
|
|
69
92
|
const local = noteList(NOTES_REF);
|
|
@@ -142,13 +165,13 @@ function round(remote, pushing, carried) {
|
|
|
142
165
|
if (remoteHas)
|
|
143
166
|
git(['update-ref', PUSH_REF, tracking]);
|
|
144
167
|
else
|
|
145
|
-
|
|
168
|
+
gitCleanup(['update-ref', '-d', PUSH_REF]);
|
|
146
169
|
for (const [object, blob] of toPush)
|
|
147
170
|
git(['notes', '--ref', PUSH_REF, 'add', '-f', '-C', blob, object]);
|
|
148
171
|
try {
|
|
149
172
|
// --no-verify: don't run the pre-push hook for the notes push itself,
|
|
150
173
|
// which would call sync again.
|
|
151
|
-
git(['push', '-q', '--no-verify', remote, `${PUSH_REF}:${NOTES_REF}`]);
|
|
174
|
+
git(['push', '-q', '--no-verify', remote, `${PUSH_REF}:${NOTES_REF}`], undefined, {}, NETWORK_TIMEOUT);
|
|
152
175
|
}
|
|
153
176
|
catch (err) {
|
|
154
177
|
if (/\(fetch first\)|non-fast-forward/.test(err.message))
|
|
@@ -159,7 +182,7 @@ function round(remote, pushing, carried) {
|
|
|
159
182
|
git(['notes', '--ref', NOTES_REF, 'merge', '-q', '-s', 'ours', PUSH_REF]);
|
|
160
183
|
}
|
|
161
184
|
finally {
|
|
162
|
-
|
|
185
|
+
gitCleanup(['update-ref', '-d', PUSH_REF]);
|
|
163
186
|
}
|
|
164
187
|
return { fetched, pushed: toPush.length, withheld, conflicts };
|
|
165
188
|
}
|
|
@@ -169,7 +192,7 @@ const REVIEWS_PUSH_REF = 'refs/notes/gloss-reviews-sync-tmp';
|
|
|
169
192
|
// commits the remote may see are sent. Returns null if the push lost a race.
|
|
170
193
|
function syncReviews(remote, pushing) {
|
|
171
194
|
const tracking = `refs/notes/remotes/${remote}/gloss-reviews`;
|
|
172
|
-
const remoteHas = gitTry(['ls-remote', '--exit-code', remote, REVIEWS_REF]) !== null;
|
|
195
|
+
const remoteHas = gitTry(['ls-remote', '--exit-code', remote, REVIEWS_REF], undefined, NETWORK_TIMEOUT) !== null;
|
|
173
196
|
if (remoteHas)
|
|
174
197
|
fetchTracking(remote, REVIEWS_REF, tracking);
|
|
175
198
|
const before = noteList(REVIEWS_REF);
|
|
@@ -190,11 +213,11 @@ function syncReviews(remote, pushing) {
|
|
|
190
213
|
if (remoteHas)
|
|
191
214
|
git(['update-ref', REVIEWS_PUSH_REF, tracking]);
|
|
192
215
|
else
|
|
193
|
-
|
|
216
|
+
gitCleanup(['update-ref', '-d', REVIEWS_PUSH_REF]);
|
|
194
217
|
for (const [object, blob] of toPush)
|
|
195
218
|
git(['notes', '--ref', REVIEWS_PUSH_REF, 'add', '-f', '-C', blob, object]);
|
|
196
219
|
try {
|
|
197
|
-
git(['push', '-q', '--no-verify', remote, `${REVIEWS_PUSH_REF}:${REVIEWS_REF}`]);
|
|
220
|
+
git(['push', '-q', '--no-verify', remote, `${REVIEWS_PUSH_REF}:${REVIEWS_REF}`], undefined, {}, NETWORK_TIMEOUT);
|
|
198
221
|
}
|
|
199
222
|
catch (err) {
|
|
200
223
|
if (/\(fetch first\)|non-fast-forward/.test(err.message))
|
|
@@ -204,7 +227,7 @@ function syncReviews(remote, pushing) {
|
|
|
204
227
|
git(['notes', '--ref', REVIEWS_REF, 'merge', '-q', '-s', 'cat_sort_uniq', REVIEWS_PUSH_REF]);
|
|
205
228
|
}
|
|
206
229
|
finally {
|
|
207
|
-
|
|
230
|
+
gitCleanup(['update-ref', '-d', REVIEWS_PUSH_REF]);
|
|
208
231
|
}
|
|
209
232
|
return { fetched, pushed: toPush.length };
|
|
210
233
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
-
import { commitDiff, git, gitTry, GlossError, NOTES_REF, resolveCommit, WIP_NOTES_REF, WIP_REF, writeNote } from '../git.js';
|
|
2
|
+
import { commitDiff, git, GitError, gitTry, GlossError, noteSizeProblem, NOTES_REF, parseNote, resolveCommit, WIP_NOTES_REF, WIP_REF, rethrowTimeout, writeNote } from '../git.js';
|
|
3
|
+
import { plain } from '../plain.js';
|
|
3
4
|
import { location, parseDiff } from '../diff.js';
|
|
5
|
+
import { loadGlossOf } from '../loader.js';
|
|
6
|
+
import { carriedErrors } from '../validate.js';
|
|
4
7
|
import { mergeGlosses } from '../merge.js';
|
|
5
8
|
import { noteFormat } from '../schema.js';
|
|
6
9
|
import { refLabel } from '../selection.js';
|
|
@@ -51,18 +54,29 @@ function history(ref, only) {
|
|
|
51
54
|
flush();
|
|
52
55
|
return out;
|
|
53
56
|
}
|
|
57
|
+
// How many group ids the listing names for one version.
|
|
58
|
+
const MAX_LISTED = 20;
|
|
59
|
+
// Everything a version of the history prints comes from a note that may have been
|
|
60
|
+
// pushed by anyone: it is size-checked before it is read, cleaned, and bounded.
|
|
54
61
|
function summary(blob) {
|
|
62
|
+
const size = Number(gitTry(['cat-file', '-s', blob]));
|
|
63
|
+
const big = noteSizeProblem(size);
|
|
64
|
+
if (big)
|
|
65
|
+
return `(not read: ${big})`;
|
|
55
66
|
try {
|
|
56
67
|
const note = JSON.parse(git(['cat-file', 'blob', blob]));
|
|
57
68
|
const a = note.annotation;
|
|
58
|
-
const from = note.sources ? `carried from ${note.sources.map((s) => s.slice(0, 7)).join(', ')}; ` : '';
|
|
59
|
-
|
|
69
|
+
const from = Array.isArray(note.sources) ? `carried from ${note.sources.slice(0, 5).map((s) => plain(String(s)).slice(0, 7)).join(', ')}; ` : '';
|
|
70
|
+
const ids = a.groups.slice(0, MAX_LISTED).map((g) => plain(String(g.id)).slice(0, 40));
|
|
71
|
+
const n = a.groups.length;
|
|
72
|
+
return `${from}${n} group${n === 1 ? '' : 's'}: ${ids.join(', ')}${n > MAX_LISTED ? `, and ${n - MAX_LISTED} more` : ''}\n task: ${JSON.stringify(plain(String(a.task)).slice(0, 200))}`;
|
|
60
73
|
}
|
|
61
|
-
catch {
|
|
74
|
+
catch (err) {
|
|
75
|
+
rethrowTimeout(err);
|
|
62
76
|
return '(not a readable gloss: joined notes from a squash, or not JSON)';
|
|
63
77
|
}
|
|
64
78
|
}
|
|
65
|
-
const when = (iso) => iso.slice(0, 16).replace('T', ' ');
|
|
79
|
+
const when = (iso) => plain(iso).slice(0, 16).replace('T', ' ');
|
|
66
80
|
export function run(argv) {
|
|
67
81
|
const { values, positionals } = parseArgs({ args: argv, allowPositionals: true, options: { show: { type: 'string' }, restore: { type: 'string' } } });
|
|
68
82
|
const wip = positionals[0] === 'wip';
|
|
@@ -84,12 +98,37 @@ export function run(argv) {
|
|
|
84
98
|
return { n, v: v };
|
|
85
99
|
};
|
|
86
100
|
if (values.show !== undefined) {
|
|
87
|
-
|
|
101
|
+
// Cleaned like everything else printed (control characters, escape sequences, bidi and
|
|
102
|
+
// zero-width characters become spaces), so not byte-identical to the stored blob.
|
|
103
|
+
process.stdout.write(plain(git(['cat-file', 'blob', pick(values.show, '--show').v.blob]), true));
|
|
88
104
|
return 0;
|
|
89
105
|
}
|
|
90
106
|
if (values.restore !== undefined) {
|
|
91
107
|
const { n, v } = pick(values.restore, '--restore');
|
|
108
|
+
// Only a gloss that readers accept is brought back (a restore must not plant what they refuse).
|
|
109
|
+
const text = git(['cat-file', 'blob', v.blob]);
|
|
110
|
+
let note;
|
|
111
|
+
try {
|
|
112
|
+
note = parseNote(text, `version ${n}`);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (!(err instanceof GlossError) || err instanceof GitError)
|
|
116
|
+
throw err;
|
|
117
|
+
throw new GlossError(`${err.message.replace(/; (replace it|re-gloss it|upgrade).*$/, '')}, so it is not restored (read it with --show ${n})`);
|
|
118
|
+
}
|
|
119
|
+
// What readers refuse for the commit it would go on (too many claimed lines).
|
|
120
|
+
const checkOn = (target) => {
|
|
121
|
+
try {
|
|
122
|
+
loadGlossOf(target, note);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (!(err instanceof GlossError) || err instanceof GitError)
|
|
126
|
+
throw err;
|
|
127
|
+
throw new GlossError(`version ${n} would be refused on ${target.slice(0, 7)}: ${err.message.replace(/^the gloss on \S+ is refused: /, '')} (not restored; read it with --show ${n})`);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
92
130
|
if (!wip) {
|
|
131
|
+
checkOn(sha);
|
|
93
132
|
git(['notes', '--ref', ref, 'add', '-f', '-C', v.blob, sha]);
|
|
94
133
|
console.log(`restored version ${n} as the gloss of ${label}`);
|
|
95
134
|
return 0;
|
|
@@ -100,8 +139,8 @@ export function run(argv) {
|
|
|
100
139
|
const onto = gitTry(['rev-parse', '-q', '--verify', WIP_REF])?.trim();
|
|
101
140
|
if (!onto)
|
|
102
141
|
throw new GlossError('there is no snapshot of uncommitted work to restore onto; run git gloss hunks wip first');
|
|
103
|
-
const note = JSON.parse(git(['cat-file', 'blob', v.blob]));
|
|
104
142
|
if (v.object === onto || gitTry(['cat-file', '-e', `${v.object}^{commit}`]) === null) {
|
|
143
|
+
checkOn(onto);
|
|
105
144
|
git(['notes', '--ref', ref, 'add', '-f', '-C', v.blob, onto]);
|
|
106
145
|
console.log(`restored version ${n} onto the wip snapshot ${onto.slice(0, 7)}` +
|
|
107
146
|
(v.object === onto ? '' : `; its snapshot ${v.object.slice(0, 7)} is gone, so hunk references may not match: check with git gloss show wip`));
|
|
@@ -109,6 +148,11 @@ export function run(argv) {
|
|
|
109
148
|
}
|
|
110
149
|
const current = parseDiff(commitDiff(onto));
|
|
111
150
|
const merged = mergeGlosses([{ sha: v.object, note, hunks: parseDiff(commitDiff(v.object)) }], current);
|
|
151
|
+
if (merged.annotation) {
|
|
152
|
+
const errors = carriedErrors(merged.annotation, current);
|
|
153
|
+
if (errors.length)
|
|
154
|
+
throw new GlossError(`version ${n} doesn't fit the uncommitted work: ${errors.join('; ')} (not restored)`);
|
|
155
|
+
}
|
|
112
156
|
if (!merged.annotation)
|
|
113
157
|
throw new GlossError(`none of version ${n}'s glossed changes are in the uncommitted work any more; read it with --show ${n}`);
|
|
114
158
|
writeNote(onto, {
|
|
@@ -122,7 +166,7 @@ export function run(argv) {
|
|
|
122
166
|
}
|
|
123
167
|
console.log(`glosses of ${label}, newest first:`);
|
|
124
168
|
versions.forEach((v, i) => {
|
|
125
|
-
const head = `${String(i + 1).padStart(3)} ${when(v.at)} ${v.how}${wip ? ` (snapshot ${v.object.slice(0, 7)})` : ''}`;
|
|
169
|
+
const head = `${String(i + 1).padStart(3)} ${when(v.at)} ${plain(v.how)}${wip ? ` (snapshot ${v.object.slice(0, 7)})` : ''}`;
|
|
126
170
|
console.log(`${head}${i === 0 && v.blob && !wip ? ' [current]' : ''}`);
|
|
127
171
|
console.log(` ${v.blob ? summary(v.blob) : 'removed'}`);
|
|
128
172
|
});
|
package/dist/commands/why.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { plain } from '../plain.js';
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
3
|
import { blameRange, commitSubject, lineHistory } from '../git.js';
|
|
4
|
-
import {
|
|
4
|
+
import { loadGlossInRange, refusedWarning } from '../loader.js';
|
|
5
5
|
import { servesOf } from '../schema.js';
|
|
6
6
|
import { field, groupHeader, style } from '../render.js';
|
|
7
7
|
import { bodyLines, formatRanges } from '../selection.js';
|
|
@@ -10,8 +10,13 @@ export const usage = 'why <file>:<line>[-<end>] [--history] [--rev <commit>] [--
|
|
|
10
10
|
// refer to it; each load diffs the commit.
|
|
11
11
|
const glossCache = new Map();
|
|
12
12
|
function glossFor(commit) {
|
|
13
|
-
if (!glossCache.has(commit))
|
|
14
|
-
|
|
13
|
+
if (!glossCache.has(commit)) {
|
|
14
|
+
// A refused gloss (hostile or broken) is skipped with a warning, once, not a failed command.
|
|
15
|
+
const { gloss, refused } = loadGlossInRange(commit);
|
|
16
|
+
if (refused)
|
|
17
|
+
console.error(refusedWarning(commit, refused));
|
|
18
|
+
glossCache.set(commit, gloss);
|
|
19
|
+
}
|
|
15
20
|
return glossCache.get(commit);
|
|
16
21
|
}
|
|
17
22
|
// Groups in a gloss that own `line` of `file` on the new side of the commit's
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { git, GitError, GlossError, gitTry } from './git.js';
|
|
2
|
+
import { git, GitError, GlossError, gitTry, rethrowTimeout } from './git.js';
|
|
3
3
|
import { globToRegExp } from './glob.js';
|
|
4
4
|
// Settings for the computed signals, in three layers (later wins for scalars,
|
|
5
5
|
// repo config extends the lists of the one before):
|
|
@@ -137,10 +137,12 @@ function gitInt(key) {
|
|
|
137
137
|
}
|
|
138
138
|
// Git failing (a broken repo, an unreadable object, no permission) is not the
|
|
139
139
|
// same as "no base" or "no file": the repo's own, perhaps stricter, config
|
|
140
|
-
// would silently not apply
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// and the
|
|
140
|
+
// would silently not apply, and a floor computed from the defaults alone could
|
|
141
|
+
// be lower than the real one. So the ratings are unverifiable: loadConfig
|
|
142
|
+
// throws a ConfigError, which `add` and `signals` report as an error, and
|
|
143
|
+
// `show`, `lint`, `html` and `hunks` meet through withSignals (the signals are
|
|
144
|
+
// unavailable, `lint` says "incomplete"). A broken base candidate is only
|
|
145
|
+
// warned about (once per message) while a later one still works.
|
|
144
146
|
const warned = new Set();
|
|
145
147
|
function warnOnce(msg) {
|
|
146
148
|
if (warned.has(msg))
|
|
@@ -171,8 +173,14 @@ function gitAnswer(args) {
|
|
|
171
173
|
// is always a `sensitive` path.
|
|
172
174
|
function trustedRef() {
|
|
173
175
|
// No commits yet: no base, and nothing failed.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
try {
|
|
177
|
+
if (gitAnswer(['rev-parse', '--verify', '-q', 'HEAD']) === null)
|
|
178
|
+
return noBase();
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
rethrowTimeout(err);
|
|
182
|
+
throw new ConfigError(`git failed finding HEAD (${err.message}), so ${CONFIG_FILE} cannot be read and the ratings cannot be verified`);
|
|
183
|
+
}
|
|
176
184
|
let failed = false;
|
|
177
185
|
// Full ref names, so a tag called "main" cannot stand in for the branch.
|
|
178
186
|
for (const b of ['refs/remotes/origin/main', 'refs/heads/main', 'refs/remotes/origin/master', 'refs/heads/master']) {
|
|
@@ -184,20 +192,23 @@ function trustedRef() {
|
|
|
184
192
|
return mb;
|
|
185
193
|
}
|
|
186
194
|
catch (err) {
|
|
195
|
+
rethrowTimeout(err);
|
|
187
196
|
// A broken candidate does not hide the ones after it.
|
|
188
197
|
failed = true;
|
|
189
198
|
warnOnce(`git failed finding the base branch with ${b} (${err.message}); trying the next candidate`);
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
// Some candidate failed: git's warning above is the reason, not an absent branch.
|
|
193
|
-
|
|
202
|
+
if (failed)
|
|
203
|
+
throw new ConfigError(`git failed on every base branch candidate, so ${CONFIG_FILE} cannot be read and the ratings cannot be verified (the repo's own patterns would not apply); fix the repository, or see the warnings above`);
|
|
204
|
+
return noBase();
|
|
194
205
|
}
|
|
195
206
|
function noBase() {
|
|
196
207
|
console.error('warning: no base branch (origin/main, main, origin/master, master) found, so .gloss/config.json is not read; using the built-in defaults');
|
|
197
208
|
return null;
|
|
198
209
|
}
|
|
199
210
|
// The config file at `ref`: null when the file is not there (the usual case),
|
|
200
|
-
// a
|
|
211
|
+
// a ConfigError when git could not tell or could not read it. Presence
|
|
201
212
|
// is asked of ls-tree (empty output: absent), not read off an error message.
|
|
202
213
|
function configAt(ref) {
|
|
203
214
|
try {
|
|
@@ -206,8 +217,8 @@ function configAt(ref) {
|
|
|
206
217
|
return git(['show', `${ref}:${CONFIG_FILE}`]);
|
|
207
218
|
}
|
|
208
219
|
catch (err) {
|
|
209
|
-
|
|
210
|
-
|
|
220
|
+
rethrowTimeout(err);
|
|
221
|
+
throw new ConfigError(`${CONFIG_FILE} could not be read from the base branch (${err.message}); the repo's own patterns cannot be applied, so the ratings cannot be verified`);
|
|
211
222
|
}
|
|
212
223
|
}
|
|
213
224
|
function readRepoConfig() {
|