@takagaki/cortex-decisions-viewer 0.12.13 → 0.12.15
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/dist/build.js +7 -274
- package/dist/render.js +385 -175
- package/dist/repo.js +347 -0
- package/package.json +1 -1
package/dist/repo.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AI_HISTORY_MAX_ENTRIES = void 0;
|
|
3
4
|
exports.resolveRepo = resolveRepo;
|
|
4
5
|
exports.latestGeneratedPaths = latestGeneratedPaths;
|
|
5
6
|
exports.normalizeToHttps = normalizeToHttps;
|
|
7
|
+
exports.isAiCommit = isAiCommit;
|
|
8
|
+
exports.collectAiChangeHistory = collectAiChangeHistory;
|
|
6
9
|
const node_child_process_1 = require("node:child_process");
|
|
7
10
|
/**
|
|
8
11
|
* GitHubのowner/repoを表すベースURLを解決する。
|
|
@@ -102,3 +105,347 @@ function normalizeToHttps(url) {
|
|
|
102
105
|
}
|
|
103
106
|
return null;
|
|
104
107
|
}
|
|
108
|
+
// ---- AIによるレコードの変更履歴(git 由来)------------------------------------
|
|
109
|
+
//
|
|
110
|
+
// レコード詳細の「AIによる最近の変更」の出どころ。**以前は週次整理レポート
|
|
111
|
+
// (.cortex/weekly-curation-report.json の actions)を読んでいたが、あれは毎回全置換で、
|
|
112
|
+
// 翌週の実行でレポートが差し替わると前の履歴が画面から消えた**(オーナーが実際に踏んだ)。
|
|
113
|
+
// 「黙って書き換えられた記録を作らない」という節の目的に対して、1週間で消える履歴は
|
|
114
|
+
// 用を成さない。git のコミットは消えないので、そちらを正本にする。
|
|
115
|
+
//
|
|
116
|
+
// **git の呼び出しはレコード数に依存させない。ビルド1回につき2回だけ呼ぶ。**
|
|
117
|
+
// レコードごとに git log を回すと、案件が育つほどビルドが線形に遅くなる。
|
|
118
|
+
//
|
|
119
|
+
// パス1: --name-status … コミットの素性(誰が・いつ・何を言って)と触ったパスだけ。出力は軽い
|
|
120
|
+
// パス2: -p -U0 --diff-filter=M … **変更(M)の差分だけ**。追加(A)の差分は取らない
|
|
121
|
+
// ——追加時の「変更後」はレコード本文そのもので、
|
|
122
|
+
// 同じページに全文が既に出ている(site の JSON が二重になる)
|
|
123
|
+
//
|
|
124
|
+
// 例外は全部握って空を返す(latestGeneratedPaths と同じ規律)。浅い clone・git 無し・
|
|
125
|
+
// メッセージ規約の変更 → 「履歴の節が出ない」だけで、他の表示は壊れない。
|
|
126
|
+
/** 行頭に置いてコミットの境目を見分ける印。差分もパスも NUL では始まらない */
|
|
127
|
+
const NUL = "\u0000";
|
|
128
|
+
/** コミット1行の中の区切り(Unit Separator)。コミットメッセージに現れない制御文字を選ぶ */
|
|
129
|
+
const US = "\u001f";
|
|
130
|
+
/** 遡るコミット数の上限。深追いしてもビルドが遅くなるだけで、画面に出るのはレコードごとに数件 */
|
|
131
|
+
const AI_HISTORY_MAX_COMMITS = 400;
|
|
132
|
+
/** レコードごとに画面へ渡す件数(分類B・2026-08-29)。折りたたみの中でも1件が差分を持つので絞る */
|
|
133
|
+
exports.AI_HISTORY_MAX_ENTRIES = 5;
|
|
134
|
+
/** 変更前・変更後それぞれの文字数の上限。超えたら丸めて truncated を立てる */
|
|
135
|
+
const AI_HISTORY_MAX_DIFF_CHARS = 2000;
|
|
136
|
+
/** レコードのパスの形。README・雛形はレコードではない */
|
|
137
|
+
const RECORD_PATH = /^Cortex\/[^/]+\/records\/[^/]+\.md$/;
|
|
138
|
+
/** 探索範囲(git の pathspec)。README や Context 配下の差分は最初から出力に入れない */
|
|
139
|
+
const RECORD_PATHSPEC = "Cortex/*/records/*.md";
|
|
140
|
+
/**
|
|
141
|
+
* そのコミットは「AIがやったこと」か。
|
|
142
|
+
*
|
|
143
|
+
* 🔴 **author だけでは判定できない。** 受け口の Lambda(`infra/lambda/intake`)は
|
|
144
|
+
* **人の操作の代理でコミットする**(`promoteCommitMessage` = 「Gold確認: …」、
|
|
145
|
+
* `deleteCommitMessage` = 「レコード削除: …」。logic.mjs:407,713)。その author は
|
|
146
|
+
* 共有トークンの持ち主で全員同じになる(index.mjs のコメント)。bot 名義だけで判定すると、
|
|
147
|
+
* **人が「確認済みにする」を押した操作が「AIによる変更」として画面に出る**。
|
|
148
|
+
* レコードのファイルを書き換える op はこの2つだけなので、件名で先に落とす。
|
|
149
|
+
*
|
|
150
|
+
* 判定は上から順に:
|
|
151
|
+
* 1. 件名が**人の操作の口**の規約 → 人
|
|
152
|
+
* 2. 件名が**夜間パイプライン**の規約 → AI(author が PAT 名義でも拾える)
|
|
153
|
+
* 3. author が bot 名義 → AI
|
|
154
|
+
* 4. それ以外 → 人(GitHub や手元から人が直接直したもの)
|
|
155
|
+
*/
|
|
156
|
+
function isAiCommit(id) {
|
|
157
|
+
const subject = id.subject || "";
|
|
158
|
+
// 1. 人の操作の代理(infra/lambda/intake/logic.mjs の promoteCommitMessage / deleteCommitMessage)
|
|
159
|
+
if (/^(Gold確認|レコード削除)[::]/.test(subject))
|
|
160
|
+
return false;
|
|
161
|
+
// 2. 夜間パイプライン(cortex-engine)の規約。**コロンより前(接頭辞)だけを見る。**
|
|
162
|
+
// 素の部分一致にすると「自動生成された用語を手で直した」「週次整理の取りこぼしを補完」
|
|
163
|
+
// のような**人が書いた件名**まで AI に倒れ、顧客の画面に「AIがこの記録を書き換えました」と
|
|
164
|
+
// 出る(この関数が防ごうとしている誤帰属の、ちょうど逆向き)。
|
|
165
|
+
// 規約側は「Gold自動追記: …」「週次整理: …」の形なので、接頭辞で足りる。
|
|
166
|
+
//
|
|
167
|
+
// ⚠️ **見るのは件名(%s)だけ。** latestGeneratedPaths(このファイル上部)の
|
|
168
|
+
// `--grep` は**メッセージ本文も**対象なので、判定の範囲が揃っていない。
|
|
169
|
+
// 本文まで足すと複数行になって1行1コミットのパースが壊れるため、ここは件名に留める。
|
|
170
|
+
// 件名に規約が無く本文にだけある形で engine が回っても、**規則3(bot 名義)で拾える**
|
|
171
|
+
// ——夜間パイプラインは GitHub Actions の bot 名義でコミットするため。
|
|
172
|
+
const scope = /^([^::]{1,40})[::]/.exec(subject);
|
|
173
|
+
if (scope && /自動追記|自動生成|週次整理/.test(scope[1]))
|
|
174
|
+
return true;
|
|
175
|
+
// 3. bot 名義
|
|
176
|
+
const who = `${id.name || ""} ${id.email || ""}`;
|
|
177
|
+
return /\[bot\]|github-actions/i.test(who);
|
|
178
|
+
}
|
|
179
|
+
/** git log を1回。失敗(git 無し・リポジトリでない・出力が大きすぎる)は空文字にする */
|
|
180
|
+
function gitLog(cwd, args) {
|
|
181
|
+
try {
|
|
182
|
+
// 🔴 **出力の形は、読む人の git 設定に左右させない。** ここで組み立てているのは
|
|
183
|
+
// 機械が読む出力なので、人の ~/.gitconfig で形が変わる余地を全部潰す:
|
|
184
|
+
//
|
|
185
|
+
// core.quotepath=false … 既定の git は ASCII 以外を含むパスを
|
|
186
|
+
// `"Cortex/Glossary/records/\344\273\256…"` と引用符でくるむ。用語集・メンバーの
|
|
187
|
+
// レコードは**日本語のファイル名**なので、付け忘れると画面側の照合キー
|
|
188
|
+
// (repoPathOf の生パス)と一致せず、履歴が**丸ごと出なくなる**(実装中に踏んだ)。
|
|
189
|
+
// なお、これが消すのは**非ASCIIの引用だけ**。ファイル名に `"` `\` 制御文字が
|
|
190
|
+
// 入ると git は常に引用符でくるむ——そのレコードは RECORD_PATH に一致せず、
|
|
191
|
+
// 履歴が出ないだけになる(事故ではないが、出ない理由はこれ)
|
|
192
|
+
// diff.mnemonicPrefix / diff.noprefix … `+++ b/<path>` の接頭辞が `w/` になったり
|
|
193
|
+
// 消えたりする。パスの取り出しが狂うので、下で接頭辞を明示して固定する
|
|
194
|
+
// log.showSignature=false … 署名の検証結果が本文に混ざる
|
|
195
|
+
// color.ui=false / --no-color … 色の制御文字が混ざる
|
|
196
|
+
return (0, node_child_process_1.execFileSync)("git", [
|
|
197
|
+
"-c", "core.quotepath=false",
|
|
198
|
+
"-c", "log.showSignature=false",
|
|
199
|
+
"-c", "color.ui=false",
|
|
200
|
+
...args,
|
|
201
|
+
], {
|
|
202
|
+
cwd,
|
|
203
|
+
encoding: "utf8",
|
|
204
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
205
|
+
// 既定の 1MB では足りない。差分を丸ごと受け取るので広めに取り、
|
|
206
|
+
// 溢れたら例外 → 上位で空扱い(履歴が出ないだけでビルドは通る)
|
|
207
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
// **握り潰すが、異常なものは無言にしない。** maxBuffer 超過や git の内部エラーまで
|
|
212
|
+
// 「履歴の節が消える」に潰れると、**ある日消えても誰も気づけない**。
|
|
213
|
+
//
|
|
214
|
+
// ただし「**git が無い**(ENOENT)」「**git リポジトリでない**(git の致命エラー = 128)」は
|
|
215
|
+
// **既定の状態**——手元のビルドやテストで普通に起きるので、ここで警告するとログが
|
|
216
|
+
// 常に注意で埋まる(gold-proposals / weekly-curation の「無いことは警告しない」と同じ規律)。
|
|
217
|
+
// 戻り値の契約(空文字 = 履歴なしで正常継続)はどちらでも変えない
|
|
218
|
+
const err = e;
|
|
219
|
+
if (err.code !== "ENOENT" && err.status !== 128) {
|
|
220
|
+
console.warn(`AIの変更履歴を git から取れませんでした(履歴は出さずに続けます): ${String(e)}`);
|
|
221
|
+
}
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** パス1: コミットの素性と、そのコミットが触ったレコードのパス(新しい順) */
|
|
226
|
+
function readCommits(cwd) {
|
|
227
|
+
const out = gitLog(cwd, [
|
|
228
|
+
"log",
|
|
229
|
+
`-n${AI_HISTORY_MAX_COMMITS}`,
|
|
230
|
+
// 改名を追わない。追うと「変更(M)」の集合から外れて差分が取れなくなる。
|
|
231
|
+
// 改名は削除+追加として素直に見せる。**log.follow=true を設定している人がいる**ので
|
|
232
|
+
// --no-follow も明示する(--follow が効くと1パス目と2パス目で対象がずれる)
|
|
233
|
+
"--no-renames",
|
|
234
|
+
"--no-follow",
|
|
235
|
+
"--no-color",
|
|
236
|
+
// パス2と揃える(--name-status はテキスト差分を作らないので発火しないが、
|
|
237
|
+
// 「読む人の git 設定に左右させない」の徹底を非対称にしない)
|
|
238
|
+
"--no-ext-diff",
|
|
239
|
+
"--name-status",
|
|
240
|
+
`--format=%x00%H%x1f%aI%x1f%an%x1f%ae%x1f%s`,
|
|
241
|
+
"--",
|
|
242
|
+
RECORD_PATHSPEC,
|
|
243
|
+
]);
|
|
244
|
+
const commits = [];
|
|
245
|
+
let cur = null;
|
|
246
|
+
for (const line of out.split("\n")) {
|
|
247
|
+
if (line.startsWith(NUL)) {
|
|
248
|
+
const parts = line.slice(1).split(US);
|
|
249
|
+
// 件名に区切り文字が混ざっても壊れないよう、5つ目以降は件名として繋ぎ直す
|
|
250
|
+
cur = {
|
|
251
|
+
sha: parts[0] || "",
|
|
252
|
+
at: parts[1] || "",
|
|
253
|
+
name: parts[2] || "",
|
|
254
|
+
email: parts[3] || "",
|
|
255
|
+
subject: parts.slice(4).join(US),
|
|
256
|
+
files: [],
|
|
257
|
+
};
|
|
258
|
+
if (cur.sha)
|
|
259
|
+
commits.push(cur);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (!cur)
|
|
263
|
+
continue;
|
|
264
|
+
const m = /^([A-Z])\d*\t(.+)$/.exec(line);
|
|
265
|
+
if (m)
|
|
266
|
+
cur.files.push({ status: m[1], path: m[2] });
|
|
267
|
+
}
|
|
268
|
+
return commits;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* パス2: 変更(M)の差分だけ。鍵は `<sha>` + NUL + `<path>`。
|
|
272
|
+
*
|
|
273
|
+
* 🔴 **遡る件数はパス1が決める(`mCount` = パス1の窓に入った「変更」コミットの数)。**
|
|
274
|
+
* `--diff-filter=M` は表示だけでなく**コミットの選択**にも効くので、ここで素の
|
|
275
|
+
* `-n400` を渡すと「変更のコミット 400 件」を探して**履歴の底まで歩き、その全部の
|
|
276
|
+
* パッチを生成する**——レコードの変更が少ないリポジトリほど無駄が大きく、
|
|
277
|
+
* 作った差分の大半はパス1の窓の外で一度も引かれない。
|
|
278
|
+
*
|
|
279
|
+
* パス1の窓は「レコードを触ったコミットの新しい 400 件」という連続した区間なので、
|
|
280
|
+
* その中の変更コミットは**全体で最も新しい mCount 件の変更コミットと一致する**。
|
|
281
|
+
* だから `-n mCount` で必要十分(多くも少なくもならない)。
|
|
282
|
+
*/
|
|
283
|
+
function readDiffs(cwd, mCount) {
|
|
284
|
+
if (mCount <= 0)
|
|
285
|
+
return new Map();
|
|
286
|
+
const out = gitLog(cwd, [
|
|
287
|
+
"log",
|
|
288
|
+
`-n${mCount}`,
|
|
289
|
+
"--no-renames",
|
|
290
|
+
"--no-follow",
|
|
291
|
+
"--no-color",
|
|
292
|
+
// diff.external / GIT_EXTERNAL_DIFF が設定されていると差分が丸ごと別物になる
|
|
293
|
+
"--no-ext-diff",
|
|
294
|
+
// 接頭辞を明示して固定する(diff.mnemonicPrefix / diff.noprefix の影響を消す)
|
|
295
|
+
"--src-prefix=a/",
|
|
296
|
+
"--dst-prefix=b/",
|
|
297
|
+
"-p",
|
|
298
|
+
// 文脈行は要らない。**変わった行だけ**を出す(画面のラベルも「消えた行/増えた行」)
|
|
299
|
+
"-U0",
|
|
300
|
+
"--diff-filter=M",
|
|
301
|
+
`--format=%x00%H`,
|
|
302
|
+
"--",
|
|
303
|
+
RECORD_PATHSPEC,
|
|
304
|
+
]);
|
|
305
|
+
const diffs = new Map();
|
|
306
|
+
let sha = "";
|
|
307
|
+
let cur = null;
|
|
308
|
+
// 🔴 **ヘッダの見分けは「位置」で決める。行の見た目で決めない。**
|
|
309
|
+
// 差分の中身は「符号1文字 + 元の行」なので、レコードに `++ …` という行があると
|
|
310
|
+
// `+++ …` に、`-- …` があると `--- …` になる——**ファイルのヘッダと区別が付かない**。
|
|
311
|
+
// 見た目で判定すると、その行を落とすだけでなく**以降の差分が別のパスの下に入る**。
|
|
312
|
+
// 区間で見る: `diff --git` から最初の `@@` までがヘッダ、そこから次の
|
|
313
|
+
// `diff --git` までが中身。
|
|
314
|
+
let inHeader = false;
|
|
315
|
+
for (const line of out.split("\n")) {
|
|
316
|
+
if (line.startsWith(NUL)) {
|
|
317
|
+
sha = line.slice(1).trim();
|
|
318
|
+
cur = null;
|
|
319
|
+
inHeader = false;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (line.startsWith("diff --git ")) {
|
|
323
|
+
cur = null;
|
|
324
|
+
inHeader = true;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (inHeader) {
|
|
328
|
+
// パスは `+++ b/<path>` から取る(上の行は空白を含むパスで割れない)
|
|
329
|
+
if (line.startsWith("+++ ")) {
|
|
330
|
+
const p = line.slice(4).trim();
|
|
331
|
+
if (p === "/dev/null" || !sha) {
|
|
332
|
+
cur = null;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const path = p.startsWith("b/") ? p.slice(2) : p;
|
|
336
|
+
cur = { before: [], after: [] };
|
|
337
|
+
diffs.set(sha + NUL + path, cur);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// 最初のハンク見出しでヘッダは終わり(index / old mode / `---` はここで捨てる)
|
|
341
|
+
if (line.startsWith("@@"))
|
|
342
|
+
inHeader = false;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (!cur)
|
|
346
|
+
continue;
|
|
347
|
+
// 2つ目以降のハンク見出し。中身の行には必ず符号が付くので、素の `@@` は見出しだけ
|
|
348
|
+
if (line.startsWith("@@"))
|
|
349
|
+
continue;
|
|
350
|
+
// `` は符号が付かないので、下の2つに落ちず自然に無視される
|
|
351
|
+
if (line.startsWith("-"))
|
|
352
|
+
cur.before.push(line.slice(1));
|
|
353
|
+
else if (line.startsWith("+"))
|
|
354
|
+
cur.after.push(line.slice(1));
|
|
355
|
+
}
|
|
356
|
+
return diffs;
|
|
357
|
+
}
|
|
358
|
+
/** 長すぎる差分は丸める(site の JSON に丸ごと載るので上限が要る) */
|
|
359
|
+
function clampDiff(text) {
|
|
360
|
+
if (text.length <= AI_HISTORY_MAX_DIFF_CHARS)
|
|
361
|
+
return { text, truncated: false };
|
|
362
|
+
let end = AI_HISTORY_MAX_DIFF_CHARS;
|
|
363
|
+
// 上位サロゲートで切ると、絵文字が半分だけ残って末尾が置換文字になる。1つ戻す
|
|
364
|
+
const code = text.charCodeAt(end - 1);
|
|
365
|
+
if (code >= 0xd800 && code <= 0xdbff)
|
|
366
|
+
end -= 1;
|
|
367
|
+
return { text: text.slice(0, end), truncated: true };
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* レコードのリポジトリ相対パス → AIによる変更(新しい順・最大 AI_HISTORY_MAX_ENTRIES 件)。
|
|
371
|
+
*
|
|
372
|
+
* 画面は既存の `repoPathOf(rec)`(editUrl から剥がしたパス)でここを引く。
|
|
373
|
+
* **いまリポジトリに無いレコード(最新の状態が削除)は地図に入れない**——
|
|
374
|
+
* もう開けないページの履歴を配っても読む人がいない。
|
|
375
|
+
*/
|
|
376
|
+
function collectAiChangeHistory(cwd) {
|
|
377
|
+
const commits = readCommits(cwd);
|
|
378
|
+
if (!commits.length)
|
|
379
|
+
return {};
|
|
380
|
+
// パス2に渡す件数を、パス1の窓に入った「変更」コミットの数から数える
|
|
381
|
+
let mCount = 0;
|
|
382
|
+
for (const c of commits) {
|
|
383
|
+
if (c.files.some((f) => f.status === "M" && RECORD_PATH.test(f.path)))
|
|
384
|
+
mCount++;
|
|
385
|
+
}
|
|
386
|
+
const diffs = readDiffs(cwd, mCount);
|
|
387
|
+
const history = {};
|
|
388
|
+
/**
|
|
389
|
+
* 🔴 **削除でそのパスの収集を打ち切る。** 新しい順に走っているので、削除より古い世代は
|
|
390
|
+
* **人が消したはずの内容**——同じパスに記録が作り直されると(決定は除外リストを持たないので
|
|
391
|
+
* 実際に起こりうる)、消したはずの本文が新しい記録のページに「AIが書き換えました」として
|
|
392
|
+
* 出てしまう。最新が削除なら1件も溜まらないので、地図にも入らない(同じ1つの規則で両方効く)。
|
|
393
|
+
*/
|
|
394
|
+
const gone = new Set();
|
|
395
|
+
for (const c of commits) {
|
|
396
|
+
const ai = isAiCommit(c);
|
|
397
|
+
for (const f of c.files) {
|
|
398
|
+
const p = f.path;
|
|
399
|
+
if (!RECORD_PATH.test(p))
|
|
400
|
+
continue;
|
|
401
|
+
const base = p.slice(p.lastIndexOf("/") + 1).toLowerCase();
|
|
402
|
+
if (base === "readme.md" || base === "template.md")
|
|
403
|
+
continue;
|
|
404
|
+
if (gone.has(p))
|
|
405
|
+
continue;
|
|
406
|
+
if (f.status === "D") {
|
|
407
|
+
gone.add(p);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (!ai)
|
|
411
|
+
continue;
|
|
412
|
+
// A(作成)と M(変更)だけ。T(ファイル↔シンボリックリンクの置き換え)等は
|
|
413
|
+
// 「何が変わったか」を言えないので出さない
|
|
414
|
+
if (f.status !== "A" && f.status !== "M")
|
|
415
|
+
continue;
|
|
416
|
+
const list = history[p] || (history[p] = []);
|
|
417
|
+
if (list.length >= exports.AI_HISTORY_MAX_ENTRIES)
|
|
418
|
+
continue;
|
|
419
|
+
const entry = {
|
|
420
|
+
commit: c.sha,
|
|
421
|
+
at: c.at,
|
|
422
|
+
subject: c.subject,
|
|
423
|
+
kind: f.status === "A" ? "add" : "edit",
|
|
424
|
+
before: "",
|
|
425
|
+
after: "",
|
|
426
|
+
};
|
|
427
|
+
// **「追加(A)の差分を載せない」の門は readDiffs の --diff-filter=M ただ1つ。**
|
|
428
|
+
// ここでも status を見て二重に守ると、どちらを外しても症状が出ず、
|
|
429
|
+
// **テストで守れない不変条件**になる(実際に変異検証で素通りした)。門は1箇所に置く
|
|
430
|
+
// モード変更だけの M・binary 扱いの M は `+++` も `@@` も出さないので、ここは空になる
|
|
431
|
+
//(画面は「変わった行をここに出せませんでした」に倒れる)。.gitattributes で
|
|
432
|
+
// `*.md` に `-diff` を付けている案件では全件がその形になる
|
|
433
|
+
const d = diffs.get(c.sha + NUL + p);
|
|
434
|
+
if (d) {
|
|
435
|
+
const before = clampDiff(d.before.join("\n"));
|
|
436
|
+
const after = clampDiff(d.after.join("\n"));
|
|
437
|
+
entry.before = before.text;
|
|
438
|
+
entry.after = after.text;
|
|
439
|
+
if (before.truncated || after.truncated)
|
|
440
|
+
entry.truncated = true;
|
|
441
|
+
}
|
|
442
|
+
list.push(entry);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// 空の器は返さない(「知らないことはキーが無いことで表す」)
|
|
446
|
+
for (const p of Object.keys(history)) {
|
|
447
|
+
if (!history[p].length)
|
|
448
|
+
delete history[p];
|
|
449
|
+
}
|
|
450
|
+
return history;
|
|
451
|
+
}
|
package/package.json
CHANGED