Skip to content
File

Blob: src/worker/git/operations/read/refs.ts

typescript49 lines
1import type { CacheContext } from "@/worker/cache";
2import type { HeadInfo, Ref } from "../types";
3import { getRepoStub, createLogger } from "@/worker/common";
4import { getLimiter } from "@/worker/git/operations/limits";
5 
6// `cacheCtx` is optional so legacy callers without a request context still
7// work, but route handlers should always thread it so the DO RPC shares the
8// per-request subrequest limiter.
9export async function getHeadAndRefs(
10 env: Env,
11 repoId: string,
12 cacheCtx?: CacheContext
13): Promise<{ head: HeadInfo | undefined; refs: Ref[] }> {
14 const stub = getRepoStub(env, repoId);
15 const logger = createLogger(env.LOG_LEVEL, { service: "getHeadAndRefs", repoId });
16 try {
17 const limiter = getLimiter(cacheCtx);
18 return await limiter.run("do:get-head-and-refs", () => stub.getHeadAndRefs());
19 } catch (e) {
20 logger.debug("getHeadAndRefs:error", { error: String(e) });
21 return { head: undefined, refs: [] };
22 }
23}
24 
25export async function resolveRef(
26 env: Env,
27 repoId: string,
28 refOrOid: string,
29 cacheCtx?: CacheContext
30): Promise<string | undefined> {
31 if (/^[0-9a-f]{40}$/i.test(refOrOid)) return refOrOid.toLowerCase();
32 const { head, refs } = await getHeadAndRefs(env, repoId, cacheCtx);
33 if (refOrOid === "HEAD" && head?.target) {
34 const r = refs.find((x) => x.name === head.target);
35 return r?.oid;
36 }
37 if (refOrOid.startsWith("refs/")) {
38 const r = refs.find((x) => x.name === refOrOid);
39 return r?.oid;
40 }
41 // Try branches first, then tags
42 const candidates = [`refs/heads/${refOrOid}`, `refs/tags/${refOrOid}`];
43 for (const name of candidates) {
44 const r = refs.find((x) => x.name === name);
45 if (r) return r.oid;
46 }
47 return undefined;
48}