mirror your GitHub repos to tangled.org automatically
1import { lineToString } from './pkt-line'
2
3const ZERO_SHA = '0000000000000000000000000000000000000000'
4
5export interface Advertisement {
6 /** Ref name -> object SHA (unpeeled). For annotated tags this is the tag object. */
7 refs: Map<string, string>
8 /** For annotated tags, the `<tag>^{}` peeled line: ref name -> commit SHA. */
9 peeled: Map<string, string>
10 capabilities: Set<string>
11}
12
13/**
14 * Parse a git ref advertisement (protocol v0) from a list of pkt-line
15 * payloads (flush-pkts already stripped by the reader).
16 *
17 * Handles three shapes that occur in practice:
18 * - smart-HTTP prelude: a leading `# service=git-upload-pack` line, which
19 * the ssh transport omits;
20 * - a populated repo: `<sha> <refname>\0<caps>` on the first ref line,
21 * `<sha> <refname>` thereafter, with `<sha> <refname>^{}` peeled lines
22 * for annotated tags;
23 * - an empty repo: a single `<zero-sha> capabilities^{}\0<caps>` line that
24 * carries capabilities but advertises no usable ref.
25 */
26export function parseAdvertisement(lines: Buffer[]): Advertisement {
27 const refs = new Map<string, string>()
28 const peeled = new Map<string, string>()
29 const capabilities = new Set<string>()
30
31 let first = true
32 for (const raw of lines) {
33 const line = lineToString(raw)
34 if (line.startsWith('# service=')) continue
35
36 let sha: string
37 let rest: string
38 if (first) {
39 const nul = line.indexOf('\0')
40 const head = nul === -1 ? line : line.slice(0, nul)
41 const caps = nul === -1 ? '' : line.slice(nul + 1)
42 for (const cap of caps.split(' ')) {
43 if (cap) capabilities.add(cap)
44 }
45 first = false
46 const sp = head.indexOf(' ')
47 sha = head.slice(0, sp)
48 rest = head.slice(sp + 1)
49 // Empty-repo sentinel: zero SHA + the literal "capabilities^{}" name.
50 if (sha === ZERO_SHA && rest === 'capabilities^{}') continue
51 }
52 else {
53 const sp = line.indexOf(' ')
54 sha = line.slice(0, sp)
55 rest = line.slice(sp + 1)
56 }
57
58 if (rest.endsWith('^{}')) {
59 peeled.set(rest.slice(0, -3), sha)
60 }
61 else {
62 refs.set(rest, sha)
63 }
64 }
65
66 return { refs, peeled, capabilities }
67}
68
69export { ZERO_SHA }