OXYGENHUB

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
· 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

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("&#60;&#60;&#60;&#60;&#60;&#60;&#60; 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