Blame of a conflicted file no longer panics 8a2dbcb56246
The file page read its blame rows by line number, and a conflicted file
renders conflict markers, lines no change wrote: the page had more lines
than rows and indexed past the end (index out of bounds at repo.rs:207,
seen in the log on Earth). A line without a row gets an empty blame
cell. Reproduced in tests/edits.rs with a conflict landed on main.
closes #120
Author Andy McCall @andymccall <[email protected] >
When 25 days ago · 2026-10-10 02:58 UTC
Signature ✔ signed by Andy McCall
Hash 8a2dbcb56246f568de32a5360d2f8be66bb9704909ce1b5cebb1f3051863f33b
Depends on 145306044896 6ae41081ccd3 d23464c0ac60
Depended on by 13b27cb2200c
In bookmarks api-unknown-fields cancel-landed-runs clear-waiting close-proposal-140 diffusion-pin-bookmark draft-ready image-check job-waits main oh-comment-body-file proposal-list-fast run-one-job wiki-says-so
In tags v0.4.1 v0.4.2 v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits 4 atoms, 0 tree operations, 2 files
Browse the tree at this change
crates/hub/tests/edits.rs templates/file.html
modified
crates/hub/tests/edits.rs
+5 −0
…
244 unchanged lines
245
245
assert!(conflicts.contains("resolve in the browser"), "{conflicts}");
246
246
let page = send(app, "GET", "/andy/demo/edit/main/a.txt", Some(&andy), None).await;
247
247
assert!(page.body.contains("Resolve a.txt") && page.body.contains("<<<<<<< oxy conflict"), "{}", page.body);
248
+
// Blame of a conflicted file: its markers are lines no change wrote,
249
+
// so the page has more lines than blame rows, and still renders.
250
+
let blamed = send(app, "GET", "/andy/demo/file/main/a.txt?blame=1", Some(&andy), None).await;
251
+
assert_eq!(blamed.status, 200, "{}", blamed.body);
252
+
assert!(blamed.body.contains("oxy conflict") && blamed.body.contains("class=\"blame\"></td>"), "{}", blamed.body);
248
253
// Leaving markers in is refused; removing them resolves.
249
254
let r = send(
250
255
app,
…
44 unchanged lines
modified
templates/file.html
+2 −2
…
49 unchanged lines
50
50
{% for line in lines %}
51
51
{% let n = loop.index %}
52
52
<tr id="L{{ n }}">
53
-
{% if let Some(b) = blame %}{% let r = b[loop .index0] %}
53
+
{% if let Some(b) = blame %}{% if let Some( r) = b.get(n - 1) %}
54
54
{% if r.first %}<td class="blame age-{{ r.age }}" rowspan="{{ r.run }}"><a class="px" href="{{ repo.url }}/change/{{ r.hash }}" title="{{ r.hash }}">{{ r.short }}</a> <span class="who">{{ r.author }}</span> <time class="mute" title="{{ r.when_ms|when }}">{{ r.when_ms|ago }}</time>{% if let Some(b) = r.before %} <a class="before" href="{{ repo.url }}/file/{{ b }}/{{ f.path }}?blame=1#L{{ n }}" title="Blame the state before this change">↶</a>{% endif %}</td>{% endif %}
55
-
{% endif %}
55
+
{% else %}<td class="blame"></td>{# a line no change wrote, such as a conflict marker #}{% endif %}{% endif %}
56
56
<td class="n">{{ n }}</td>
57
57
<td class="t"><pre>{{ line|safe }}</pre></td>
58
58
</tr>
…
8 unchanged lines