Skip to content

fix(ui): keep what is loaded on screen while it reloads - #48

Merged
LeadcodeDev merged 1 commit into
mainfrom
fix/history-flicker-on-reload
Aug 29, 2026
Merged

fix(ui): keep what is loaded on screen while it reloads#48
LeadcodeDev merged 1 commit into
mainfrom
fix/history-flicker-on-reload

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Collaborator

Deleting a local branch made the history blank out twice, about a third of a second apart.

Why it blanks

reload_head, reload_references and reload_history each assigned LoadState::Loading before spawning their read, discarding the value the views were drawing. HistoryTableDelegate::recompute_visible returns early on anything but Ready, so rows_count drops to zero and the table renders nothing until the walk lands. The sidebar loses its branch list the same way.

So any reload flickers — a commit made in a terminal, a fetch, a scope change. Deleting a branch is just where it shows up twice.

Why twice

Nothing deduplicates the two reloads a deletion causes:

  1. delete_local_branch reloads the moment git branch -D returns (workspace.rs:619).
  2. 200–350 ms later FSEvents reports refs/heads/<branch> gone, classify maps it to Aspect::References, and the watch loop reloads again.

The gap is FSEvents latency + the 200 ms debounce window + up to one 100 ms mailbox poll — long enough for the two blanks to read as separate flashes.

I confirmed the echo rather than assuming it: watching a repository through FsRepositoryWatcher across a git branch -D reports Aspect::References every time.

The change

LoadState::start_reload moves to Loading only when there is nothing worth keeping. A first load and a retry after a failure still show the loading state; a re-read leaves the previous value on screen until apply_* replaces it.

One change, three call sites, and it fixes the flicker for every reload path rather than just deletion.

It also fixes a smaller artifact in the same family: apply_history reads self.references.ready(), so when references was still Loading it built an empty refs_by_commit and every badge vanished for a frame.

What I deliberately did not do

The redundant second reload stays. It is invisible now, and suppressing gitr's own echo would need the watcher to tell gitr's writes from a terminal's — domain::watch leaves that to the caller by design, and nothing in the tree does it. Worth its own change if the duplicate walk ever costs measurably on a large history.

Testing

Four unit tests on the state transition, written before the code and seen to fail. cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --all --check all pass.

Not yet confirmed in the running app. gitr is single-instance and the installed binary held the socket, so I never got the fixed build in front of a human eye. The reasoning above is from reading the code plus the watcher probe — the visual result is unverified.

Deleting a local branch blanked the history twice. Each blank is one reload:
`reload_head`, `reload_references` and `reload_history` each assigned
`LoadState::Loading` before spawning their read, discarding the value the views
were drawing. `HistoryTableDelegate::recompute_visible` returns early on
anything but `Ready`, so `rows_count` drops to zero and the table renders
nothing until the walk lands. The sidebar loses its branch list the same way.

There are two reloads because nothing deduplicates them. `delete_local_branch`
reloads the moment `git branch -D` returns, and 200-350 ms later FSEvents
reports the removal of `refs/heads/<branch>` — classified `Aspect::References`
— and the watch loop reloads again. The gap is FSEvents latency plus the 200 ms
debounce window plus up to one 100 ms mailbox poll, which is long enough for the
two blanks to read as two flashes rather than one. That the echo is real was
checked by watching a repository through `FsRepositoryWatcher` across a
`git branch -D`: it reports `Aspect::References` every time.

`start_reload` moves to `Loading` only when there is nothing worth keeping, so a
first load and a retry after a failure still show the loading state, while a
re-read leaves the previous value up until `apply_*` replaces it. That also
stops `apply_history` from seeing `references` as `Loading` and building an
empty `refs_by_commit`, which wiped every badge for a frame.

The second reload stays. It is invisible now, and suppressing gitr's own echo
would need the watcher to tell gitr's writes from a terminal's, which nothing in
the tree does — `domain::watch` leaves that to the caller by design.
@LeadcodeDev LeadcodeDev self-assigned this Aug 29, 2026
@LeadcodeDev LeadcodeDev added the bug Something isn't working label Aug 29, 2026
@LeadcodeDev
LeadcodeDev merged commit a962404 into main Aug 29, 2026
3 checks passed
@LeadcodeDev
LeadcodeDev deleted the fix/history-flicker-on-reload branch August 29, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant