Skip to content

fix: show a detached HEAD clearly, offer the way back, and report the real Linux package version - #86

Merged
noahbclarkson merged 3 commits into
mainfrom
fix/issue-85-linux
Sep 17, 2026
Merged

noahbclarkson merged 3 commits into
mainfrom
fix/issue-85-linux

Conversation

@noahbclarkson

@noahbclarkson noahbclarkson commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes every point in #85 (Manjaro, KDE Wayland, 0.4.1 AppImage):

Reported Cause Fix
AppImage shows version 0.1.0 No X-AppImage-Version in the desktop entry, and the metainfo only listed 0.1.0 Version stamped into the AppImage; metainfo lists every release; a test keeps it current
Shortcuts work once, then need a click Closing an overlay left focus on an element that was no longer drawn Already fixed on main by #84; this PR extends the same hand-back
Hard to tell which commit HEAD is detached at Only a faint tint marked it, and the selection painted over that Distinct HEAD badge and marker, a detached-HEAD banner, commit id in the title bar
No easy way back from a detached HEAD Double-click always detached, and nothing offered a way back Double-click checks out the commit's branch; "Return to <branch>" in the banner and the palette

Closes #85.

Changes

Packaging (cec4542)

  • script/bundle-linux.sh writes X-AppImage-Version into the AppDir's desktop entries and passes VERSION to appimagetool. AppManager reads this key before anything else (installer.vala).
  • com.rgitui.app.metainfo.xml lists every tagged release, newest first. This is also where the .deb and software centres get the version.
  • New crates/rgitui/tests/linux_metadata.rs fails if the newest metainfo release isn't the crate version, a CHANGELOG.md release is missing or dated differently, or releases are out of order.

Detached HEAD (6bb1718)

  • Double-click on a commit: checks out the local branch that points at it. If that branch is already checked out, it only shows a toast. It detaches only when no local branch points there (rgitui_git::commit_checkout, unit tested). Tags and remote-only branches still detach. The context menu's "Checkout commit" is unchanged.
  • Detached-HEAD banner (main checkout only; not while inspecting a worktree or mid-rebase/bisect):
    • "HEAD is detached at abc1234", plus the commit summary and "Commits made here belong to no branch".
    • Show in Graph unhides the graph, selects HEAD and focuses it.
    • Return to <branch>, or Switch Branch… when there's no branch to return to.
  • Git: Switch to Previous Branch (palette, rebindable, unbound by default) works like git switch -.
    • It walks the HEAD reflog's checkout: moving from X to Y entries, which git and libgit2 both write.
    • Detached hops and deleted branches are skipped (tested).
    • The branch is computed during the background refresh, not on the UI thread.
  • Graph:
    • The HEAD badge reads → HEAD / → HEAD (detached).
    • The HEAD row keeps its marker bar while selected.
    • A detached HEAD row is tinted in the theme's warning colour instead of accent blue.
  • Title and status bars: show the short commit id instead of "detached". The title-bar tooltip says HEAD is detached, and clicking the pill reveals HEAD in the graph.
  • Bug fix: head_branch was shorthand(), which returns the literal "HEAD" when detached. The title bar showed a branch called HEAD and Rename Branch offered to rename it. The refresh now reports no branch when detached, as worktree rows already did.
  • CHANGELOG [Unreleased] entries for feat(ui): let the commit graph be hidden so the diff can fill the column #83, fix: hand focus back when overlays close, and raise the Windows stack #84 and this PR. Keymap docs regenerated with RGITUI_BLESS=1.

Testing

  • Tested on Windows
  • Tested on macOS
  • Tested on Linux
  • All existing tests pass (cargo test --workspace: 1754 passed, 0 failed; 17 new)
  • Code passes clippy (cargo clippy --workspace --all-targets -- -D warnings, and with --features rgitui/perf)
  • Code is formatted (cargo fmt --all -- --check)

New tests:

  • previous_branch: detaching, hopping between detached commits, switching between branches, a deleted branch, never switched, and a detached refresh reporting no head_branch.
  • commit_checkout: no local branch, a local branch, the checked-out branch preferred.
  • head_display_name, and the three metainfo checks.

Not run in the app: the desktop was in use, so the banner, badge styling and double-click behaviour haven't been exercised in a running window. The AppImage path is covered by the script change and the metadata test, not by building an AppImage.

Review fixes

Codex review, round 1 (c8a258a): two P2 findings, both valid and fixed.

  • Switch to Previous Branch stayed hidden after a detach. The palette filters against a cached command context, and nothing rebuilt it when a refresh landed. The repository-change handler now rebuilds it for the active tab. The stash, changes and remote predicates had the same staleness and are fixed too.
  • Show in Graph did nothing when HEAD wasn't loaded. This happened when "My Commits" hid another author's commit, or when a detached checkout sat beyond the loaded pages. The reveal now uses HEAD's oid from the project. If HEAD isn't loaded, it lifts "My Commits" and keeps loading pages until HEAD arrives, which always ends because the history walk includes a detached HEAD.
  • Selection jumping back after a reveal. A pending scroll target is now cleared once it's found. Before, on repositories larger than one page, each later refresh jumped the selection back to it.
  • 3 new unit tests for the pending-scroll decision. cargo test --workspace: 1757 passed, 0 failed. Clippy -D warnings and fmt check are clean.

🤖 Generated with Claude Code

noahbclarkson and others added 2 commits September 16, 2026 22:34
The 0.4.1 AppImage showed as 0.1.0 in AppImage managers (#85).
AppManager takes the version from `X-AppImage-Version` in the desktop
entry, then from the first `<release>` in the AppStream metainfo. The
AppImage had no `X-AppImage-Version`, since `bundle-linux.sh` never
passed `VERSION` to appimagetool, and the metainfo listed only 0.1.0.
The `.deb` ships the same metainfo, so software centres read 0.1.0
from it too.

- `bundle-linux.sh` writes `X-AppImage-Version` into both desktop
  entries in the AppDir, and passes `VERSION` to appimagetool, which
  embeds the same value rather than guessing one.
- The metainfo lists every tagged release, newest first, with its
  date and release page.
- `tests/linux_metadata.rs` fails when the newest metainfo release is
  not the crate version, when a CHANGELOG.md release is missing or
  dated differently, or when releases are out of order. A release bump
  that forgets the metainfo now fails CI, not the shipped package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses the remaining points of #85. Shortcuts dying after the
palette closed was fixed by #84.

Double-clicking a commit always detached HEAD there, including the
commit the current branch was on. Nothing then said where HEAD was
beyond a faint row tint, which the selection also covered, and getting
back meant finding the branch in the sidebar.

- Double-click now checks out the local branch pointing at the commit,
  does nothing if that branch is already checked out, and detaches only
  when no local branch names the commit (`rgitui_git::commit_checkout`,
  unit tested). "Checkout commit" in the context menu still detaches.
- While the main checkout is detached, a banner names the commit, with
  "Show in Graph" and "Return to <branch>", or "Switch Branch..." when
  there is no branch to return to. It is not shown for an inspected
  worktree (the inspection banner covers it) or during a rebase or
  bisect.
- "Git: Switch to Previous Branch" does the same from the palette,
  like `git switch -`. The branch is read from the HEAD reflog, whose
  "checkout: moving from X to Y" entries are written by both git and
  libgit2. Detached hops and branches since deleted are skipped, so
  stepping from commit to commit still returns to the starting branch.
- In the graph, the HEAD badge reads "-> HEAD" or "-> HEAD (detached)",
  and the HEAD row keeps its marker bar while selected. A detached HEAD
  is tinted in the warning colour, as in the title and status bars,
  instead of a blue that read as a selection.
- The title and status bars show the short commit id when detached,
  instead of "detached"; clicking the title pill shows HEAD in the
  graph.
- `head_branch` came from `shorthand()`, which is the literal "HEAD"
  when detached, so the title bar named a branch called HEAD and
  Rename Branch offered to rename it. The refresh now reports no branch
  when detached, matching what worktree rows already did.

CHANGELOG entries cover #83, #84 and this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-16T10:38:29.007668Z 6bb1718 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bb171818e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

has_token,
)
.with_multi_commit_selection(tab.graph.read(cx).selected_commit_count() > 1)
.with_previous_branch(proj.previous_branch().is_some())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh previous-branch availability after repository changes

When this context is initially built with has_previous_branch == false (for example, in a repository with no earlier checkout), detaching HEAD later populates GitProject::previous_branch, but the RepositoryChanged subscription never calls update_command_context. Because this command is unbound and the palette filters against the cached context, “Git: Switch to Previous Branch” remains hidden until an unrelated selection, tab, or settings change rebuilds the context. Update the command context when refreshed repository state is applied.

Useful? React with 👍 / 👎.

Comment thread crates/rgitui_graph/src/lib.rs Outdated
Comment on lines +978 to +981
if let Some(head) = self
.commits
.iter()
.find(|commit| commit.refs.contains(&RefLabel::Head))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reveal HEAD even when it is outside the loaded commit list

If the detached commit is absent from self.commits—for example, an active “My Commits” filter excludes a commit authored by someone else, or an old detached commit falls beyond the refreshed history window—this method silently does nothing, so both new “Show in Graph” entry points fail. Pass the project's HEAD OID to scroll_to_commit (and account for an excluding author filter) rather than trying to discover HEAD only from the currently visible commits.

Useful? React with 👍 / 👎.

…er it is

Review findings on #86.

- "Git: Switch to Previous Branch" is filtered by a command context
  the palette caches, and nothing rebuilt that context when a refresh
  landed. Detaching HEAD in a repository with no earlier checkout set
  the previous branch, but the command stayed hidden until an unrelated
  selection, tab or settings change happened to rebuild it. The same
  staleness applied to the stash, changes and remote predicates. The
  repository-change handler now rebuilds the context when the event
  comes from the active tab's project.
- "Show in Graph" and the detached title pill looked for HEAD among the
  commits the graph had loaded and did nothing when it was not there.
  Two ways that happens: "My Commits" drops HEAD when someone else
  authored it, and a detached checkout of an old commit sits beyond the
  loaded pages. The workspace now passes HEAD's oid from the project.
  When HEAD is not loaded and "My Commits" is on, the filter is lifted,
  since it may be what hides HEAD. The reveal then keeps loading pages
  until HEAD arrives. That always terminates, because the history walk
  includes a detached HEAD explicitly. Other reveals (tags, reflog,
  worktrees) still load one page, since their commit may be unreachable
  and paging would run to the end of history.
- A pending scroll target now stops waiting once it is found. It was
  only cleared when every commit had loaded, so on a repository larger
  than one page, each later refresh pulled the selection back to it. A
  new reveal also replaces one still waiting.

The pending-scroll decision is a pure function with unit tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahbclarkson
noahbclarkson merged commit ead56a9 into main Sep 17, 2026
4 checks passed
noahbclarkson added a commit that referenced this pull request Oct 7, 2026
Changelog entry for everything since 0.4.1: #78, #79, #80, #82, #83, #84,
#86, #87 and the model-picker follow-up pushed to main. The version is bumped
across the workspace, and the AppStream metainfo lists 0.5.0, which the Linux
metadata test requires before the crate version can move.

The AI work (#79, #80, #82) had no changelog entries; it now has them, with
each fix checked against the 0.4.1 code so that only bugs that shipped are
listed as fixed. The model-picker fixes in #82 are folded into the feature
for that reason: the picker did not exist in 0.4.1.

The drafts already under [Unreleased] are checked against the code and
corrected where they were off: binary, symlink, submodule and modify/delete
conflicts are the whole-file cases (add/add is resolved per region), Stage
All skips conflicted files rather than refusing, double-clicking the commit
the current branch is on shows a toast, and the diff viewer grows until the
graph is down to 120px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Linux issues

1 participant