git-fs-s3 0.3.12 → 0.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/dist/chunk-RMAOPWHJ.js +38 -0
- package/dist/chunk-RMAOPWHJ.js.map +1 -0
- package/dist/{chunk-3BDDEGAP.js → chunk-Z4656PGE.js} +6 -2
- package/dist/chunk-Z4656PGE.js.map +1 -0
- package/dist/http.cjs +367 -80
- package/dist/http.cjs.map +1 -1
- package/dist/http.d.cts +11 -2
- package/dist/http.d.ts +11 -2
- package/dist/http.js +273 -80
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +145 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -3
- package/dist/index.d.ts +101 -3
- package/dist/index.js +114 -16
- package/dist/index.js.map +1 -1
- package/dist/ops.cjs +4 -1
- package/dist/ops.cjs.map +1 -1
- package/dist/ops.js +1 -1
- package/dist/s3.d.cts +1 -1
- package/dist/s3.d.ts +1 -1
- package/dist/{types-QgIkUR_q.d.cts → types-dbHzZ4ed.d.cts} +13 -3
- package/dist/{types-QgIkUR_q.d.ts → types-dbHzZ4ed.d.ts} +13 -3
- package/package.json +9 -9
- package/dist/chunk-3BDDEGAP.js.map +0 -1
package/dist/http.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
packFileName,
|
|
3
|
+
parsePackDirectory
|
|
4
|
+
} from "./chunk-RMAOPWHJ.js";
|
|
5
|
+
import {
|
|
6
|
+
listBranches,
|
|
7
|
+
listLooseRefNames,
|
|
2
8
|
resolveLooseRefFast
|
|
3
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Z4656PGE.js";
|
|
4
10
|
import {
|
|
5
11
|
concat,
|
|
6
12
|
decodeAscii,
|
|
@@ -9,7 +15,8 @@ import {
|
|
|
9
15
|
encodeUtf8,
|
|
10
16
|
fromHex,
|
|
11
17
|
isSafeFullRefName,
|
|
12
|
-
sha1
|
|
18
|
+
sha1,
|
|
19
|
+
toHex
|
|
13
20
|
} from "./chunk-YQFNY6PG.js";
|
|
14
21
|
|
|
15
22
|
// src/http/info-refs.ts
|
|
@@ -73,34 +80,24 @@ function rawFs(repo) {
|
|
|
73
80
|
|
|
74
81
|
// src/http/info-refs.ts
|
|
75
82
|
async function listAllRefs(repo, defaultBranch = "main") {
|
|
76
|
-
const [branches,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Promise.resolve(git.currentBranch({ ...repo, fullname: true })).then((cb) => cb ?? `refs/heads/${defaultBranch}`).catch(() => `refs/heads/${defaultBranch}`)
|
|
81
|
-
]);
|
|
82
|
-
const [branchRefs, tagRefs] = await Promise.all([
|
|
83
|
-
Promise.all(
|
|
84
|
-
branches.map(async (branch) => {
|
|
85
|
-
try {
|
|
86
|
-
const oid = await resolveLooseRefFast(repo, `refs/heads/${branch}`);
|
|
87
|
-
return { name: `refs/heads/${branch}`, oid };
|
|
88
|
-
} catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
),
|
|
93
|
-
Promise.all(
|
|
94
|
-
tags.map(async (tag) => {
|
|
95
|
-
try {
|
|
96
|
-
const oid = await resolveLooseRefFast(repo, `refs/tags/${tag}`);
|
|
97
|
-
return { name: `refs/tags/${tag}`, oid };
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
})
|
|
83
|
+
const [branches, tagNames] = await Promise.all([
|
|
84
|
+
listBranches(repo),
|
|
85
|
+
listLooseRefNames(repo, "refs/tags").then(
|
|
86
|
+
(names) => names ?? git.listTags(repo)
|
|
102
87
|
)
|
|
103
88
|
]);
|
|
89
|
+
const tagRefs = await Promise.all(
|
|
90
|
+
tagNames.map(async (tag) => {
|
|
91
|
+
const name = `refs/tags/${tag}`;
|
|
92
|
+
return resolveLooseRefFast(repo, name).then((oid) => ({ name, oid })).catch(() => null);
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
const branchRefs = branches.map((b) => ({
|
|
96
|
+
name: `refs/heads/${b.name}`,
|
|
97
|
+
oid: b.commit
|
|
98
|
+
}));
|
|
99
|
+
const defaultRef = branches.find((b) => b.isDefault);
|
|
100
|
+
const headSymref = defaultRef ? `refs/heads/${defaultRef.name}` : await Promise.resolve(git.currentBranch({ ...repo, fullname: true })).then((cb) => cb ?? `refs/heads/${defaultBranch}`).catch(() => `refs/heads/${defaultBranch}`);
|
|
104
101
|
const headOid = branchRefs.find((r) => r?.name === headSymref)?.oid ?? await git.resolveRef({ ...repo, ref: "HEAD" }).catch(() => null);
|
|
105
102
|
const refs = [];
|
|
106
103
|
if (headOid) refs.push({ name: "HEAD", oid: headOid });
|
|
@@ -153,7 +150,8 @@ async function handleInfoRefs(repo, options, hooks) {
|
|
|
153
150
|
import git2 from "isomorphic-git";
|
|
154
151
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
155
152
|
var MISSING_OBJECT_RETRY_DELAY_MS = 200;
|
|
156
|
-
async function collectReachableOids(repo, startOids, hooks, haveOids) {
|
|
153
|
+
async function collectReachableOids(repo, startOids, hooks, haveOids, options) {
|
|
154
|
+
const verifyBlobs = options?.verifyBlobs ?? true;
|
|
157
155
|
const have = haveOids instanceof Set ? haveOids : new Set(haveOids ?? []);
|
|
158
156
|
const seen = /* @__PURE__ */ new Set();
|
|
159
157
|
let complete = true;
|
|
@@ -161,23 +159,26 @@ async function collectReachableOids(repo, startOids, hooks, haveOids) {
|
|
|
161
159
|
async function readAndVisitChildren(oid) {
|
|
162
160
|
const obj = await git2.readObject({ ...repo, oid });
|
|
163
161
|
seen.add(oid);
|
|
164
|
-
|
|
162
|
+
const children = [];
|
|
165
163
|
if (obj.type === "commit") {
|
|
166
|
-
const
|
|
167
|
-
children
|
|
164
|
+
const commit = obj.object;
|
|
165
|
+
children.push([commit.tree, "tree"]);
|
|
166
|
+
for (const parent of commit.parent) children.push([parent, "commit"]);
|
|
168
167
|
} else if (obj.type === "tree") {
|
|
169
|
-
const
|
|
170
|
-
|
|
168
|
+
const tree = obj.object;
|
|
169
|
+
for (const entry of tree) {
|
|
170
|
+
if (entry.type === "commit") continue;
|
|
171
|
+
children.push([entry.oid, entry.type]);
|
|
172
|
+
}
|
|
171
173
|
} else if (obj.type === "tag") {
|
|
172
|
-
|
|
173
|
-
children = [tag.object];
|
|
174
|
+
children.push([obj.object.object, void 0]);
|
|
174
175
|
}
|
|
175
|
-
await Promise.all(children.map(visit));
|
|
176
|
+
await Promise.all(children.map(([child, type]) => visit(child, type)));
|
|
176
177
|
}
|
|
177
|
-
function visit(oid) {
|
|
178
|
+
function visit(oid, type) {
|
|
178
179
|
const existing = promises.get(oid);
|
|
179
180
|
if (existing) return existing;
|
|
180
|
-
if (have.has(oid)) {
|
|
181
|
+
if (have.has(oid) || type === "blob" && !verifyBlobs) {
|
|
181
182
|
seen.add(oid);
|
|
182
183
|
const p2 = Promise.resolve();
|
|
183
184
|
promises.set(oid, p2);
|
|
@@ -199,7 +200,7 @@ async function collectReachableOids(repo, startOids, hooks, haveOids) {
|
|
|
199
200
|
promises.set(oid, p);
|
|
200
201
|
return p;
|
|
201
202
|
}
|
|
202
|
-
await Promise.all(startOids.map(visit));
|
|
203
|
+
await Promise.all(startOids.map((oid) => visit(oid)));
|
|
203
204
|
return { oids: Array.from(seen), complete };
|
|
204
205
|
}
|
|
205
206
|
|
|
@@ -208,6 +209,167 @@ import git4 from "isomorphic-git";
|
|
|
208
209
|
|
|
209
210
|
// src/http/repack.ts
|
|
210
211
|
import git3 from "isomorphic-git";
|
|
212
|
+
|
|
213
|
+
// src/http/pack-reuse.ts
|
|
214
|
+
var OBJ_OFS_DELTA = 6;
|
|
215
|
+
var OBJ_REF_DELTA = 7;
|
|
216
|
+
var OID_BYTES = 20;
|
|
217
|
+
var PACK_TRAILER_BYTES = 20;
|
|
218
|
+
var IDX_V2 = {
|
|
219
|
+
magic: 4285812579,
|
|
220
|
+
version: 2,
|
|
221
|
+
headerBytes: 8,
|
|
222
|
+
fanoutEntries: 256,
|
|
223
|
+
/** Offsets with this bit set index the 64-bit large-offset table. */
|
|
224
|
+
largeOffsetFlag: 2147483648
|
|
225
|
+
};
|
|
226
|
+
function readPackIndex(idx) {
|
|
227
|
+
const view = new DataView(idx.buffer, idx.byteOffset, idx.byteLength);
|
|
228
|
+
const namesStart = IDX_V2.headerBytes + IDX_V2.fanoutEntries * 4;
|
|
229
|
+
if (idx.byteLength < namesStart || view.getUint32(0) !== IDX_V2.magic || view.getUint32(4) !== IDX_V2.version) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const count = view.getUint32(namesStart - 4);
|
|
233
|
+
const crcStart = namesStart + count * OID_BYTES;
|
|
234
|
+
const offsetsStart = crcStart + count * 4;
|
|
235
|
+
if (idx.byteLength < offsetsStart + count * 4) return null;
|
|
236
|
+
const entries = [];
|
|
237
|
+
for (let i = 0; i < count; i++) {
|
|
238
|
+
const offset = view.getUint32(offsetsStart + i * 4);
|
|
239
|
+
if (offset & IDX_V2.largeOffsetFlag) return null;
|
|
240
|
+
const at = namesStart + i * OID_BYTES;
|
|
241
|
+
entries.push({ oid: toHex(idx.subarray(at, at + OID_BYTES)), offset });
|
|
242
|
+
}
|
|
243
|
+
return entries;
|
|
244
|
+
}
|
|
245
|
+
function indexPack(idx, pack) {
|
|
246
|
+
const located = readPackIndex(idx);
|
|
247
|
+
if (!located) return null;
|
|
248
|
+
located.sort((a, b) => a.offset - b.offset);
|
|
249
|
+
const entries = /* @__PURE__ */ new Map();
|
|
250
|
+
const oidAtOffset = /* @__PURE__ */ new Map();
|
|
251
|
+
const packEnd = pack.byteLength - PACK_TRAILER_BYTES;
|
|
252
|
+
for (let i = 0; i < located.length; i++) {
|
|
253
|
+
const { oid, offset } = located[i];
|
|
254
|
+
const end = located[i + 1]?.offset ?? packEnd;
|
|
255
|
+
entries.set(oid, { pack, start: offset, end });
|
|
256
|
+
oidAtOffset.set(offset, oid);
|
|
257
|
+
}
|
|
258
|
+
return { entries, oidAtOffset };
|
|
259
|
+
}
|
|
260
|
+
function readEntryHeader(bytes, start) {
|
|
261
|
+
let byte = bytes[start];
|
|
262
|
+
const type = byte >> 4 & 7;
|
|
263
|
+
let size = byte & 15;
|
|
264
|
+
let shift = 4;
|
|
265
|
+
let at = start + 1;
|
|
266
|
+
while (byte & 128) {
|
|
267
|
+
byte = bytes[at++];
|
|
268
|
+
size += (byte & 127) * 2 ** shift;
|
|
269
|
+
shift += 7;
|
|
270
|
+
}
|
|
271
|
+
return { type, size, length: at - start };
|
|
272
|
+
}
|
|
273
|
+
function encodeEntryHeader(type, size) {
|
|
274
|
+
const bytes = [];
|
|
275
|
+
let rest = Math.floor(size / 16);
|
|
276
|
+
bytes.push((rest > 0 ? 128 : 0) | type << 4 | size % 16);
|
|
277
|
+
while (rest > 0) {
|
|
278
|
+
const low = rest % 128;
|
|
279
|
+
rest = Math.floor(rest / 128);
|
|
280
|
+
bytes.push((rest > 0 ? 128 : 0) | low);
|
|
281
|
+
}
|
|
282
|
+
return new Uint8Array(bytes);
|
|
283
|
+
}
|
|
284
|
+
function readOfsDeltaOffset(bytes, start) {
|
|
285
|
+
let at = start;
|
|
286
|
+
let byte = bytes[at++];
|
|
287
|
+
let distance = byte & 127;
|
|
288
|
+
while (byte & 128) {
|
|
289
|
+
byte = bytes[at++];
|
|
290
|
+
distance = (distance + 1) * 128 + (byte & 127);
|
|
291
|
+
}
|
|
292
|
+
return { distance, length: at - start };
|
|
293
|
+
}
|
|
294
|
+
function reusableEntry(entry, indexed) {
|
|
295
|
+
const { pack, start, end } = entry;
|
|
296
|
+
const header = readEntryHeader(pack, start);
|
|
297
|
+
const bodyStart = start + header.length;
|
|
298
|
+
if (header.type === OBJ_REF_DELTA) {
|
|
299
|
+
return {
|
|
300
|
+
bytes: pack.subarray(start, end),
|
|
301
|
+
baseOid: toHex(pack.subarray(bodyStart, bodyStart + OID_BYTES))
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (header.type !== OBJ_OFS_DELTA) {
|
|
305
|
+
return { bytes: pack.subarray(start, end), baseOid: null };
|
|
306
|
+
}
|
|
307
|
+
const { distance, length } = readOfsDeltaOffset(pack, bodyStart);
|
|
308
|
+
const baseOid = indexed.oidAtOffset.get(start - distance);
|
|
309
|
+
if (baseOid === void 0) return null;
|
|
310
|
+
return {
|
|
311
|
+
bytes: concat(
|
|
312
|
+
encodeEntryHeader(OBJ_REF_DELTA, header.size),
|
|
313
|
+
fromHex(baseOid),
|
|
314
|
+
pack.subarray(bodyStart + length, end)
|
|
315
|
+
),
|
|
316
|
+
baseOid
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async function buildPackFromExistingPacks(repo, oids) {
|
|
320
|
+
const fsp = rawFs(repo);
|
|
321
|
+
const packDir = `${repo.gitdir}/objects/pack`;
|
|
322
|
+
const packs = parsePackDirectory(
|
|
323
|
+
await fsp.readdir(packDir).catch(() => [])
|
|
324
|
+
).filter((file) => file.kind === "pack");
|
|
325
|
+
const indexes = [];
|
|
326
|
+
for (const { base } of packs) {
|
|
327
|
+
const [pack, idx] = await Promise.all([
|
|
328
|
+
fsp.readFile(`${packDir}/${packFileName(base, "pack")}`),
|
|
329
|
+
fsp.readFile(`${packDir}/${packFileName(base, "idx")}`)
|
|
330
|
+
]).catch(() => [null, null]);
|
|
331
|
+
if (!(pack instanceof Uint8Array) || !(idx instanceof Uint8Array)) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const indexed = indexPack(idx, pack);
|
|
335
|
+
if (!indexed) return null;
|
|
336
|
+
indexes.push(indexed);
|
|
337
|
+
}
|
|
338
|
+
const locate = (oid) => {
|
|
339
|
+
for (const indexed of indexes) {
|
|
340
|
+
const entry = indexed.entries.get(oid);
|
|
341
|
+
if (entry) return reusableEntry(entry, indexed);
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
};
|
|
345
|
+
const ordered = [];
|
|
346
|
+
const done = /* @__PURE__ */ new Set();
|
|
347
|
+
const pending = /* @__PURE__ */ new Set();
|
|
348
|
+
const visit = (oid) => {
|
|
349
|
+
if (done.has(oid)) return true;
|
|
350
|
+
if (pending.has(oid)) return false;
|
|
351
|
+
const entry = locate(oid);
|
|
352
|
+
if (!entry) return false;
|
|
353
|
+
pending.add(oid);
|
|
354
|
+
if (entry.baseOid !== null && !visit(entry.baseOid)) return false;
|
|
355
|
+
pending.delete(oid);
|
|
356
|
+
done.add(oid);
|
|
357
|
+
ordered.push(entry.bytes);
|
|
358
|
+
return true;
|
|
359
|
+
};
|
|
360
|
+
for (const oid of oids) {
|
|
361
|
+
if (!visit(oid)) return null;
|
|
362
|
+
}
|
|
363
|
+
const header = new Uint8Array(12);
|
|
364
|
+
header.set(encodeUtf8("PACK"), 0);
|
|
365
|
+
const view = new DataView(header.buffer);
|
|
366
|
+
view.setUint32(4, 2);
|
|
367
|
+
view.setUint32(8, ordered.length);
|
|
368
|
+
const body = concat(header, ...ordered);
|
|
369
|
+
return concat(body, fromHex(await sha1(body)));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/http/repack.ts
|
|
211
373
|
var PACK_OBJECT_TYPE_BITS = {
|
|
212
374
|
commit: 16,
|
|
213
375
|
tree: 32,
|
|
@@ -291,13 +453,13 @@ async function buildVerifiedPack(oids, objects) {
|
|
|
291
453
|
return concat(packBody, trailer);
|
|
292
454
|
}
|
|
293
455
|
var REPACK_PACK_COUNT_THRESHOLD = 4;
|
|
456
|
+
async function readPackDirectory(repo) {
|
|
457
|
+
const entries = await rawFs(repo).readdir(`${repo.gitdir}/objects/pack`).catch(() => []);
|
|
458
|
+
return parsePackDirectory(entries);
|
|
459
|
+
}
|
|
294
460
|
async function countPacks(repo) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return entries.filter((f) => f.endsWith(".pack")).length;
|
|
298
|
-
} catch {
|
|
299
|
-
return 0;
|
|
300
|
-
}
|
|
461
|
+
const files = await readPackDirectory(repo);
|
|
462
|
+
return files.filter((file) => file.kind === "pack").length;
|
|
301
463
|
}
|
|
302
464
|
async function repackRepository(repo, options, hooks) {
|
|
303
465
|
const threshold = options?.threshold ?? REPACK_PACK_COUNT_THRESHOLD;
|
|
@@ -320,41 +482,48 @@ async function repackRepository(repo, options, hooks) {
|
|
|
320
482
|
)
|
|
321
483
|
)).filter((oid) => oid !== null);
|
|
322
484
|
if (tipOids.length === 0) return [];
|
|
323
|
-
const { oids, complete } = await collectReachableOids(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
485
|
+
const { oids: reachable, complete } = await collectReachableOids(
|
|
486
|
+
repo,
|
|
487
|
+
tipOids,
|
|
488
|
+
hooks
|
|
489
|
+
);
|
|
490
|
+
if (!complete || reachable.length === 0) return [];
|
|
327
491
|
const packDir = `${repo.gitdir}/objects/pack`;
|
|
492
|
+
const packBuffer = await buildPackFromExistingPacks(repo, reachable) ?? await buildVerifiedPack(
|
|
493
|
+
reachable,
|
|
494
|
+
await readAndVerifyObjects(repo, reachable)
|
|
495
|
+
);
|
|
328
496
|
await fsp.mkdir(packDir, { recursive: true });
|
|
329
497
|
const newBase = `pack-${Date.now()}`;
|
|
330
|
-
const newPackFile =
|
|
331
|
-
const newIdxFile = `${newBase}.idx`;
|
|
498
|
+
const newPackFile = packFileName(newBase, "pack");
|
|
332
499
|
await fsp.writeFile(`${packDir}/${newPackFile}`, packBuffer);
|
|
333
500
|
const { oids: indexedOids } = await git3.indexPack({
|
|
334
501
|
...repo,
|
|
335
502
|
dir: packDir,
|
|
336
503
|
filepath: newPackFile
|
|
337
504
|
});
|
|
338
|
-
const expected = new Set(oids);
|
|
339
505
|
const indexed = new Set(indexedOids);
|
|
340
|
-
if (
|
|
341
|
-
await
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
506
|
+
if (reachable.some((oid) => !indexed.has(oid))) {
|
|
507
|
+
await Promise.all(
|
|
508
|
+
["pack", "idx"].map(
|
|
509
|
+
(kind) => fsp.unlink(`${packDir}/${packFileName(newBase, kind)}`).catch(() => {
|
|
510
|
+
})
|
|
511
|
+
)
|
|
512
|
+
);
|
|
345
513
|
throw new Error(
|
|
346
|
-
"repack: indexed pack
|
|
514
|
+
"repack: indexed pack is missing reachable objects \u2014 aborting"
|
|
347
515
|
);
|
|
348
516
|
}
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
(f) => f !== newPackFile && f !== newIdxFile && (f.endsWith(".pack") || f.endsWith(".idx") || f.endsWith(".keep"))
|
|
517
|
+
const stale = (await readPackDirectory(repo)).filter(
|
|
518
|
+
(file) => file.base !== newBase
|
|
352
519
|
);
|
|
353
520
|
await Promise.all(
|
|
354
|
-
|
|
355
|
-
|
|
521
|
+
stale.map(
|
|
522
|
+
(file) => fsp.unlink(`${packDir}/${file.name}`).catch(() => {
|
|
523
|
+
})
|
|
524
|
+
)
|
|
356
525
|
);
|
|
357
|
-
return
|
|
526
|
+
return stale.map((file) => `objects/pack/${file.name}`);
|
|
358
527
|
} catch (err) {
|
|
359
528
|
hooks?.onWarn?.("repack failed (non-fatal)", err);
|
|
360
529
|
return [];
|
|
@@ -428,6 +597,20 @@ async function checkRefCas(repo, refName, oldOid) {
|
|
|
428
597
|
reason: "non-fast-forward, ref updated by another push"
|
|
429
598
|
};
|
|
430
599
|
}
|
|
600
|
+
async function collectExistingRefTips(repo) {
|
|
601
|
+
const [branches, tags] = await Promise.all([
|
|
602
|
+
git4.listBranches(repo),
|
|
603
|
+
git4.listTags(repo)
|
|
604
|
+
]);
|
|
605
|
+
const refs = [
|
|
606
|
+
...branches.map((b) => `refs/heads/${b}`),
|
|
607
|
+
...tags.map((t) => `refs/tags/${t}`)
|
|
608
|
+
];
|
|
609
|
+
const oids = await Promise.all(
|
|
610
|
+
refs.map((ref) => git4.resolveRef({ ...repo, ref }).catch(() => null))
|
|
611
|
+
);
|
|
612
|
+
return new Set(oids.filter((oid) => oid !== null));
|
|
613
|
+
}
|
|
431
614
|
async function validateRefUpdates(repo, refUpdates) {
|
|
432
615
|
return Promise.all(
|
|
433
616
|
refUpdates.map(
|
|
@@ -491,7 +674,10 @@ async function applyReceivePack(repo, parsed, options, hooks) {
|
|
|
491
674
|
);
|
|
492
675
|
const newTips = acceptedUpdates.filter((update) => update.newOid !== ZERO_OID).map((update) => update.newOid);
|
|
493
676
|
if (newTips.length > 0) {
|
|
494
|
-
const haveOids =
|
|
677
|
+
const haveOids = await collectExistingRefTips(repo);
|
|
678
|
+
for (const update of acceptedUpdates) {
|
|
679
|
+
if (update.oldOid !== ZERO_OID) haveOids.add(update.oldOid);
|
|
680
|
+
}
|
|
495
681
|
const { complete } = await collectReachableOids(
|
|
496
682
|
repo,
|
|
497
683
|
newTips,
|
|
@@ -572,7 +758,7 @@ async function handleUploadPack(repo, body, options, hooks) {
|
|
|
572
758
|
"readdir objects/pack",
|
|
573
759
|
() => rawFs(repo).readdir(packDirPath).catch(() => [])
|
|
574
760
|
);
|
|
575
|
-
const packNames = entries.filter((
|
|
761
|
+
const packNames = parsePackDirectory(entries).filter((file) => file.kind === "pack").map((file) => file.name);
|
|
576
762
|
const packName = packNames[0];
|
|
577
763
|
if (packNames.length === 1 && packName !== void 0) {
|
|
578
764
|
const packData = await runStep(
|
|
@@ -594,27 +780,34 @@ async function handleUploadPack(repo, body, options, hooks) {
|
|
|
594
780
|
if (options?.beforeWalk) {
|
|
595
781
|
await runStep(hooks, "beforeWalk", options.beforeWalk);
|
|
596
782
|
}
|
|
597
|
-
|
|
598
|
-
hooks,
|
|
599
|
-
"collectReachableOids(wants)",
|
|
600
|
-
() => collectReachableOids(repo, wants, hooks)
|
|
601
|
-
);
|
|
602
|
-
let oids = wantOids;
|
|
783
|
+
let haveSet;
|
|
603
784
|
if (haves.length > 0) {
|
|
604
785
|
const { oids: haveOids } = await runStep(
|
|
605
786
|
hooks,
|
|
606
787
|
"collectReachableOids(haves)",
|
|
607
|
-
() => collectReachableOids(repo, haves, hooks
|
|
788
|
+
() => collectReachableOids(repo, haves, hooks, void 0, {
|
|
789
|
+
verifyBlobs: false
|
|
790
|
+
})
|
|
608
791
|
);
|
|
609
|
-
|
|
610
|
-
oids = oids.filter((oid) => !haveSet.has(oid));
|
|
792
|
+
haveSet = new Set(haveOids);
|
|
611
793
|
}
|
|
612
|
-
const {
|
|
794
|
+
const { oids: wantOids } = await runStep(
|
|
795
|
+
hooks,
|
|
796
|
+
"collectReachableOids(wants)",
|
|
797
|
+
() => collectReachableOids(repo, wants, hooks, haveSet, {
|
|
798
|
+
verifyBlobs: false
|
|
799
|
+
})
|
|
800
|
+
);
|
|
801
|
+
const oids = haveSet ? wantOids.filter((oid) => !haveSet.has(oid)) : wantOids;
|
|
802
|
+
const reused = await runStep(
|
|
613
803
|
hooks,
|
|
614
|
-
`
|
|
615
|
-
() =>
|
|
804
|
+
`reuse pack entries (${oids.length} oids)`,
|
|
805
|
+
() => buildPackFromExistingPacks(repo, oids)
|
|
616
806
|
);
|
|
617
|
-
const packBytes =
|
|
807
|
+
const packBytes = reused ?? await runStep(hooks, `packObjects (${oids.length} oids)`, async () => {
|
|
808
|
+
const { packfile } = await git5.packObjects({ ...repo, oids });
|
|
809
|
+
return packfile instanceof Uint8Array ? packfile : new Uint8Array(packfile ?? []);
|
|
810
|
+
});
|
|
618
811
|
return {
|
|
619
812
|
status: 200,
|
|
620
813
|
headers: {
|