Skip to content

Fix the history list selection fighting the sidebar branch - #579

Merged
hannesa2 merged 5 commits into
gitx:masterfrom
goneng:fix-branch-focus-on-selection
Aug 21, 2026
Merged

hannesa2 merged 5 commits into
gitx:masterfrom
goneng:fix-branch-focus-on-selection

Conversation

@goneng

@goneng goneng commented Aug 20, 2026 •

Copy link
Copy Markdown
Collaborator

Selecting a branch used to fight with selecting a commit: the history-list would drag its selection back to the sidebar's branch on any update, and the sidebar would not follow a branch clicked in the history-list.

What did not work

Making a branch active from the history-list

Symptom

  • Select branch_B in the sidebar - the history-list focuses on that branch's tip-commit (Figure 1)
  • Click (once) the green label of branch_C in the history-list - the history-list focuses on that line (Figure 2)
    (the sidebar does not update-back - it is still focused on branch_B)
  • Double-click the green branch_C label in the history-list - branch_C is checked out correctly,
    but the history-list jumps back to branch_B's commit - not the one just clicked (Figure 3)

Figure 1:
Figure-1

Figure 2:
Figure-2

Figure 3:
Figure-3

Cause

The checkout makes the list update, and every update re-asserted the tip of whatever the sidebar had selected, discarding the commit the user picked.

Clicking a branch label

Symptom

Single-clicking a green (or blue) branch-label selected the row, but the sidebar stayed on the previous branch, so the two views disagreed about which branch was being looked at.

Behavior after the fix

  • Double-clicking branch_C's label checks it out and the list stays on the commit that carries the label.
  • In the history-list, single-clicking any branch-label (green or blue) moves the sidebar to that branch.
  • Clicking a commit in the history-list and then letting anything refresh the list -
    a fetch, a git switch in a terminal, the repository watcher -
    keeps that commit selected instead of snapping to a branch tip.
  • Clicking a branch in the sidebar still takes you to its tip in the history-list,
    including a second click on the branch already selected,
    which is now the deliberate way to get back there.

The commits

commit what
Point the sidebar property at the controller that exists Pre-existing: sidebarViewController has returned nil since 2018
Cover the window controller wiring with a test Guards the seam above
Keep the history-list selection unless a branch is picked The jump in scenario 1
Put back a selection that reading the list in again dropped Reading the list in replaces the commit objects, dropping the selection
Focus the sidebar on a branch label clicked in the history-list Scenario 2

The first one is worth a look on its own:
60d23480 (2018) added the sidebarViewController property and converted working code onto it,
but named the ivar _sidebarController while the property synthesizes _sidebarViewController.
Two ivars, one of them never assigned.
Nothing crashed because messaging nil is silent, and the one consumer had a fallback - so
-selectedRef has been quietly taking its history-list branch even when the sidebar had focus.
That is restored here, which is a behavior change beyond the reported bug and deliberate.

Test plan

  • 31 unit tests pass, up from 27 on master. New cases cover the reselect decision, the restore after a rebuild, and that a branch change still outranks the restore.
  • Each new test was checked by reverting the behavior it guards and confirming it fails:
    • dropping the outstanding-change guard
    • removing the restore
    • restoring the 2018 ivar wiring
  • Every commit in the series builds on its own.
  • By hand against branch_A/B/C:
    • both scenarios above
    • a second click on the already-selected sidebar branch
    • git switch in a terminal while a commit is selected,
    • an ordinary click on a history row.

