import { useQueryClient } from '@tanstack/react-query'; import { CircleCheckIcon, GitMergeIcon, TriangleAlertIcon } from 'lucide-react'; import { useState } from 'react'; import { Button } from '#/components/atoms/button.jsx'; import { Link } from '#/components/atoms/link.jsx'; import { PathList } from '#/components/molecules/path-list.jsx'; import { checkLook, summarize } from '#/lib/checks.js'; import { account, loadRepos } from '#/lib/git.js'; import { mergePull } from '#/lib/merge.js'; import { useSession } from '#/lib/session.jsx'; /** * Whether the pull request can merge, and the merge itself. * * The answer is the server's three-way merge of the branch and main against * their base, made when the list was read: clean, or the paths where the two * changed the same thing differently. Everybody reads the answer. The owner * of the repository gets the buttons, because merging writes the canonical * branch and nobody else may. */ export function MergeBox({ repo, pull, checks, checksHref }) { const { session } = useSession(); const queryClient = useQueryClient(); const [busy, setBusy] = useState(''); const [error, setError] = useState(''); const [merged, setMerged] = useState(''); if (pull.merged) return null; const mine = session?.did === account.did; const conflicts = pull.conflicts ?? []; const merge = async (mode) => { setBusy(mode); setError(''); try { const answer = await mergePull(session, repo, pull, mode); setMerged(answer.sha); // Main moved. The repository record the whole app reads from names the // old tip, so it is read again before the reads that hang off it are. // Then every read is marked stale: the pull request is merged, its // commits are on main, and main's history, tree and list have changed. await loadRepos(); await queryClient.invalidateQueries(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(''); } }; return (
{(() => { const summary = summarize(checks ?? []); if (!summary) return null; const look = checkLook(summary.status); return (
{summary.passed} of {summary.total}{' '} {summary.total === 1 ? 'check' : 'checks'} passed {checksHref && ( Details )}
); })()} {merged ? (

Merged as {merged.slice(0, 8)}.

) : conflicts.length > 0 ? (

This branch has conflicts that must be resolved.

{mine && (

Merge main into the branch, or rebase it, and push again.

)}
) : !pull.rooted ? (

This branch shares no history with main, so there is nothing to merge it onto.

) : pull.conflictsUnknown ? (

This branch changes many of the files main has changed. Merging reads them all and stops if any conflict.

) : (

This branch has no conflicts with main. {pull.behind > 0 && ( Main has moved {pull.behind}{' '} {pull.behind === 1 ? 'commit' : 'commits'} since it branched. )}

)} {mine && !merged && conflicts.length === 0 && pull.rooted && (
{error && ( {error} )}
)}
); }