Skip to content
File

Blob: src/worker/git/pack/rewrite/selectionRetained.ts

typescript103 lines
1import type { OrderedPackSnapshot } from "@/worker/git/operations/fetch/types";
2import type { Logger } from "@/worker/common/logger";
3 
4import { clonePackHeader, type DuplicateHeaderCache } from "./ownership";
5import { pruneUnsafeDeadSlotRedirects } from "./selectionCompact";
6import { resolveDeltaBaseFromHeader } from "./selectionResolve";
7import {
8 ensurePackReadState,
9 readSelectedHeader,
10 selectionKey,
11 type PackReadState,
12 type RewriteOptions,
13 type SelectionTable,
14} from "./shared";
15 
16export function collectRetainedRedirectsNeedingBaseResolution(
17 table: SelectionTable,
18 deadSlots: Map<number, number>
19): number[] {
20 const safeDeadSlots = pruneUnsafeDeadSlotRedirects(table, deadSlots);
21 const retained: number[] = [];
22 
23 for (const [deadSel] of deadSlots) {
24 // `safeDeadSlots` is the subset we are still allowed to remove. When a row
25 // is present in `deadSlots` but absent here, dead-slot pruning decided that
26 // the redirect would manufacture a cycle, so this "dead" row is actually
27 // staying live in the output pack.
28 if (safeDeadSlots.has(deadSel)) continue;
29 const typeCode = table.typeCodes[deadSel];
30 if ((typeCode === 6 || typeCode === 7) && table.baseSlots[deadSel] < 0) {
31 retained.push(deadSel);
32 }
33 }
34 
35 return retained;
36}
37 
38export async function resolveRetainedRedirectBase(
39 table: SelectionTable,
40 sel: number,
41 snapshot: OrderedPackSnapshot,
42 readerStates: Map<number, PackReadState>,
43 dedupMap: Map<number, number>,
44 duplicateHeaderCache: DuplicateHeaderCache,
45 secondaryQueue: number[],
46 env: Env,
47 log: Logger,
48 warnedFlags: Set<string>,
49 options?: RewriteOptions
50): Promise<boolean> {
51 if (table.baseSlots[sel] >= 0) return true;
52 
53 const packSlot = table.packSlots[sel];
54 const pack = snapshot.packs[packSlot];
55 const cacheKey = selectionKey(packSlot, table.entryIndices[sel]);
56 let header = duplicateHeaderCache.get(cacheKey);
57 
58 if (header === undefined) {
59 // The common path already memoized this header before issuing the redirect.
60 // Falling back to a reread is correct here, but it should stay rare and
61 // bounded to the retained-redirect edge case.
62 const readState = await ensurePackReadState(
63 env,
64 pack,
65 packSlot,
66 readerStates,
67 log,
68 warnedFlags,
69 options
70 );
71 header = await readSelectedHeader(readState, table.offsets[sel]);
72 duplicateHeaderCache.set(cacheKey, header ? clonePackHeader(header) : null);
73 }
74 
75 if (!header) {
76 log.warn("rewrite:header-read-failed", { packKey: pack.packKey, offset: table.offsets[sel] });
77 return false;
78 }
79 
80 log.debug("rewrite:retained-redirect-base-resolve", {
81 sel,
82 packKey: pack.packKey,
83 entryIndex: table.entryIndices[sel],
84 typeCode: table.typeCodes[sel],
85 });
86 
87 // This intentionally resolves only the retained row's own base edge. Any
88 // newly discovered base rows still flow through the normal secondary queue so
89 // they get the same header-read and duplicate-owner handling as the primary
90 // selection pass.
91 return await resolveDeltaBaseFromHeader(
92 table,
93 sel,
94 snapshot,
95 dedupMap,
96 secondaryQueue,
97 env,
98 log,
99 header,
100 options
101 );
102}