neiam /gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Packages
⭐
Log in to mark this repository.
Make cross-reference backlinks clickable
d2fd801 · Gabriel Morell · 2026-09-10 16:04
Message
{commit_body(@commit)}
Files changed
modified
lib/git_gud_web/components/event_components.ex
+28
−2
@@ -251,12 +251,20 @@ defmodule GitGudWeb.EventComponents do
| 251 | 251 | kind: if(data["from_type"] == "issue", do: "issue", else: "pull request"), |
| 252 | 252 | repo: data["from_repo"], |
| 253 | 253 | number: data["from_number"], |
| 254 | − title: data["from_title"] | |
| 254 | + title: data["from_title"], | |
| 255 | + path: cross_ref_path(data) | |
| 255 | 256 | } |
| 256 | 257 | |
| 257 | 258 | ~H""" |
| 258 | 259 | referenced this from {@kind} |
| 259 | − <span class="font-mono text-xs">{@repo}#{@number}</span><span :if={@title}> — {@title}</span>. | |
| 260 | + <.link :if={@path} navigate={@path} class="link link-hover"> | |
| 261 | + <span class="font-mono text-xs">{@repo}#{@number}</span><span :if={@title}> — {@title}</span> | |
| 262 | + </.link> | |
| 263 | + <span :if={is_nil(@path)}> | |
| 264 | + <span class="font-mono text-xs">{@repo}#{@number}</span><span :if={ | |
| 265 | + @title | |
| 266 | + }> — {@title}</span> | |
| 267 | + </span>. | |
| 260 | 268 | """ |
| 261 | 269 | end |
| 262 | 270 |
@@ -272,6 +280,24 @@ defmodule GitGudWeb.EventComponents do
| 272 | 280 | defp describe(%{kind: "unmoderated"}), do: "restored this." |
| 273 | 281 | defp describe(%{kind: kind}), do: kind |
| 274 | 282 | |
| 283 | + # `from_repo` is stored as "handle/name"; verified routes need the two | |
| 284 | + # segments apart. Neither a handle nor a repo name may contain a | |
| 285 | + # slash, so a single split is unambiguous. | |
| 286 | + defp cross_ref_path(%{"from_repo" => repo, "from_number" => number, "from_type" => type}) | |
| 287 | + when is_binary(repo) and is_integer(number) do | |
| 288 | + case String.split(repo, "/") do | |
| 289 | + [handle, name] when handle != "" and name != "" -> | |
| 290 | + if type == "issue", | |
| 291 | + do: ~p"/r/#{handle}/#{name}/issues/#{number}", | |
| 292 | + else: ~p"/r/#{handle}/#{name}/pulls/#{number}" | |
| 293 | + | |
| 294 | + _ -> | |
| 295 | + nil | |
| 296 | + end | |
| 297 | + end | |
| 298 | + | |
| 299 | + defp cross_ref_path(_data), do: nil | |
| 300 | + | |
| 275 | 301 | # `data` holds hex strings — the sha columns are binary, but an event |
| 276 | 302 | # payload is JSON, so callers encode before recording. |
| 277 | 303 | defp short(hex) when is_binary(hex) and byte_size(hex) >= 7, do: binary_part(hex, 0, 7) |
modified
test/git_gud_web/live/cross_reference_test.exs
+25
−0
@@ -96,6 +96,9 @@ defmodule GitGudWeb.CrossReferenceTest do
| 96 | 96 | assert html =~ "referenced this from issue" |
| 97 | 97 | assert html =~ "Mentions it" |
| 98 | 98 | assert html =~ "##{source.number}" |
| 99 | + | |
| 100 | + # And it links back to the source, not just naming it. | |
| 101 | + assert html =~ ~s(href="/r/#{handle(repo)}/#{repo.name}/issues/#{source.number}") | |
| 99 | 102 | end |
| 100 | 103 | |
| 101 | 104 | test "a reference already present isn't re-announced on re-save", %{conn: conn} do |
@@ -172,6 +175,28 @@ defmodule GitGudWeb.CrossReferenceTest do
| 172 | 175 | assert Events.list_for(target) == [] |
| 173 | 176 | end |
| 174 | 177 | |
| 178 | + test "a backlink from a PR links to the PR", %{conn: conn} do | |
| 179 | + {user, repo} = repository_fixture() | |
| 180 | + target = issue_fixture(repo, user) | |
| 181 | + pr = pr_with_branches(repo, user, %{"title" => "Source PR", "body" => ""}) | |
| 182 | + | |
| 183 | + {:ok, lv, _html} = | |
| 184 | + live(log_in_user(conn, user), ~p"/r/#{handle(repo)}/#{repo.name}/pulls/#{pr.number}") | |
| 185 | + | |
| 186 | + _ = lv |> element("button[phx-click=edit_body]") |> render_click() | |
| 187 | + | |
| 188 | + _ = | |
| 189 | + lv | |
| 190 | + |> form("form[phx-submit=save_body]", %{body: "closes ##{target.number}"}) | |
| 191 | + |> render_submit() | |
| 192 | + | |
| 193 | + {:ok, _hist, html} = | |
| 194 | + live(log_in_user(conn, user), issue_path(repo, target) <> "/history") | |
| 195 | + | |
| 196 | + assert html =~ "referenced this from pull request" | |
| 197 | + assert html =~ ~s(href="/r/#{handle(repo)}/#{repo.name}/pulls/#{pr.number}") | |
| 198 | + end | |
| 199 | + | |
| 175 | 200 | test "a PR referenced from an issue is backlinked too", %{conn: conn} do |
| 176 | 201 | {user, repo} = repository_fixture() |
| 177 | 202 | pr = pr_with_branches(repo, user, %{"title" => "The PR"}) |
Parents: 8d95805