Two things to know

  • PBGitWindowWiringTests loads the real window nib and creates a repository under NSTemporaryDirectory(), so it is the first test here that touches the filesystem and needs a window server - the nib does not survive a repository with nothing in it.
    (CI has since run it on macos-26: both cases pass, at a cost of 3.8s of the suite's 5.9s, which is the price of loading a real nib and shelling out to git on a shared runner)
  • GitXTests now links ObjectiveGit.framework, which it needs to construct a GTOID.

The window controller creates its sidebar into `_sidebarController`, but
the `sidebarViewController` property is backed by `_sidebarViewController`,
which nothing ever assigns, so the property always returned `nil`.

- Rename the ivar to the name the property synthesizes
- This revives `-selectedRef`, whose sidebar branch could never be taken
@goneng
goneng force-pushed the fix-branch-focus-on-selection branch from fbe9161 to d143c29 Compare August 20, 2026 22:26
@goneng

goneng commented Aug 20, 2026 •

Copy link
Copy Markdown
Collaborator Author

@hannesa2, Two things worth your attention before looking at the code:

  1. A pre-existing bug that this fix depends on:
    PBGitWindowController.sidebarViewController has returned nil since 60d23480 (2018):
    the sidebar is created into _sidebarController, but the property synthesizes _sidebarViewController,
    which nothing assigns.
    Messaging nil is silent, so it never surfaced.
    Fixing it revives -selectedRef's sidebar branch, which has been unreachable for eight years -
    so ref actions taken with the sidebar focused change behavior.
    That is intended, but I prefer you take a look at the UI behavior before we merge this one.

  2. Two changes to the test target:
    GitXTests now links ObjectiveGit.framework (needed to build a GTOID), and PBGitWindowWiringTests loads
    the real window nib against a repository created under NSTemporaryDirectory().
    That makes it the first test here that touches the filesystem and needs a window server.
    It passes on macos-26 in CI and costs 3.8s of the suite's 5.9s.
    Happy to move it to a separate target if you would rather keep the unit suite pure.

Every commit builds on its own, so the series bisects.
Screenshots of the original failing behavior are in the description.

Thanks!

@hannesa2

Copy link
Copy Markdown
Contributor

I'm neither a ObjectiveC developer. I just keep the repo alive.
Your AI generated commit are fine for me, as long as it works. That's why I ask for more test (if possible)

That is intended, but I prefer you take a look at the UI behavior before we merge this one.

Please can you do this. Or do we have a chance to automate this ?

I'm in the mode, that I merge stuff which sounds at least reasonable. But I'll fast to probably revert them too, when it makes issues.

To sum up: tests are the key !

@goneng
goneng marked this pull request as ready for review August 21, 2026 11:18
@goneng

goneng commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

@hannesa2, I'm 100% with you on that. This usability-bug was indeed harder to crack, and took about a day to identify, test properly (manual+automated) and resolve.
I'm pretty confident the UI behavior will be more consistent now - especially around the commit-history-list and the branches-sidebar.
(my next goal would be to cleanup most of the 1300 warnings we have during the build, as most of them are trivial and repeated)

goneng added 4 commits August 21, 2026 15:21
Assert that the window hands out the three view controllers it builds,
which is the seam the sidebar property was silently failing at, and
that the ref a ref action acts on follows whichever list has focus.

- Load the real window nib and let `-windowDidLoad` build the controllers,
  rather than assembling them the way the other suites do
- Check the sidebar handed out is the installed one, not a second
  instance
- Assert a focused sidebar names its branch, and a REMOTES row the whole
  remote, which is what fetch and pull are offered on
- Assert the history list still names the single branch on the selected
  commit, and nothing when two branches share it
- Back it with a repository created under `NSTemporaryDirectory`, since
  the nib does not survive a repository with nothing in it
Move the history-list to a branch tip only while a sidebar branch change
is outstanding, instead of on every update the list happens to get.

- Track the outstanding branch change, and clear it once the tip has
  actually been shown rather than on the first attempt
- Send the list back to the branch tip whenever the sidebar is clicked,
  including a second click on the branch already selected
- Report from -selectCommit: whether the requested commit was found
- Add PBGitHistorySelectionTests over the reselect decision
Restore the commit the user picked once the history-list has been read
in, instead of leaving the list with nothing selected.

- Remember the OID behind the selection as it changes
- Re-select it after an update that found nothing owed to the sidebar,
  which is what a checkout leaves behind
- Cover both the restore and its precedence against a branch change
Follow a click on a ref label in the history-list with the matching
sidebar branch, so that the two views agree on which branch is being
looked at.

- Move the outline selection rather than writing to `currentBranch`, which
  the sidebar undoes as it preserves its own selection over the reload
- Pull the ref hit-testing out of the double-click handler, so both
  clicks read the label under the mouse the same way
@hannesa2

Copy link
Copy Markdown
Contributor

@goneng
goneng force-pushed the fix-branch-focus-on-selection branch from d143c29 to 4b99faf Compare August 21, 2026 12:24
@goneng

goneng commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

Please can you do this. Or do we have a chance to automate this ?

Added tests to cover the sidebar-focused arm of -selectedRef that was unreachable before this PR
(the fix itself stayed the same - no code-changes around it).

@goneng

goneng commented Aug 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Hmm I can't reproduce this issue on 1.5.5

I've fix the first scenario to make it more obvious -

  • Select branch_B in the sidebar - the history-list focuses on that branch's tip-commit (Figure 1)
  • Click (once) the green label of branch_C in the history-list - the history-list focuses on that line (Figure 2)
    (the sidebar does not update-back - it is still focused on branch_B)
  • Double-click the green branch_C label in the history-list - branch_C is checked out correctly,
    but the history-list jumps back to branch_B's commit - not the one just clicked (Figure 3)

(basically, when clicking a green (or blue) tag in the history-list, the focus on the sidebar does not update to reflect that, so any later "refresh" of the history-list "stomps" the (out-dated) status of the sidebar to the history-list)

@hannesa2
hannesa2 merged commit d2b7c78 into gitx:master Aug 21, 2026
3 checks passed
@goneng
goneng deleted the fix-branch-focus-on-selection branch August 21, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants