diff --git a/src/components/PatchActivityItem.svelte b/src/components/PatchActivityItem.svelte
index 47a134590..a49057c1f 100644
--- a/src/components/PatchActivityItem.svelte
+++ b/src/components/PatchActivityItem.svelte
@@ -257,6 +257,11 @@
.merge-badge :global(*) {
color: inherit;
}
+ .merge-target {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ }
.copy-link {
display: inline-flex;
align-items: center;
@@ -572,7 +577,11 @@
merged patch
{#if targetBranch}
- into {targetBranch}
+ into
+
+
+ {targetBranch}
+
{/if}
diff --git a/src/components/PatchMetadata.svelte b/src/components/PatchMetadata.svelte
index 0e2a5c6a7..9c6676f70 100644
--- a/src/components/PatchMetadata.svelte
+++ b/src/components/PatchMetadata.svelte
@@ -6,6 +6,7 @@
import type { Config } from "@bindings/config/Config";
import type { Stats } from "@bindings/diff/Stats";
import type { RepoInfo } from "@bindings/repo/RepoInfo";
+ import type { RepoRefs } from "@bindings/repo/RepoRefs";
import debounce from "lodash/debounce";
@@ -19,12 +20,14 @@
summaryTitle,
} from "@app/lib/reviewSummary";
import * as roles from "@app/lib/roles";
- import { push } from "@app/lib/router";
+ import { push, routeToPath } from "@app/lib/router";
import {
authorForNodeId,
+ defaultBranch,
formatOid,
pluralize,
publicKeyFromDid,
+ unqualifyBranch,
verdictIcon,
} from "@app/lib/utils";
@@ -149,6 +152,58 @@
await loadPatch();
}
}
+
+ const targetBranch = $derived(
+ patch.targetBranch === undefined
+ ? undefined
+ : unqualifyBranch(patch.targetBranch),
+ );
+ const targetBranchCaption = $derived(
+ `This patch merges into ${targetBranch}`,
+ );
+
+ // A patch's target is not validated when it is opened, so it can name a
+ // branch the source view cannot resolve. The default branch is canonical by
+ // definition; any other target needs the canonical ref list, which is
+ // expensive enough to load after render. An unresolved target renders
+ // unlinked — a missing link beats one that errors.
+ const targetIsDefaultBranch = $derived(
+ targetBranch !== undefined && targetBranch === defaultBranch(repo),
+ );
+
+ let canonicalBranches: Record | undefined = $state();
+ let canonicalBranchesRid: string | undefined;
+
+ $effect(() => {
+ if (targetBranch === undefined || targetIsDefaultBranch) {
+ return;
+ }
+ const requested = repo.rid;
+ if (canonicalBranchesRid === requested) {
+ return;
+ }
+ canonicalBranchesRid = requested;
+ canonicalBranches = undefined;
+ void invoke("list_repo_refs", { rid: requested })
+ .then(refs => {
+ if (canonicalBranchesRid === requested) {
+ canonicalBranches = refs.canonical.branches;
+ }
+ })
+ .catch(error => {
+ if (canonicalBranchesRid === requested) {
+ canonicalBranchesRid = undefined;
+ }
+ console.error("Failed to load repo refs", error);
+ });
+ });
+
+ const targetBranchBrowsable = $derived(
+ targetIsDefaultBranch ||
+ (targetBranch !== undefined &&
+ canonicalBranches !== undefined &&
+ targetBranch in canonicalBranches),
+ );