git-fs-s3 0.4.0 → 0.6.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 +23 -17
- package/dist/{chunk-YQFNY6PG.js → chunk-FIFWOMPM.js} +1 -1
- package/dist/chunk-FIFWOMPM.js.map +1 -0
- package/dist/{chunk-Z4656PGE.js → chunk-XNSZTA4Y.js} +3 -3
- package/dist/{chunk-Z4656PGE.js.map → chunk-XNSZTA4Y.js.map} +1 -1
- package/dist/http.js +8 -8
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/ops.js +15 -18
- package/dist/ops.js.map +1 -1
- package/dist/types/cache.d.ts +81 -0
- package/dist/types/edge-utils.d.ts +43 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/git-errors.d.ts +70 -0
- package/dist/types/git-fs.d.ts +60 -0
- package/dist/types/git-layout.d.ts +52 -0
- package/dist/types/http/index.d.ts +7 -0
- package/dist/types/http/info-refs.d.ts +28 -0
- package/dist/types/http/pack-reuse.d.ts +26 -0
- package/dist/types/http/pkt-line.d.ts +14 -0
- package/dist/types/http/reachability.d.ts +36 -0
- package/dist/types/http/receive-pack.d.ts +55 -0
- package/dist/types/http/repack.d.ts +24 -0
- package/dist/types/http/types.d.ts +40 -0
- package/dist/types/http/upload-pack.d.ts +21 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/ops/branch.d.ts +38 -0
- package/dist/types/ops/commit.d.ts +45 -0
- package/dist/types/ops/diff.d.ts +24 -0
- package/dist/types/ops/file-history.d.ts +41 -0
- package/dist/types/ops/history.d.ts +86 -0
- package/dist/types/ops/index.d.ts +9 -0
- package/dist/types/ops/last-commit.d.ts +23 -0
- package/dist/types/ops/merge.d.ts +28 -0
- package/dist/types/ops/tree.d.ts +19 -0
- package/dist/{types-BHoHOaQt.d.cts → types/ops/types.d.ts} +7 -9
- package/dist/types/pack-files.d.ts +28 -0
- package/dist/types/path.d.ts +9 -0
- package/dist/types/refs.d.ts +54 -0
- package/dist/types/retry.d.ts +52 -0
- package/dist/types/stores/memory.d.ts +16 -0
- package/dist/{s3.d.ts → types/stores/s3.d.ts} +4 -7
- package/dist/{types-dbHzZ4ed.d.ts → types/types.d.ts} +11 -22
- package/package.json +18 -22
- package/dist/chunk-YQFNY6PG.js.map +0 -1
- package/dist/http.cjs +0 -1063
- package/dist/http.cjs.map +0 -1
- package/dist/http.d.cts +0 -215
- package/dist/http.d.ts +0 -215
- package/dist/index.cjs +0 -944
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -479
- package/dist/index.d.ts +0 -479
- package/dist/ops.cjs +0 -1060
- package/dist/ops.cjs.map +0 -1
- package/dist/ops.d.cts +0 -302
- package/dist/ops.d.ts +0 -302
- package/dist/s3.cjs +0 -123
- package/dist/s3.cjs.map +0 -1
- package/dist/s3.d.cts +0 -36
- package/dist/types-BHoHOaQt.d.ts +0 -53
- package/dist/types-dbHzZ4ed.d.cts +0 -131
package/dist/http.cjs
DELETED
|
@@ -1,1063 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/http/index.ts
|
|
31
|
-
var http_exports = {};
|
|
32
|
-
__export(http_exports, {
|
|
33
|
-
FLUSH: () => FLUSH,
|
|
34
|
-
REPACK_PACK_COUNT_THRESHOLD: () => REPACK_PACK_COUNT_THRESHOLD,
|
|
35
|
-
applyReceivePack: () => applyReceivePack,
|
|
36
|
-
applyRefUpdates: () => applyRefUpdates,
|
|
37
|
-
collectReachableOids: () => collectReachableOids,
|
|
38
|
-
ensureRepoInitialized: () => ensureRepoInitialized,
|
|
39
|
-
handleInfoRefs: () => handleInfoRefs,
|
|
40
|
-
handleUploadPack: () => handleUploadPack,
|
|
41
|
-
indexIncomingPack: () => indexIncomingPack,
|
|
42
|
-
listAllRefs: () => listAllRefs,
|
|
43
|
-
parsePktLines: () => parsePktLines,
|
|
44
|
-
parseReceivePackBody: () => parseReceivePackBody,
|
|
45
|
-
pktLine: () => pktLine,
|
|
46
|
-
pktLineBuffer: () => pktLineBuffer,
|
|
47
|
-
rawFs: () => rawFs,
|
|
48
|
-
receivePackResponse: () => receivePackResponse,
|
|
49
|
-
repackRepository: () => repackRepository,
|
|
50
|
-
sideBandPackfile: () => sideBandPackfile
|
|
51
|
-
});
|
|
52
|
-
module.exports = __toCommonJS(http_exports);
|
|
53
|
-
|
|
54
|
-
// src/http/info-refs.ts
|
|
55
|
-
var import_isomorphic_git2 = __toESM(require("isomorphic-git"), 1);
|
|
56
|
-
|
|
57
|
-
// src/edge-utils.ts
|
|
58
|
-
var textEncoder = new TextEncoder();
|
|
59
|
-
var textDecoder = new TextDecoder();
|
|
60
|
-
function encodeUtf8(data) {
|
|
61
|
-
return textEncoder.encode(data);
|
|
62
|
-
}
|
|
63
|
-
function decodeUtf8(data) {
|
|
64
|
-
return textDecoder.decode(data);
|
|
65
|
-
}
|
|
66
|
-
function decodeAscii(data) {
|
|
67
|
-
let s = "";
|
|
68
|
-
for (let i = 0; i < data.length; i++)
|
|
69
|
-
s += String.fromCharCode(data[i]);
|
|
70
|
-
return s;
|
|
71
|
-
}
|
|
72
|
-
function concat(...parts) {
|
|
73
|
-
let total = 0;
|
|
74
|
-
for (const p of parts) total += p.length;
|
|
75
|
-
const out = new Uint8Array(total);
|
|
76
|
-
let offset = 0;
|
|
77
|
-
for (const p of parts) {
|
|
78
|
-
out.set(p, offset);
|
|
79
|
-
offset += p.length;
|
|
80
|
-
}
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
83
|
-
function toHex(data) {
|
|
84
|
-
let hex = "";
|
|
85
|
-
for (let i = 0; i < data.length; i++)
|
|
86
|
-
hex += data[i].toString(16).padStart(2, "0");
|
|
87
|
-
return hex;
|
|
88
|
-
}
|
|
89
|
-
function fromHex(hex) {
|
|
90
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
91
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
92
|
-
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
93
|
-
}
|
|
94
|
-
return bytes;
|
|
95
|
-
}
|
|
96
|
-
async function sha1(data) {
|
|
97
|
-
const bytes = typeof data === "string" ? encodeUtf8(data) : data;
|
|
98
|
-
const hash = await globalThis.crypto.subtle.digest("SHA-1", bytes);
|
|
99
|
-
return toHex(new Uint8Array(hash));
|
|
100
|
-
}
|
|
101
|
-
async function deflate(data) {
|
|
102
|
-
const stream = new Blob([data]).stream().pipeThrough(new CompressionStream("deflate"));
|
|
103
|
-
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/ops/branch.ts
|
|
107
|
-
var import_isomorphic_git = __toESM(require("isomorphic-git"), 1);
|
|
108
|
-
|
|
109
|
-
// src/refs.ts
|
|
110
|
-
var BAD_REF_COMPONENT = (
|
|
111
|
-
// biome-ignore lint/suspicious/noControlCharactersInRegex: control chars are exactly what git's own ref-name rules reject — this needs to match the same range.
|
|
112
|
-
/(^|[/.])([/.]|$)|^@$|@\{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/
|
|
113
|
-
);
|
|
114
|
-
var FULL_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
115
|
-
function isSafeFullRefName(ref) {
|
|
116
|
-
if (!ref.startsWith("refs/heads/") && !ref.startsWith("refs/tags/")) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
return !BAD_REF_COMPONENT.test(ref);
|
|
120
|
-
}
|
|
121
|
-
function isSafeBranchName(name) {
|
|
122
|
-
if (!name || name.startsWith("refs/") || name === "HEAD") return false;
|
|
123
|
-
if (FULL_SHA_RE.test(name)) return false;
|
|
124
|
-
return !BAD_REF_COMPONENT.test(name);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// src/ops/branch.ts
|
|
128
|
-
var FULL_SHA_RE2 = /^[0-9a-f]{40}$/i;
|
|
129
|
-
function hasRecursiveFileLister(fs) {
|
|
130
|
-
return typeof fs === "object" && fs !== null && "listFilesRecursively" in fs && typeof fs.listFilesRecursively === "function";
|
|
131
|
-
}
|
|
132
|
-
function getPromisesFs(fs) {
|
|
133
|
-
const promises = fs.promises;
|
|
134
|
-
return promises ?? null;
|
|
135
|
-
}
|
|
136
|
-
function decodeFileContent(content) {
|
|
137
|
-
return typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
138
|
-
}
|
|
139
|
-
async function listLooseBranches(repo) {
|
|
140
|
-
return listLooseRefNames(repo, "refs/heads");
|
|
141
|
-
}
|
|
142
|
-
async function listLooseRefNames(repo, dir) {
|
|
143
|
-
if (!hasRecursiveFileLister(repo.fs)) return null;
|
|
144
|
-
const names = await repo.fs.listFilesRecursively(`${repo.gitdir}/${dir}`);
|
|
145
|
-
return names.filter(isSafeBranchName);
|
|
146
|
-
}
|
|
147
|
-
async function readPackedBranches(repo) {
|
|
148
|
-
if (!hasRecursiveFileLister(repo.fs)) return /* @__PURE__ */ new Map();
|
|
149
|
-
const promisesFs = getPromisesFs(repo.fs);
|
|
150
|
-
if (!promisesFs) return /* @__PURE__ */ new Map();
|
|
151
|
-
try {
|
|
152
|
-
const content = await promisesFs.readFile(
|
|
153
|
-
`${repo.gitdir}/packed-refs`,
|
|
154
|
-
"utf8"
|
|
155
|
-
);
|
|
156
|
-
const refs = /* @__PURE__ */ new Map();
|
|
157
|
-
for (const line of decodeFileContent(content).split("\n")) {
|
|
158
|
-
const match = /^([0-9a-f]{40}) refs\/heads\/(.+)$/i.exec(line);
|
|
159
|
-
if (match?.[1] && match[2] && isSafeBranchName(match[2])) {
|
|
160
|
-
refs.set(match[2], match[1]);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return refs;
|
|
164
|
-
} catch {
|
|
165
|
-
return /* @__PURE__ */ new Map();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
async function getCurrentLooseBranch(repo) {
|
|
169
|
-
if (!hasRecursiveFileLister(repo.fs)) return null;
|
|
170
|
-
const promisesFs = getPromisesFs(repo.fs);
|
|
171
|
-
if (!promisesFs) return null;
|
|
172
|
-
try {
|
|
173
|
-
const content = await promisesFs.readFile(`${repo.gitdir}/HEAD`, "utf8");
|
|
174
|
-
const head = decodeFileContent(content).trim();
|
|
175
|
-
const match = /^ref: refs\/heads\/(.+)$/.exec(head);
|
|
176
|
-
return match?.[1] && isSafeBranchName(match[1]) ? match[1] : null;
|
|
177
|
-
} catch {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
async function resolveLooseRefFast(repo, ref) {
|
|
182
|
-
const promisesFs = getPromisesFs(repo.fs);
|
|
183
|
-
if (promisesFs) {
|
|
184
|
-
try {
|
|
185
|
-
const content = await promisesFs.readFile(
|
|
186
|
-
`${repo.gitdir}/${ref}`,
|
|
187
|
-
"utf8"
|
|
188
|
-
);
|
|
189
|
-
const oid = decodeFileContent(content).trim();
|
|
190
|
-
if (FULL_SHA_RE2.test(oid)) return oid;
|
|
191
|
-
} catch {
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return import_isomorphic_git.default.resolveRef({ ...repo, ref });
|
|
195
|
-
}
|
|
196
|
-
async function listBranches(repo) {
|
|
197
|
-
try {
|
|
198
|
-
const [looseBranches, looseCurrentBranch, packedBranches] = await Promise.all([
|
|
199
|
-
listLooseBranches(repo),
|
|
200
|
-
getCurrentLooseBranch(repo),
|
|
201
|
-
readPackedBranches(repo)
|
|
202
|
-
]);
|
|
203
|
-
const branches = looseBranches === null ? await import_isomorphic_git.default.listBranches(repo) : [.../* @__PURE__ */ new Set([...looseBranches, ...packedBranches.keys()])].sort();
|
|
204
|
-
const currentBranch = looseBranches === null ? await import_isomorphic_git.default.currentBranch({ ...repo, fullname: false }).catch(() => null) : looseCurrentBranch;
|
|
205
|
-
return Promise.all(
|
|
206
|
-
branches.map(async (branch) => {
|
|
207
|
-
const packedCommit = packedBranches.get(branch);
|
|
208
|
-
return {
|
|
209
|
-
name: branch,
|
|
210
|
-
commit: packedCommit && !looseBranches?.includes(branch) ? packedCommit : await resolveLooseRefFast(repo, `refs/heads/${branch}`),
|
|
211
|
-
isDefault: branch === currentBranch
|
|
212
|
-
};
|
|
213
|
-
})
|
|
214
|
-
);
|
|
215
|
-
} catch (err) {
|
|
216
|
-
if (err.code === "NotFoundError") return [];
|
|
217
|
-
throw err;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// src/http/pkt-line.ts
|
|
222
|
-
var FLUSH = new Uint8Array([
|
|
223
|
-
48,
|
|
224
|
-
48,
|
|
225
|
-
48,
|
|
226
|
-
48
|
|
227
|
-
]);
|
|
228
|
-
function pktLine(data) {
|
|
229
|
-
const body = encodeUtf8(data);
|
|
230
|
-
const len = (body.length + 4).toString(16).padStart(4, "0");
|
|
231
|
-
return concat(encodeUtf8(len), body);
|
|
232
|
-
}
|
|
233
|
-
function pktLineBuffer(body) {
|
|
234
|
-
const len = (body.length + 4).toString(16).padStart(4, "0");
|
|
235
|
-
return concat(encodeUtf8(len), body);
|
|
236
|
-
}
|
|
237
|
-
function parsePktLines(buf) {
|
|
238
|
-
const lines = [];
|
|
239
|
-
let pos = 0;
|
|
240
|
-
while (pos + 4 <= buf.length) {
|
|
241
|
-
const len = Number.parseInt(decodeAscii(buf.subarray(pos, pos + 4)), 16);
|
|
242
|
-
if (len === 0) {
|
|
243
|
-
lines.push(null);
|
|
244
|
-
pos += 4;
|
|
245
|
-
} else if (len >= 4) {
|
|
246
|
-
lines.push(decodeUtf8(buf.subarray(pos + 4, pos + len)));
|
|
247
|
-
pos += len;
|
|
248
|
-
} else {
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return lines;
|
|
253
|
-
}
|
|
254
|
-
var SIDE_BAND_MAX_CHUNK = 65515;
|
|
255
|
-
function sideBandPackfile(packData) {
|
|
256
|
-
const parts = [];
|
|
257
|
-
for (let offset = 0; offset < packData.length; offset += SIDE_BAND_MAX_CHUNK) {
|
|
258
|
-
const chunk = packData.subarray(offset, offset + SIDE_BAND_MAX_CHUNK);
|
|
259
|
-
parts.push(pktLineBuffer(concat(new Uint8Array([1]), chunk)));
|
|
260
|
-
}
|
|
261
|
-
parts.push(FLUSH);
|
|
262
|
-
return concat(...parts);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// src/http/types.ts
|
|
266
|
-
var runStep = (hooks, label, fn) => hooks?.step ? hooks.step(label, fn) : fn();
|
|
267
|
-
function rawFs(repo) {
|
|
268
|
-
const fs = repo.fs;
|
|
269
|
-
if (!fs?.promises) {
|
|
270
|
-
throw new TypeError(
|
|
271
|
-
"This operation needs a promise fs (`fs.promises`) \u2014 node:fs and createGitFs both provide one"
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
return fs.promises;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/http/info-refs.ts
|
|
278
|
-
async function listAllRefs(repo, defaultBranch = "main") {
|
|
279
|
-
const [branches, tagNames] = await Promise.all([
|
|
280
|
-
listBranches(repo),
|
|
281
|
-
listLooseRefNames(repo, "refs/tags").then(
|
|
282
|
-
(names) => names ?? import_isomorphic_git2.default.listTags(repo)
|
|
283
|
-
)
|
|
284
|
-
]);
|
|
285
|
-
const tagRefs = await Promise.all(
|
|
286
|
-
tagNames.map(async (tag) => {
|
|
287
|
-
const name = `refs/tags/${tag}`;
|
|
288
|
-
return resolveLooseRefFast(repo, name).then((oid) => ({ name, oid })).catch(() => null);
|
|
289
|
-
})
|
|
290
|
-
);
|
|
291
|
-
const branchRefs = branches.map((b) => ({
|
|
292
|
-
name: `refs/heads/${b.name}`,
|
|
293
|
-
oid: b.commit
|
|
294
|
-
}));
|
|
295
|
-
const defaultRef = branches.find((b) => b.isDefault);
|
|
296
|
-
const headSymref = defaultRef ? `refs/heads/${defaultRef.name}` : await Promise.resolve(import_isomorphic_git2.default.currentBranch({ ...repo, fullname: true })).then((cb) => cb ?? `refs/heads/${defaultBranch}`).catch(() => `refs/heads/${defaultBranch}`);
|
|
297
|
-
const headOid = branchRefs.find((r) => r?.name === headSymref)?.oid ?? await import_isomorphic_git2.default.resolveRef({ ...repo, ref: "HEAD" }).catch(() => null);
|
|
298
|
-
const refs = [];
|
|
299
|
-
if (headOid) refs.push({ name: "HEAD", oid: headOid });
|
|
300
|
-
for (const r of branchRefs) if (r) refs.push(r);
|
|
301
|
-
for (const r of tagRefs) if (r) refs.push(r);
|
|
302
|
-
return { refs, headSymref };
|
|
303
|
-
}
|
|
304
|
-
async function handleInfoRefs(repo, options, hooks) {
|
|
305
|
-
const { service } = options;
|
|
306
|
-
const agent = options.agent ?? "git-fs-s3";
|
|
307
|
-
const { refs, headSymref } = await runStep(
|
|
308
|
-
hooks,
|
|
309
|
-
"listAllRefs",
|
|
310
|
-
() => listAllRefs(repo, options.defaultBranch ?? "main")
|
|
311
|
-
);
|
|
312
|
-
const isUpload = service === "git-upload-pack";
|
|
313
|
-
const caps = isUpload ? `no-progress side-band-64k symref=HEAD:${headSymref} allow-tip-sha1-in-want allow-reachable-sha1-in-want agent=${agent}` : `delete-refs report-status no-done agent=${agent}`;
|
|
314
|
-
const parts = [pktLine(`# service=${service}
|
|
315
|
-
`), FLUSH];
|
|
316
|
-
if (refs.length === 0) {
|
|
317
|
-
parts.push(
|
|
318
|
-
pktLine(
|
|
319
|
-
`0000000000000000000000000000000000000000 capabilities^{}\0${caps}
|
|
320
|
-
`
|
|
321
|
-
)
|
|
322
|
-
);
|
|
323
|
-
} else {
|
|
324
|
-
let first = true;
|
|
325
|
-
for (const { name, oid } of refs) {
|
|
326
|
-
parts.push(
|
|
327
|
-
pktLine(first ? `${oid} ${name}\0${caps}
|
|
328
|
-
` : `${oid} ${name}
|
|
329
|
-
`)
|
|
330
|
-
);
|
|
331
|
-
first = false;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
parts.push(FLUSH);
|
|
335
|
-
return {
|
|
336
|
-
status: 200,
|
|
337
|
-
headers: {
|
|
338
|
-
"Content-Type": `application/x-${service}-advertisement`,
|
|
339
|
-
"Cache-Control": "no-cache"
|
|
340
|
-
},
|
|
341
|
-
body: concat(...parts)
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// src/http/reachability.ts
|
|
346
|
-
var import_isomorphic_git3 = __toESM(require("isomorphic-git"), 1);
|
|
347
|
-
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
348
|
-
var MISSING_OBJECT_RETRY_DELAY_MS = 200;
|
|
349
|
-
async function collectReachableOids(repo, startOids, hooks, haveOids, options) {
|
|
350
|
-
const verifyBlobs = options?.verifyBlobs ?? true;
|
|
351
|
-
const have = haveOids instanceof Set ? haveOids : new Set(haveOids ?? []);
|
|
352
|
-
const seen = /* @__PURE__ */ new Set();
|
|
353
|
-
let complete = true;
|
|
354
|
-
const promises = /* @__PURE__ */ new Map();
|
|
355
|
-
async function readAndVisitChildren(oid) {
|
|
356
|
-
const obj = await import_isomorphic_git3.default.readObject({ ...repo, oid });
|
|
357
|
-
seen.add(oid);
|
|
358
|
-
const children = [];
|
|
359
|
-
if (obj.type === "commit") {
|
|
360
|
-
const commit = obj.object;
|
|
361
|
-
children.push([commit.tree, "tree"]);
|
|
362
|
-
for (const parent of commit.parent) children.push([parent, "commit"]);
|
|
363
|
-
} else if (obj.type === "tree") {
|
|
364
|
-
const tree = obj.object;
|
|
365
|
-
for (const entry of tree) {
|
|
366
|
-
if (entry.type === "commit") continue;
|
|
367
|
-
children.push([entry.oid, entry.type]);
|
|
368
|
-
}
|
|
369
|
-
} else if (obj.type === "tag") {
|
|
370
|
-
children.push([obj.object.object, void 0]);
|
|
371
|
-
}
|
|
372
|
-
await Promise.all(children.map(([child, type]) => visit(child, type)));
|
|
373
|
-
}
|
|
374
|
-
function visit(oid, type) {
|
|
375
|
-
const existing = promises.get(oid);
|
|
376
|
-
if (existing) return existing;
|
|
377
|
-
if (have.has(oid) || type === "blob" && !verifyBlobs) {
|
|
378
|
-
seen.add(oid);
|
|
379
|
-
const p2 = Promise.resolve();
|
|
380
|
-
promises.set(oid, p2);
|
|
381
|
-
return p2;
|
|
382
|
-
}
|
|
383
|
-
const p = (async () => {
|
|
384
|
-
try {
|
|
385
|
-
await readAndVisitChildren(oid);
|
|
386
|
-
} catch {
|
|
387
|
-
try {
|
|
388
|
-
await delay(MISSING_OBJECT_RETRY_DELAY_MS);
|
|
389
|
-
await readAndVisitChildren(oid);
|
|
390
|
-
} catch (err) {
|
|
391
|
-
complete = false;
|
|
392
|
-
hooks?.onWarn?.(`missing object ${oid}`, err);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
})();
|
|
396
|
-
promises.set(oid, p);
|
|
397
|
-
return p;
|
|
398
|
-
}
|
|
399
|
-
await Promise.all(startOids.map((oid) => visit(oid)));
|
|
400
|
-
return { oids: Array.from(seen), complete };
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// src/http/receive-pack.ts
|
|
404
|
-
var import_isomorphic_git5 = __toESM(require("isomorphic-git"), 1);
|
|
405
|
-
|
|
406
|
-
// src/http/repack.ts
|
|
407
|
-
var import_isomorphic_git4 = __toESM(require("isomorphic-git"), 1);
|
|
408
|
-
|
|
409
|
-
// src/pack-files.ts
|
|
410
|
-
var PACK_FILE_KINDS = [
|
|
411
|
-
"pack",
|
|
412
|
-
"idx",
|
|
413
|
-
"keep",
|
|
414
|
-
"bitmap",
|
|
415
|
-
"rev"
|
|
416
|
-
];
|
|
417
|
-
var KNOWN_KINDS = new Set(PACK_FILE_KINDS);
|
|
418
|
-
function isPackFileKind(value) {
|
|
419
|
-
return KNOWN_KINDS.has(value);
|
|
420
|
-
}
|
|
421
|
-
function parsePackFileName(name) {
|
|
422
|
-
const dot = name.lastIndexOf(".");
|
|
423
|
-
if (dot <= 0) return null;
|
|
424
|
-
const kind = name.slice(dot + 1);
|
|
425
|
-
if (!isPackFileKind(kind)) return null;
|
|
426
|
-
return { base: name.slice(0, dot), kind, name };
|
|
427
|
-
}
|
|
428
|
-
function packFileName(base, kind) {
|
|
429
|
-
return `${base}.${kind}`;
|
|
430
|
-
}
|
|
431
|
-
function parsePackDirectory(entries) {
|
|
432
|
-
return entries.map(parsePackFileName).filter((file) => file !== null);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// src/http/pack-reuse.ts
|
|
436
|
-
var OBJ_OFS_DELTA = 6;
|
|
437
|
-
var OBJ_REF_DELTA = 7;
|
|
438
|
-
var OID_BYTES = 20;
|
|
439
|
-
var PACK_TRAILER_BYTES = 20;
|
|
440
|
-
var IDX_V2 = {
|
|
441
|
-
magic: 4285812579,
|
|
442
|
-
version: 2,
|
|
443
|
-
headerBytes: 8,
|
|
444
|
-
fanoutEntries: 256,
|
|
445
|
-
/** Offsets with this bit set index the 64-bit large-offset table. */
|
|
446
|
-
largeOffsetFlag: 2147483648
|
|
447
|
-
};
|
|
448
|
-
function readPackIndex(idx) {
|
|
449
|
-
const view = new DataView(idx.buffer, idx.byteOffset, idx.byteLength);
|
|
450
|
-
const namesStart = IDX_V2.headerBytes + IDX_V2.fanoutEntries * 4;
|
|
451
|
-
if (idx.byteLength < namesStart || view.getUint32(0) !== IDX_V2.magic || view.getUint32(4) !== IDX_V2.version) {
|
|
452
|
-
return null;
|
|
453
|
-
}
|
|
454
|
-
const count = view.getUint32(namesStart - 4);
|
|
455
|
-
const crcStart = namesStart + count * OID_BYTES;
|
|
456
|
-
const offsetsStart = crcStart + count * 4;
|
|
457
|
-
if (idx.byteLength < offsetsStart + count * 4) return null;
|
|
458
|
-
const entries = [];
|
|
459
|
-
for (let i = 0; i < count; i++) {
|
|
460
|
-
const offset = view.getUint32(offsetsStart + i * 4);
|
|
461
|
-
if (offset & IDX_V2.largeOffsetFlag) return null;
|
|
462
|
-
const at = namesStart + i * OID_BYTES;
|
|
463
|
-
entries.push({ oid: toHex(idx.subarray(at, at + OID_BYTES)), offset });
|
|
464
|
-
}
|
|
465
|
-
return entries;
|
|
466
|
-
}
|
|
467
|
-
function indexPack(idx, pack) {
|
|
468
|
-
const located = readPackIndex(idx);
|
|
469
|
-
if (!located) return null;
|
|
470
|
-
located.sort((a, b) => a.offset - b.offset);
|
|
471
|
-
const entries = /* @__PURE__ */ new Map();
|
|
472
|
-
const oidAtOffset = /* @__PURE__ */ new Map();
|
|
473
|
-
const packEnd = pack.byteLength - PACK_TRAILER_BYTES;
|
|
474
|
-
for (let i = 0; i < located.length; i++) {
|
|
475
|
-
const { oid, offset } = located[i];
|
|
476
|
-
const end = located[i + 1]?.offset ?? packEnd;
|
|
477
|
-
entries.set(oid, { pack, start: offset, end });
|
|
478
|
-
oidAtOffset.set(offset, oid);
|
|
479
|
-
}
|
|
480
|
-
return { entries, oidAtOffset };
|
|
481
|
-
}
|
|
482
|
-
function readEntryHeader(bytes, start) {
|
|
483
|
-
let byte = bytes[start];
|
|
484
|
-
const type = byte >> 4 & 7;
|
|
485
|
-
let size = byte & 15;
|
|
486
|
-
let shift = 4;
|
|
487
|
-
let at = start + 1;
|
|
488
|
-
while (byte & 128) {
|
|
489
|
-
byte = bytes[at++];
|
|
490
|
-
size += (byte & 127) * 2 ** shift;
|
|
491
|
-
shift += 7;
|
|
492
|
-
}
|
|
493
|
-
return { type, size, length: at - start };
|
|
494
|
-
}
|
|
495
|
-
function encodeEntryHeader(type, size) {
|
|
496
|
-
const bytes = [];
|
|
497
|
-
let rest = Math.floor(size / 16);
|
|
498
|
-
bytes.push((rest > 0 ? 128 : 0) | type << 4 | size % 16);
|
|
499
|
-
while (rest > 0) {
|
|
500
|
-
const low = rest % 128;
|
|
501
|
-
rest = Math.floor(rest / 128);
|
|
502
|
-
bytes.push((rest > 0 ? 128 : 0) | low);
|
|
503
|
-
}
|
|
504
|
-
return new Uint8Array(bytes);
|
|
505
|
-
}
|
|
506
|
-
function readOfsDeltaOffset(bytes, start) {
|
|
507
|
-
let at = start;
|
|
508
|
-
let byte = bytes[at++];
|
|
509
|
-
let distance = byte & 127;
|
|
510
|
-
while (byte & 128) {
|
|
511
|
-
byte = bytes[at++];
|
|
512
|
-
distance = (distance + 1) * 128 + (byte & 127);
|
|
513
|
-
}
|
|
514
|
-
return { distance, length: at - start };
|
|
515
|
-
}
|
|
516
|
-
function reusableEntry(entry, indexed) {
|
|
517
|
-
const { pack, start, end } = entry;
|
|
518
|
-
const header = readEntryHeader(pack, start);
|
|
519
|
-
const bodyStart = start + header.length;
|
|
520
|
-
if (header.type === OBJ_REF_DELTA) {
|
|
521
|
-
return {
|
|
522
|
-
bytes: pack.subarray(start, end),
|
|
523
|
-
baseOid: toHex(pack.subarray(bodyStart, bodyStart + OID_BYTES))
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
if (header.type !== OBJ_OFS_DELTA) {
|
|
527
|
-
return { bytes: pack.subarray(start, end), baseOid: null };
|
|
528
|
-
}
|
|
529
|
-
const { distance, length } = readOfsDeltaOffset(pack, bodyStart);
|
|
530
|
-
const baseOid = indexed.oidAtOffset.get(start - distance);
|
|
531
|
-
if (baseOid === void 0) return null;
|
|
532
|
-
return {
|
|
533
|
-
bytes: concat(
|
|
534
|
-
encodeEntryHeader(OBJ_REF_DELTA, header.size),
|
|
535
|
-
fromHex(baseOid),
|
|
536
|
-
pack.subarray(bodyStart + length, end)
|
|
537
|
-
),
|
|
538
|
-
baseOid
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
async function buildPackFromExistingPacks(repo, oids) {
|
|
542
|
-
const fsp = rawFs(repo);
|
|
543
|
-
const packDir = `${repo.gitdir}/objects/pack`;
|
|
544
|
-
const packs = parsePackDirectory(
|
|
545
|
-
await fsp.readdir(packDir).catch(() => [])
|
|
546
|
-
).filter((file) => file.kind === "pack");
|
|
547
|
-
const indexes = [];
|
|
548
|
-
for (const { base } of packs) {
|
|
549
|
-
const [pack, idx] = await Promise.all([
|
|
550
|
-
fsp.readFile(`${packDir}/${packFileName(base, "pack")}`),
|
|
551
|
-
fsp.readFile(`${packDir}/${packFileName(base, "idx")}`)
|
|
552
|
-
]).catch(() => [null, null]);
|
|
553
|
-
if (!(pack instanceof Uint8Array) || !(idx instanceof Uint8Array)) {
|
|
554
|
-
return null;
|
|
555
|
-
}
|
|
556
|
-
const indexed = indexPack(idx, pack);
|
|
557
|
-
if (!indexed) return null;
|
|
558
|
-
indexes.push(indexed);
|
|
559
|
-
}
|
|
560
|
-
const locate = (oid) => {
|
|
561
|
-
for (const indexed of indexes) {
|
|
562
|
-
const entry = indexed.entries.get(oid);
|
|
563
|
-
if (entry) return reusableEntry(entry, indexed);
|
|
564
|
-
}
|
|
565
|
-
return null;
|
|
566
|
-
};
|
|
567
|
-
const ordered = [];
|
|
568
|
-
const done = /* @__PURE__ */ new Set();
|
|
569
|
-
const pending = /* @__PURE__ */ new Set();
|
|
570
|
-
const visit = (oid) => {
|
|
571
|
-
if (done.has(oid)) return true;
|
|
572
|
-
if (pending.has(oid)) return false;
|
|
573
|
-
const entry = locate(oid);
|
|
574
|
-
if (!entry) return false;
|
|
575
|
-
pending.add(oid);
|
|
576
|
-
if (entry.baseOid !== null && !visit(entry.baseOid)) return false;
|
|
577
|
-
pending.delete(oid);
|
|
578
|
-
done.add(oid);
|
|
579
|
-
ordered.push(entry.bytes);
|
|
580
|
-
return true;
|
|
581
|
-
};
|
|
582
|
-
for (const oid of oids) {
|
|
583
|
-
if (!visit(oid)) return null;
|
|
584
|
-
}
|
|
585
|
-
const header = new Uint8Array(12);
|
|
586
|
-
header.set(encodeUtf8("PACK"), 0);
|
|
587
|
-
const view = new DataView(header.buffer);
|
|
588
|
-
view.setUint32(4, 2);
|
|
589
|
-
view.setUint32(8, ordered.length);
|
|
590
|
-
const body = concat(header, ...ordered);
|
|
591
|
-
return concat(body, fromHex(await sha1(body)));
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// src/http/repack.ts
|
|
595
|
-
var PACK_OBJECT_TYPE_BITS = {
|
|
596
|
-
commit: 16,
|
|
597
|
-
tree: 32,
|
|
598
|
-
blob: 48,
|
|
599
|
-
tag: 64
|
|
600
|
-
};
|
|
601
|
-
function encodePackObjectHeader(type, length) {
|
|
602
|
-
const bytes = [];
|
|
603
|
-
let more = length > 15;
|
|
604
|
-
bytes.push(
|
|
605
|
-
(more ? 128 : 0) | PACK_OBJECT_TYPE_BITS[type] | length & 15
|
|
606
|
-
);
|
|
607
|
-
length >>>= 4;
|
|
608
|
-
while (more) {
|
|
609
|
-
more = length > 127;
|
|
610
|
-
bytes.push((more ? 128 : 0) | length & 127);
|
|
611
|
-
length >>>= 7;
|
|
612
|
-
}
|
|
613
|
-
return new Uint8Array(bytes);
|
|
614
|
-
}
|
|
615
|
-
async function hashGitObject(type, content) {
|
|
616
|
-
const prefix = encodeUtf8(`${type} ${content.length}\0`);
|
|
617
|
-
return sha1(concat(prefix, content));
|
|
618
|
-
}
|
|
619
|
-
async function readAndVerifyObjects(repo, oids) {
|
|
620
|
-
const objects = /* @__PURE__ */ new Map();
|
|
621
|
-
const BATCH_SIZE = 100;
|
|
622
|
-
for (let i = 0; i < oids.length; i += BATCH_SIZE) {
|
|
623
|
-
const batch = oids.slice(i, i + BATCH_SIZE);
|
|
624
|
-
const entries = await Promise.all(
|
|
625
|
-
batch.map(async (oid) => {
|
|
626
|
-
const { type, object } = await import_isomorphic_git4.default.readObject({
|
|
627
|
-
...repo,
|
|
628
|
-
oid,
|
|
629
|
-
format: "content"
|
|
630
|
-
});
|
|
631
|
-
const content = object;
|
|
632
|
-
const actualOid = await hashGitObject(type, content);
|
|
633
|
-
if (actualOid !== oid) {
|
|
634
|
-
throw new Error(
|
|
635
|
-
`repack: object ${oid} failed independent SHA-1 verification (recomputed ${actualOid}) \u2014 refusing to trust this read, aborting repack`
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
return { oid, type, content };
|
|
639
|
-
})
|
|
640
|
-
);
|
|
641
|
-
for (const { oid, type, content } of entries) {
|
|
642
|
-
objects.set(oid, { type, content });
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
return objects;
|
|
646
|
-
}
|
|
647
|
-
async function buildVerifiedPack(oids, objects) {
|
|
648
|
-
const header = new Uint8Array(12);
|
|
649
|
-
const view = new DataView(header.buffer);
|
|
650
|
-
header.set(encodeUtf8("PACK"), 0);
|
|
651
|
-
view.setUint32(4, 2, false);
|
|
652
|
-
view.setUint32(8, oids.length, false);
|
|
653
|
-
const chunks = [header];
|
|
654
|
-
const BATCH_SIZE = 100;
|
|
655
|
-
for (let i = 0; i < oids.length; i += BATCH_SIZE) {
|
|
656
|
-
const batch = oids.slice(i, i + BATCH_SIZE);
|
|
657
|
-
const encoded = await Promise.all(
|
|
658
|
-
batch.map(async (oid) => {
|
|
659
|
-
const entry = objects.get(oid);
|
|
660
|
-
if (!entry) {
|
|
661
|
-
throw new Error(`repack: missing verified object for ${oid}`);
|
|
662
|
-
}
|
|
663
|
-
const objHeader = encodePackObjectHeader(
|
|
664
|
-
entry.type,
|
|
665
|
-
entry.content.length
|
|
666
|
-
);
|
|
667
|
-
const compressed = await deflate(entry.content);
|
|
668
|
-
return concat(objHeader, compressed);
|
|
669
|
-
})
|
|
670
|
-
);
|
|
671
|
-
chunks.push(...encoded);
|
|
672
|
-
}
|
|
673
|
-
const packBody = concat(...chunks);
|
|
674
|
-
const trailer = fromHex(await sha1(packBody));
|
|
675
|
-
return concat(packBody, trailer);
|
|
676
|
-
}
|
|
677
|
-
var REPACK_PACK_COUNT_THRESHOLD = 4;
|
|
678
|
-
async function readPackDirectory(repo) {
|
|
679
|
-
const entries = await rawFs(repo).readdir(`${repo.gitdir}/objects/pack`).catch(() => []);
|
|
680
|
-
return parsePackDirectory(entries);
|
|
681
|
-
}
|
|
682
|
-
async function countPacks(repo) {
|
|
683
|
-
const files = await readPackDirectory(repo);
|
|
684
|
-
return files.filter((file) => file.kind === "pack").length;
|
|
685
|
-
}
|
|
686
|
-
async function repackRepository(repo, options, hooks) {
|
|
687
|
-
const threshold = options?.threshold ?? REPACK_PACK_COUNT_THRESHOLD;
|
|
688
|
-
const fsp = rawFs(repo);
|
|
689
|
-
try {
|
|
690
|
-
if (await countPacks(repo) < threshold) {
|
|
691
|
-
return [];
|
|
692
|
-
}
|
|
693
|
-
const [branches, tags] = await Promise.all([
|
|
694
|
-
import_isomorphic_git4.default.listBranches(repo),
|
|
695
|
-
import_isomorphic_git4.default.listTags(repo)
|
|
696
|
-
]);
|
|
697
|
-
const refNames = [
|
|
698
|
-
...branches.map((b) => `refs/heads/${b}`),
|
|
699
|
-
...tags.map((t) => `refs/tags/${t}`)
|
|
700
|
-
];
|
|
701
|
-
const tipOids = (await Promise.all(
|
|
702
|
-
refNames.map(
|
|
703
|
-
(ref) => import_isomorphic_git4.default.resolveRef({ ...repo, ref }).catch(() => null)
|
|
704
|
-
)
|
|
705
|
-
)).filter((oid) => oid !== null);
|
|
706
|
-
if (tipOids.length === 0) return [];
|
|
707
|
-
const { oids: reachable, complete } = await collectReachableOids(
|
|
708
|
-
repo,
|
|
709
|
-
tipOids,
|
|
710
|
-
hooks
|
|
711
|
-
);
|
|
712
|
-
if (!complete || reachable.length === 0) return [];
|
|
713
|
-
const packDir = `${repo.gitdir}/objects/pack`;
|
|
714
|
-
const packBuffer = await buildPackFromExistingPacks(repo, reachable) ?? await buildVerifiedPack(
|
|
715
|
-
reachable,
|
|
716
|
-
await readAndVerifyObjects(repo, reachable)
|
|
717
|
-
);
|
|
718
|
-
await fsp.mkdir(packDir, { recursive: true });
|
|
719
|
-
const newBase = `pack-${Date.now()}`;
|
|
720
|
-
const newPackFile = packFileName(newBase, "pack");
|
|
721
|
-
await fsp.writeFile(`${packDir}/${newPackFile}`, packBuffer);
|
|
722
|
-
const { oids: indexedOids } = await import_isomorphic_git4.default.indexPack({
|
|
723
|
-
...repo,
|
|
724
|
-
dir: packDir,
|
|
725
|
-
filepath: newPackFile
|
|
726
|
-
});
|
|
727
|
-
const indexed = new Set(indexedOids);
|
|
728
|
-
if (reachable.some((oid) => !indexed.has(oid))) {
|
|
729
|
-
await Promise.all(
|
|
730
|
-
["pack", "idx"].map(
|
|
731
|
-
(kind) => fsp.unlink(`${packDir}/${packFileName(newBase, kind)}`).catch(() => {
|
|
732
|
-
})
|
|
733
|
-
)
|
|
734
|
-
);
|
|
735
|
-
throw new Error(
|
|
736
|
-
"repack: indexed pack is missing reachable objects \u2014 aborting"
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
const stale = (await readPackDirectory(repo)).filter(
|
|
740
|
-
(file) => file.base !== newBase
|
|
741
|
-
);
|
|
742
|
-
await Promise.all(
|
|
743
|
-
stale.map(
|
|
744
|
-
(file) => fsp.unlink(`${packDir}/${file.name}`).catch(() => {
|
|
745
|
-
})
|
|
746
|
-
)
|
|
747
|
-
);
|
|
748
|
-
return stale.map((file) => `objects/pack/${file.name}`);
|
|
749
|
-
} catch (err) {
|
|
750
|
-
hooks?.onWarn?.("repack failed (non-fatal)", err);
|
|
751
|
-
return [];
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// src/http/receive-pack.ts
|
|
756
|
-
var ZERO_OID = "0".repeat(40);
|
|
757
|
-
function parseReceivePackBody(body) {
|
|
758
|
-
const refUpdates = [];
|
|
759
|
-
let pos = 0;
|
|
760
|
-
while (pos + 4 <= body.length) {
|
|
761
|
-
const len = Number.parseInt(decodeAscii(body.subarray(pos, pos + 4)), 16);
|
|
762
|
-
if (len === 0) {
|
|
763
|
-
pos += 4;
|
|
764
|
-
break;
|
|
765
|
-
}
|
|
766
|
-
if (len < 4) break;
|
|
767
|
-
const line = decodeUtf8(body.subarray(pos + 4, pos + len)).replace(/\n$/, "").split("\0")[0];
|
|
768
|
-
pos += len;
|
|
769
|
-
const parts = (line ?? "").split(" ");
|
|
770
|
-
const [oldOid, newOid, refName] = parts;
|
|
771
|
-
if (oldOid && newOid && refName) {
|
|
772
|
-
refUpdates.push({ oldOid, newOid, refName });
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
return { refUpdates, packData: body.subarray(pos) };
|
|
776
|
-
}
|
|
777
|
-
async function ensureRepoInitialized(repo, defaultBranch = "main") {
|
|
778
|
-
const fsp = rawFs(repo);
|
|
779
|
-
try {
|
|
780
|
-
await fsp.stat(`${repo.gitdir}/HEAD`);
|
|
781
|
-
} catch {
|
|
782
|
-
await fsp.mkdir(repo.gitdir, { recursive: true }).catch(() => {
|
|
783
|
-
});
|
|
784
|
-
await import_isomorphic_git5.default.init({ ...repo, dir: repo.gitdir, defaultBranch, bare: true });
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
async function indexIncomingPack(repo, packData, hooks) {
|
|
788
|
-
if (packData.length < 4) return [];
|
|
789
|
-
const packName = await runStep(
|
|
790
|
-
hooks,
|
|
791
|
-
"write + indexPack incoming pack",
|
|
792
|
-
async () => {
|
|
793
|
-
const fsp = rawFs(repo);
|
|
794
|
-
const packDir = `${repo.gitdir}/objects/pack`;
|
|
795
|
-
await fsp.mkdir(packDir, { recursive: true });
|
|
796
|
-
const packName2 = `recv-${Date.now()}`;
|
|
797
|
-
await fsp.writeFile(`${packDir}/${packName2}.pack`, packData);
|
|
798
|
-
await import_isomorphic_git5.default.indexPack({
|
|
799
|
-
...repo,
|
|
800
|
-
dir: packDir,
|
|
801
|
-
filepath: `${packName2}.pack`
|
|
802
|
-
});
|
|
803
|
-
return packName2;
|
|
804
|
-
}
|
|
805
|
-
);
|
|
806
|
-
return [
|
|
807
|
-
`${repo.gitdir}/objects/pack/${packName}.pack`,
|
|
808
|
-
`${repo.gitdir}/objects/pack/${packName}.idx`
|
|
809
|
-
];
|
|
810
|
-
}
|
|
811
|
-
async function checkRefCas(repo, refName, oldOid) {
|
|
812
|
-
if (!isSafeFullRefName(refName)) {
|
|
813
|
-
return { refName, ok: false, reason: "invalid ref name" };
|
|
814
|
-
}
|
|
815
|
-
const currentOid = await import_isomorphic_git5.default.resolveRef({ ...repo, ref: refName }).catch(() => ZERO_OID);
|
|
816
|
-
return currentOid === oldOid ? null : {
|
|
817
|
-
refName,
|
|
818
|
-
ok: false,
|
|
819
|
-
reason: "non-fast-forward, ref updated by another push"
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
async function collectExistingRefTips(repo) {
|
|
823
|
-
const [branches, tags] = await Promise.all([
|
|
824
|
-
import_isomorphic_git5.default.listBranches(repo),
|
|
825
|
-
import_isomorphic_git5.default.listTags(repo)
|
|
826
|
-
]);
|
|
827
|
-
const refs = [
|
|
828
|
-
...branches.map((b) => `refs/heads/${b}`),
|
|
829
|
-
...tags.map((t) => `refs/tags/${t}`)
|
|
830
|
-
];
|
|
831
|
-
const oids = await Promise.all(
|
|
832
|
-
refs.map((ref) => import_isomorphic_git5.default.resolveRef({ ...repo, ref }).catch(() => null))
|
|
833
|
-
);
|
|
834
|
-
return new Set(oids.filter((oid) => oid !== null));
|
|
835
|
-
}
|
|
836
|
-
async function validateRefUpdates(repo, refUpdates) {
|
|
837
|
-
return Promise.all(
|
|
838
|
-
refUpdates.map(
|
|
839
|
-
async ({ oldOid, refName }) => await checkRefCas(repo, refName, oldOid) ?? { refName, ok: true }
|
|
840
|
-
)
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
async function applyRefUpdates(repo, refUpdates, hooks) {
|
|
844
|
-
return runStep(
|
|
845
|
-
hooks,
|
|
846
|
-
"apply ref updates",
|
|
847
|
-
() => Promise.all(
|
|
848
|
-
refUpdates.map(async ({ oldOid, newOid, refName }) => {
|
|
849
|
-
const failure = await checkRefCas(repo, refName, oldOid);
|
|
850
|
-
if (failure) return failure;
|
|
851
|
-
if (newOid === ZERO_OID) {
|
|
852
|
-
await import_isomorphic_git5.default.deleteRef({ ...repo, ref: refName }).catch(() => {
|
|
853
|
-
});
|
|
854
|
-
} else {
|
|
855
|
-
await import_isomorphic_git5.default.writeRef({
|
|
856
|
-
...repo,
|
|
857
|
-
ref: refName,
|
|
858
|
-
value: newOid,
|
|
859
|
-
force: true
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
return { refName, ok: true };
|
|
863
|
-
})
|
|
864
|
-
)
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
function receivePackResponse(results) {
|
|
868
|
-
const responseBody = concat(
|
|
869
|
-
pktLine("unpack ok\n"),
|
|
870
|
-
...results.map(
|
|
871
|
-
({ refName, ok, reason }) => pktLine(ok ? `ok ${refName}
|
|
872
|
-
` : `ng ${refName} ${reason}
|
|
873
|
-
`)
|
|
874
|
-
),
|
|
875
|
-
FLUSH
|
|
876
|
-
);
|
|
877
|
-
return {
|
|
878
|
-
status: 200,
|
|
879
|
-
headers: {
|
|
880
|
-
"Content-Type": "application/x-git-receive-pack-result",
|
|
881
|
-
"Cache-Control": "no-cache"
|
|
882
|
-
},
|
|
883
|
-
body: responseBody
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
async function applyReceivePack(repo, parsed, options, hooks) {
|
|
887
|
-
await ensureRepoInitialized(repo, options?.defaultBranch ?? "main");
|
|
888
|
-
const preflight = await validateRefUpdates(repo, parsed.refUpdates);
|
|
889
|
-
const acceptedUpdates = parsed.refUpdates.filter(
|
|
890
|
-
(_, index) => preflight[index]?.ok
|
|
891
|
-
);
|
|
892
|
-
const incomingPackPaths = await indexIncomingPack(
|
|
893
|
-
repo,
|
|
894
|
-
acceptedUpdates.some((update) => update.newOid !== ZERO_OID) ? parsed.packData : new Uint8Array(),
|
|
895
|
-
hooks
|
|
896
|
-
);
|
|
897
|
-
const newTips = acceptedUpdates.filter((update) => update.newOid !== ZERO_OID).map((update) => update.newOid);
|
|
898
|
-
if (newTips.length > 0) {
|
|
899
|
-
const haveOids = await collectExistingRefTips(repo);
|
|
900
|
-
for (const update of acceptedUpdates) {
|
|
901
|
-
if (update.oldOid !== ZERO_OID) haveOids.add(update.oldOid);
|
|
902
|
-
}
|
|
903
|
-
const { complete } = await collectReachableOids(
|
|
904
|
-
repo,
|
|
905
|
-
newTips,
|
|
906
|
-
hooks,
|
|
907
|
-
haveOids
|
|
908
|
-
);
|
|
909
|
-
if (!complete) {
|
|
910
|
-
await Promise.all(
|
|
911
|
-
incomingPackPaths.map(
|
|
912
|
-
(filepath) => rawFs(repo).unlink(filepath).catch(() => {
|
|
913
|
-
})
|
|
914
|
-
)
|
|
915
|
-
);
|
|
916
|
-
return {
|
|
917
|
-
results: preflight.map(
|
|
918
|
-
(result) => result.ok ? { ...result, ok: false, reason: "incomplete object graph" } : result
|
|
919
|
-
),
|
|
920
|
-
stalePackPaths: []
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
const applied = await applyRefUpdates(repo, acceptedUpdates, hooks);
|
|
925
|
-
const results = preflight.map(
|
|
926
|
-
(result, index) => result.ok ? applied.find(
|
|
927
|
-
(entry) => entry.refName === parsed.refUpdates[index]?.refName
|
|
928
|
-
) ?? result : result
|
|
929
|
-
);
|
|
930
|
-
const repackOptions = options?.repack;
|
|
931
|
-
const stalePackPaths = repackOptions === false || !results.some((result) => result.ok) ? [] : await runStep(
|
|
932
|
-
hooks,
|
|
933
|
-
"repack",
|
|
934
|
-
() => repackRepository(repo, repackOptions, hooks)
|
|
935
|
-
);
|
|
936
|
-
return { results, stalePackPaths };
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// src/http/upload-pack.ts
|
|
940
|
-
var import_isomorphic_git6 = __toESM(require("isomorphic-git"), 1);
|
|
941
|
-
var ZERO_OID2 = "0".repeat(40);
|
|
942
|
-
async function handleUploadPack(repo, body, options, hooks) {
|
|
943
|
-
const lines = parsePktLines(body);
|
|
944
|
-
const wants = [];
|
|
945
|
-
const haves = [];
|
|
946
|
-
let done = false;
|
|
947
|
-
for (const line of lines) {
|
|
948
|
-
if (!line) continue;
|
|
949
|
-
if (line.startsWith("want ")) {
|
|
950
|
-
wants.push(line.slice(5, 45));
|
|
951
|
-
}
|
|
952
|
-
if (line.startsWith("have ")) {
|
|
953
|
-
const sha = line.slice(5, 45);
|
|
954
|
-
if (sha !== ZERO_OID2) {
|
|
955
|
-
haves.push(sha);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
if (line.startsWith("done")) {
|
|
959
|
-
done = true;
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
if (wants.length === 0) {
|
|
963
|
-
return {
|
|
964
|
-
status: 200,
|
|
965
|
-
headers: { "Content-Type": "application/x-git-upload-pack-result" },
|
|
966
|
-
body: concat(pktLine("NAK\n"))
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
if (haves.length > 0 && !done) {
|
|
970
|
-
return {
|
|
971
|
-
status: 200,
|
|
972
|
-
headers: { "Content-Type": "application/x-git-upload-pack-result" },
|
|
973
|
-
body: concat(pktLine("NAK\n"))
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
if (haves.length === 0) {
|
|
977
|
-
const packDirPath = `${repo.gitdir}/objects/pack`;
|
|
978
|
-
const entries = await runStep(
|
|
979
|
-
hooks,
|
|
980
|
-
"readdir objects/pack",
|
|
981
|
-
() => rawFs(repo).readdir(packDirPath).catch(() => [])
|
|
982
|
-
);
|
|
983
|
-
const packNames = parsePackDirectory(entries).filter((file) => file.kind === "pack").map((file) => file.name);
|
|
984
|
-
const packName = packNames[0];
|
|
985
|
-
if (packNames.length === 1 && packName !== void 0) {
|
|
986
|
-
const packData = await runStep(
|
|
987
|
-
hooks,
|
|
988
|
-
"read consolidated pack (fast path)",
|
|
989
|
-
() => rawFs(repo).readFile(`${packDirPath}/${packName}`)
|
|
990
|
-
);
|
|
991
|
-
const bytes = packData instanceof Uint8Array ? packData : new TextEncoder().encode(packData);
|
|
992
|
-
return {
|
|
993
|
-
status: 200,
|
|
994
|
-
headers: {
|
|
995
|
-
"Content-Type": "application/x-git-upload-pack-result",
|
|
996
|
-
"Cache-Control": "no-cache"
|
|
997
|
-
},
|
|
998
|
-
body: concat(pktLine("NAK\n"), sideBandPackfile(bytes))
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
if (options?.beforeWalk) {
|
|
1003
|
-
await runStep(hooks, "beforeWalk", options.beforeWalk);
|
|
1004
|
-
}
|
|
1005
|
-
let haveSet;
|
|
1006
|
-
if (haves.length > 0) {
|
|
1007
|
-
const { oids: haveOids } = await runStep(
|
|
1008
|
-
hooks,
|
|
1009
|
-
"collectReachableOids(haves)",
|
|
1010
|
-
() => collectReachableOids(repo, haves, hooks, void 0, {
|
|
1011
|
-
verifyBlobs: false
|
|
1012
|
-
})
|
|
1013
|
-
);
|
|
1014
|
-
haveSet = new Set(haveOids);
|
|
1015
|
-
}
|
|
1016
|
-
const { oids: wantOids } = await runStep(
|
|
1017
|
-
hooks,
|
|
1018
|
-
"collectReachableOids(wants)",
|
|
1019
|
-
() => collectReachableOids(repo, wants, hooks, haveSet, {
|
|
1020
|
-
verifyBlobs: false
|
|
1021
|
-
})
|
|
1022
|
-
);
|
|
1023
|
-
const oids = haveSet ? wantOids.filter((oid) => !haveSet.has(oid)) : wantOids;
|
|
1024
|
-
const reused = await runStep(
|
|
1025
|
-
hooks,
|
|
1026
|
-
`reuse pack entries (${oids.length} oids)`,
|
|
1027
|
-
() => buildPackFromExistingPacks(repo, oids)
|
|
1028
|
-
);
|
|
1029
|
-
const packBytes = reused ?? await runStep(hooks, `packObjects (${oids.length} oids)`, async () => {
|
|
1030
|
-
const { packfile } = await import_isomorphic_git6.default.packObjects({ ...repo, oids });
|
|
1031
|
-
return packfile instanceof Uint8Array ? packfile : new Uint8Array(packfile ?? []);
|
|
1032
|
-
});
|
|
1033
|
-
return {
|
|
1034
|
-
status: 200,
|
|
1035
|
-
headers: {
|
|
1036
|
-
"Content-Type": "application/x-git-upload-pack-result",
|
|
1037
|
-
"Cache-Control": "no-cache"
|
|
1038
|
-
},
|
|
1039
|
-
body: concat(pktLine("NAK\n"), sideBandPackfile(packBytes))
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1043
|
-
0 && (module.exports = {
|
|
1044
|
-
FLUSH,
|
|
1045
|
-
REPACK_PACK_COUNT_THRESHOLD,
|
|
1046
|
-
applyReceivePack,
|
|
1047
|
-
applyRefUpdates,
|
|
1048
|
-
collectReachableOids,
|
|
1049
|
-
ensureRepoInitialized,
|
|
1050
|
-
handleInfoRefs,
|
|
1051
|
-
handleUploadPack,
|
|
1052
|
-
indexIncomingPack,
|
|
1053
|
-
listAllRefs,
|
|
1054
|
-
parsePktLines,
|
|
1055
|
-
parseReceivePackBody,
|
|
1056
|
-
pktLine,
|
|
1057
|
-
pktLineBuffer,
|
|
1058
|
-
rawFs,
|
|
1059
|
-
receivePackResponse,
|
|
1060
|
-
repackRepository,
|
|
1061
|
-
sideBandPackfile
|
|
1062
|
-
});
|
|
1063
|
-
//# sourceMappingURL=http.cjs.map
|