Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128import type { Did } from "@atcute/lexicons/syntax";import type { GitRepoInfo } from "#lib/components/repo/types.ts";import { createBobbinClient } from "#lib/api/client.ts";import { branches as listBranches, tags as listTags, gitTarget, type GitServiceConfig} from "#lib/api/gitclient.ts";import { commitAtRef, getMergeBase, listCommits, toMirrorCommitSummary} from "#lib/api/knotmirror.ts";import { sortBranchesByRecency } from "#lib/api/pullCompose.ts";import { branchSummaries, toTagSummary, type BranchSummary, type CommitSummary, type TagSummary} from "#lib/api/repo.ts";import type { Fetch } from "#lib/fetch.ts";
const REF_LIMIT = 100;const COMMIT_LIMIT = 100;
// the spec is `base...head` as on github. a git ref cannot contain `..`, thus the split is not ambiguousexport const parseCompareSpec = (spec: string): { base: string; head: string } | null => { const at = spec.indexOf("..."); if (at < 0) return null; return { base: spec.slice(0, at), head: spec.slice(at + 3) };};
export const comparePath = (repoPath: string, base: string, head: string): string => base && head ? `${repoPath}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}` : `${repoPath}/compare?${new URLSearchParams({ ...(base && { base }), ...(head && { head }) })}`;
export interface CompareData { branches: BranchSummary[]; tags: TagSummary[]; base: string; head: string; baseHead: string; headHead: string; mergeBase: string; commits: CommitSummary[]; error: string;}
export const loadCompare = async (args: { config: GitServiceConfig; repo: GitRepoInfo; base: string; head: string; fetch: Fetch;}): Promise<CompareData> => { const { config, repo, fetch: fetchFn } = args; const repoDid = repo.repoDid as Did; const ctx = createBobbinClient({ serviceUrl: config.bobbinUrl, fetch: fetchFn }); const git = gitTarget(config, repo, fetchFn);
const [branchList, tagList] = await Promise.all([ listBranches(git, REF_LIMIT) .then(branchSummaries) .catch(() => [] as BranchSummary[]), listTags(git, REF_LIMIT) .then((page) => page.tags.map(toTagSummary)) .catch(() => [] as TagSummary[]) ]);
const base = args.base || repo.git.defaultBranch; const head = args.head || sortBranchesByRecency(branchList).find((branch) => branch.name !== base)?.name || "";
// a listed ref resolves without a request. other refs, for example a commit hash, go to the mirror const resolveRef = async (ref: string): Promise<string> => { if (!ref) return ""; const listed = branchList.find((branch) => branch.name === ref)?.hash ?? tagList.find((tag) => tag.name === ref)?.commitHash; if (listed) return listed; return (await commitAtRef(ctx, repoDid, ref))?.oid ?? ""; };
let baseHead = ""; let headHead = ""; let mergeBase = ""; let commits: CommitSummary[] = []; let error = ""; try { [baseHead, headHead] = await Promise.all([resolveRef(base), resolveRef(head)]); if (base && !baseHead) error = `Could not find ${base} in this repository.`; else if (head && !headHead) error = `Could not find ${head} in this repository.`; else if (baseHead && headHead && baseHead !== headHead) { const [page, merge] = await Promise.all([ listCommits(ctx, { repos: [repoDid], ranges: [`${baseHead}..${headHead}`], limit: COMMIT_LIMIT, includeStats: true }), getMergeBase(ctx, { repos: [repoDid], base: baseHead, head: headHead }) ]); commits = page.commits.map(toMirrorCommitSummary); mergeBase = merge.commit; } } catch (cause) { error = cause instanceof Error ? cause.message : String(cause); }
return { branches: branchList, tags: tagList, base, head, baseHead, headHead, mergeBase, commits, error };};