TurtleGit for Mac

View Markdown source ↗

Reference browser parity

The October 18 comparison and native ownership follow-up covers Shift, source default commands, sheet release, tracking handoff and refreshed action catalogs. Its grouped validation is recorded separately.

Captured native context commands — October 16, 2026

The native list and namespace menus now retain the leaves, row order and last-selected reference used to build each menu. Selecting another row before invoking an existing menu entry preserves that entry's original target without replacing the user's visible selection. This applies to Log, Browse, Reflog, working-tree comparison, pair/unified comparison, both history ranges, Copy, owned child dialogs, inline Rename, tracking and reference deletion. Dynamically generated remote-folder entries retain their remote payload and original icons.

Captured commands are scoped to their model and catalog/view epoch. Refresh, folder/filter/search-field/nesting/sort changes, child-modal transitions and rename transitions retire old entries, including changes followed by restoration. Busy, confirmation and closed-owner states reject dispatch. Synchronous dispatch provides captured inputs to existing controllers; asynchronous operations retain their own reference/hash/cancellation inputs. No transient selection is published. Delete-all keeps upstream's explicit selection of every displayed tag, and obtains expected hashes for those displayed references rather than a previous selection.

The source reference is BrowseRefsDlg.cpp::ShowContextMenu at the pinned commit: it dispatches against the collected selectedLeafs, uses the captured remote-menu index, and selects all displayed rows for eCmd_DeleteAllTags. The native epoch checks adapt that synchronous popup lifetime to AppKit ownership. scripts/test-reference-menu-targets.py groups the new captured-action receiver with retained folder, comparison, tracking, rename and standalone workflows. The batch evidence records the build, twelve passing system/bundled native cases, both fixture repairs and owned cleanup. This remains partial parity; physical popup interaction, complete visual/control review and signed sandbox/Finder acceptance remain open.

TurtleGit's Reset, Switch, Branch/Tag and New Worktree browse routes uses a native reference browser based on TortoiseGit BrowseRefsDlg, BrowseRefsDlgFilter, GitRevRefBrowser and CChooseVersion at commit 7338078f8ddd924b8cddee35f512f2286072136d. This is a partial port. It is not full BrowseRefs or whole-app acceptance.

Tree, list and selection

The left outline contains namespace directories, starting at refs; references are leaves in the right table. Revision pickers remain single-selection; the standalone browser allows multiple selections. Selecting an initial reference opens its containing directory and selects its row. The list has Branch Name, Tracked branch, Last Author Date, Last Commit, Last Author, Date Last Commit, Last Committer, SHA-1 and Description columns. Tracking and description columns are visible in the local-branch namespace. The split starts with a 190-point namespace pane; resizing preserves a user's adjusted divider, now also across windows. See Presentation and keyboard for saved columns, sorting, header menus, sizing and source shortcuts.

Metadata includes symbolic targets, mailmapped author/committer names, separate dates, branch descriptions and gone upstream names. Annotated tags retain their own object ID, subject and tagger metadata rather than substituting the peeled commit. Custom namespaces, notes, tags and remote references are selectable. Reference keys and namespace boundaries preserve UTF-8 spelling, including canonical-equivalent names and leading combining marks. The shared native revision popup adds explicit menu items because AppKit addItems(withTitles:) merges canonically equivalent titles; direct menu insertion retains both entries and selection indices. Invalid UTF-8 names and all Git Unicode argv/precomposition policies are not established by these checks.

Filter fields are Refname, Subject, Authors and SHA-1, initially all enabled. Refname filtering uses the displayed name relative to the selected directory. The source non-regex, case-insensitive token filter is reused, including its post-quote prefix behavior. Show nested refs is persisted and reloads the catalog. All/Only merged/Only unmerged use HEAD reachability. Current Branch accepts live HEAD and closes the browser, even when the current branch is filtered out or absent from the displayed catalog. A local branch is returned canonically for the native typed choosers; upstream returns its short name. Detached HEAD returns its full object ID, and an unborn local branch is still selectable. This follows OnBnClickedCurrentbranch and GetCurrentBranch(true) rather than treating the button as a navigation shortcut. Non-local symbolic HEAD falls back to HEAD. Git read failures remain visible errors. F5 refreshes the catalog. Column clicks change sort and native indicators; names use macOS logical comparison, dates use numeric epochs, and hashes use case-insensitive lexical comparison. Exact Windows sort policies, large catalog performance and all metadata/error variants remain pending.

Reset handoff and context commands

Reset owns the browser. Parent Reset/apply, competing previews/pickers, close and Quit are gated while it is open and while the fresh selection catalog is loading. OK returns the canonical reference. Branch/remote and tag results select their corresponding Reset controls; other namespaces use the explicit commit field. The unused commit draft survives branch/tag choices. Cancel refreshes the original reference catalog and preserves the selected draft. The active revision control receives native focus after the handoff. Closed or superseded requests cannot publish a new choice. Rejected presentation releases its child without reloading.

The read-only context subset uses original TortoiseGit icons: Select, Show log, Show Reflog, Browse repository, Compare with working tree and Copy reference name. Log and working-tree comparison are offered for commit objects, Reflog for local and remote branches, and working-tree comparison is suppressed in bare repos. Reflog is owned by the browser and blocks parent selection/close until released. Other context callbacks retain canonical names. The sections below describe the implemented deletion, transport, range and namespace commands. Reset, Switch, Branch/Tag and New Worktree use this browser; complete menu behavior, other chooser consumers and physical acceptance remain pending.

Verification and limits

ReferenceBrowserTests covers private real-Git metadata, annotated tags, custom objects/namespaces, scope/token filters, exact packed Unicode references, classification, cancellation, reachability, bare/empty catalogs and read-only repository invariants. scripts/test-reference-browser.py hosts actual Reset, reference browser and Reflog controllers without ordering windows. Detailed checkpoint results are in reference browser QA.

Hidden receivers use isolated preferences and repositories, activation prohibited, and injected sheet presentation that releases keyboard focus from the disabled parent. They do not establish physical sheet transfer/restoration, mouse/keyboard or IME behavior, default buttons, accessibility, light/dark layout comparison, error/close-during-load recovery, signed security scopes or App Store acceptance. No current screenshots or site deployment are claimed. The whole port remains incomplete.

Branch descriptions

The local-branch commit context menu now includes Edit description with the original rename icon, including in bare repositories. Its owned resizable native multiline input follows the pinned BrowseRefsDlg/InputDlg call: title and hint, existing text, shared log font, initial end caret, clean Undo history, Cancel, Ctrl+Return to accept and InputDlg geometry. Plain Return remains multiline. There is no optional checkbox or project log-width/minimum-length requirement at this upstream call site. Empty text is accepted.

The write removes carriage returns and trims surrounding whitespace; empty text unsets the branch description. Successful writes reload the browser's catalog and retain its canonical selection. Cancel leaves config unchanged. Parent selection, refresh and close are locked while the editor is owned. Writes lock the editor; failures retain the draft for retry. Forced parent cleanup cancels pending work and prevents late UI publication. Core config writes explicitly disable Git argument Unicode precomposition to keep byte-distinct branch keys separate. This is not a transactional fence against another process renaming or deleting the branch concurrently.

See scripts/test-reference-description.py and checkpoint evidence. Physical sheets, keyboard/IME/accessibility, visual light/dark comparison and complete Scintilla input features remain unverified or incomplete.

Inline branch rename

Local branches now offer Rename with the original rename icon and F2 in the native reference list. The branch-name cell becomes editable in place, with its folder-relative label selected. Escape cancels, Return accepts, and ending the edit by moving focus also accepts, as the upstream label-edit notification does. The selected namespace prefixes the entered label: in refs/heads, entering other/topic renames to that branch; in refs, the label must begin heads/. The command is available in bare repositories and is not limited to commit object rows. Remote refs and tags cannot be renamed by this command.

Git performs a non-forcing branch -m, preserving branch configuration and reflog history and updating symbolic HEAD when renaming the current branch. Invalid names, existing destinations and Git lock errors are surfaced. Success reloads the catalog using the previous selection, matching source Refresh after label editing: when the old reference disappears, selection falls back to the deepest surviving namespace instead of silently accepting the new reference in the picker. Failures retain the original catalog row. Parent acceptance, refresh, namespace changes, competing children, close and Quit are locked during the edit/write. Forced cleanup cancels pending work and suppresses late UI publication; it cannot roll back an already completed Git rename.

Core and hidden native checks are recorded in rename QA. Physical inline mouse editing, focus-change acceptance, F2 keyboard delivery, IME, visual/accessibility comparison, concurrent external ref changes and signed/security-scope acceptance remain unverified. The full browser command set and whole port remain unfinished.

Tracked branches

Local commit branches in working-tree repositories now offer Select tracked branch and, when a tracked branch is displayed, Unset tracked branch. Tags, remote refs, other namespaces and bare repositories omit these commands, following the pinned BrowseRefsDlg gate. Per the requested in-app icon treatment, these two commands reuse upstream branch/delete artwork; the original BrowseRefsDlg entries have no explicit icon ID.

Select owns another full native reference browser restricted to refs/remotes/, including symbolic remote HEAD entries. It shares the browser's namespace tree, metadata table, text/merge/nested filters, private preference store and canonical context callbacks. Local branches, tags, notes and other namespaces are excluded from its catalog and tree. Cancel leaves the parent and repository unchanged. Parent selection, refresh, competing children, close and Quit are locked while it is owned. Rejected presentation and forced parent cleanup release/cancel the child; closed/stale callbacks cannot set tracking.

The accepted canonical remote ref is checked against configured remotes. Git's branch --set-upstream-to validates the remote fetch mapping instead of writing remote/merge keys directly. Errors use the native browser alert, retain the current selection and explain the possible fetch-setting cause. Success reloads the catalog and tracked-branch column. Unset removes all local branch.<name>.remote and .merge values, accepts absent keys, and retains description, pushRemote, rebase and other branch settings. Errors during unset refresh metadata with the error retained. Unset is two config writes, as upstream; it is not atomic rollback across both keys.

Tracked-branch QA records Core and hidden native checks. Full remote picker mutation/tree/range/network menus, physical sheet/keyboard/focus/IME/layout/accessibility, signed security scopes and App Store acceptance remain pending. No fetch or network operation is performed by setting tracking. The whole port remains incomplete.

Switch from a reference

Commit-object rows in working-tree repositories now offer Switch/Checkout to this… with the original Switch icon. This includes local and remote branches, symbolic remote HEAD, lightweight tags and commit-valued custom namespaces. Annotated tag objects, blobs and bare repositories omit the command, matching BrowseRefsDlg's object/working-tree gate.

The browser owns the existing native Switch window as a modal child and passes the canonical reference as its initial revision. The Switch window retains its branch/tag/commit classification, private preferences, native revision controls, new-branch/tracking defaults, full reference/Log pickers, checkout progress and post-actions. Parent selection, competing dialogs, refresh, close and Quit are locked while it is open. Cancel and rejected presentation release ownership; forced parent cleanup also closes the Switch window's nested selection picker. The browser does not automatically reload after this dialog closes, following the source Switch action; F5 remains available afterward.

RepositoryModel now configures all four browser consumers through one helper and shares the existing standalone Switch change/status/log/post-action callbacks with this owned route. The remote-only tracking picker inherits that configuration. No RepositoryModel is instantiated by the hidden receiver, because its normal constructor writes shared Finder settings; production factory wiring is inspected in source while dialog configuration is captured in the native test.

See reference Switch QA and scripts/test-reference-switch.py. Hidden route checks and existing real-Git Switch progress/picker regressions do not establish physical sheets/focus/keyboard, visual/accessibility/signed behavior. The expanded transaction receiver checks real checkout through this owned route, captured options, all three progress-close policies, acknowledgement locks and a post-action. Initial metadata reads publish together and are cancelled/fenced on close. Forced checkout cleanup is now covered by the hidden receiver described below; physical sheet/window behavior remains unverified. Complete Merge parity, Fetch, creation/tree/range/deletion commands and the full port remain unfinished.

Current Branch acceptance correction

The Current Branch read uses a cancellable request and live repository metadata. Duplicate acceptance, refresh, ordinary close and other choices are blocked while it runs; forced controller cleanup cancels the request and rejects late results. The remote-only tracking picker can return a local Current Branch choice, as in upstream; its owner ignores that non-remote result without changing configuration.

Core coverage includes live branch changes after a cached snapshot, detached and unborn HEAD, bare repositories, linked worktrees, exact Unicode HEAD spelling, cancellation and read-only repository bytes. The hidden native receiver also covers filtered-out choices, single completion/close, owned-child locks, immediate forced close and the owned tracking picker. These checks do not prove physical button interaction, sheet focus restoration or concurrent external HEAD writes.

See Current Branch checkpoint evidence for the tested cases and remaining limits.

Owned checkout transaction and initial load lifecycle

The reference browser retains Switch while its progress controller is open, including successful operations awaiting acknowledgement. The submitted checkout options are captured before asynchronous validation; later draft changes do not change the running checkout. Closing progress acknowledges once, releases Switch and its browser owner, and retains the source browser catalog until explicit F5. Current Branch then reads live HEAD, including a branch changed by that checkout. The existing configured post-action callback receives the previous branch.

The progress presentation entry point preserves the normal native sheet path and allows a hidden receiver to exercise the shipping controller's ownership callback. A rejected presentation invalidates the unstarted progress model and releases its owner without launching Git. Completion callbacks check controller identity before clearing ownership. This does not establish physical sheet behavior.

Switch's initial catalog and branch reads now share a cancellable request, with App Store security-scope checks and publication only after both reads finish. Forced browser/child close cancels that request and prevents late fields or errors. The receiver pauses each actual Git read with an owned wrapper/helper, observes the live processes, closes the owning browser and verifies both processes exit without publishing a partial catalog. See transaction checkpoint evidence.

Forced checkout cleanup

Forced browser/Switch close now cancels checkout validation and the owned progress operation, ends/releases its progress window and rejects late errors, conflict prompts, change notifications, acknowledgements and post-actions. Read-only checkout argument validation uses the same cancellation request as the eventual Git switch, including reference resolution, name validation and branch/tag existence probes. A cancelled probe remains cancellation rather than being converted to an invalid-name/revision result or swallowed as a missing ref.

Progress collects its result locally and publishes only while its model is live. Normal Cancel still allows conflict inspection and retry; forced cleanup cancels that separate inspection read too. Closing a running progress window directly releases its result while leaving the editable Switch owner available for a new attempt. A stale completion cannot clear a newer progress controller.

Cancellation stops owned processes; it does not reverse an already completed checkout. The receiver checks both sides: pausing before switch preserves main, while pausing the follow-up status read leaves HEAD on older. In both cases forced close terminates the recorded wrapper/helper and prevents late callbacks. It also checks a pending cancellation answer, closed-model actions and a fresh retry after forced progress close. See cleanup checkpoint evidence. Physical sheets, external writers, signed scopes and full source parity remain separate unfinished gates.

Merge context command

The commit-object/working-tree context group now includes Merge to "branch"… with the original Merge icon before Switch. The current local branch is omitted; remote branches, symbolic remote HEAD, lightweight tags and custom commit refs qualify. Annotated tag objects and blobs do not qualify, matching BrowseRefsDlg's object-type gate. The menu label and current-branch gate read live HEAD using its Git-resolved admin path, including linked worktrees; detached HEAD follows the source (no branch) label. The action rechecks the gate before presenting.

The browser owns the existing native Merge dialog and passes the canonical ref. Native branch/tag popups and the commit field preserve exact UTF-8 names; a symbolic remote preset remains selectable. The owner blocks selection, competing dialogs, refresh, close and Quit through the Merge/progress acknowledgement. Cancel and rejected presentation release ownership. Initial branch/reference reads are cancellable and closed dialogs reject late metadata publication. The browser retains its catalog after Merge, as the pinned handler does.

RepositoryModel shares the existing standalone Merge status/log/message-picker, Abort and post-action configuration with the owned route; remote-only tracking pickers inherit that configuration. The hidden receiver captures this hook without constructing RepositoryModel or writing shared Finder settings. Merge now passes its selected preference store to progress and hosting, so private receiver policies and message history stay isolated.

The route receiver checks the actual menu/icon/order, live and linked HEAD gates, canonical native presets including packed NFC/NFD names, ownership/rejection/close and a real captured fast-forward merge through native progress and acknowledgement. See Merge route QA and scripts/test-reference-merge.py. Source CAppUtils user-data/rebase preflight, full Merge chooser parity and physical sheets, light/dark/accessibility and signed execution remain incomplete or unverified. Tree creation, range/deletion commands and the full port remain unfinished.

Merge now uses the owned full reference browser and typed-revision Log picker. Its forced lifetime cleanup is checked separately in Merge cleanup QA; picker behavior is recorded in Merge parity. Hidden receivers do not establish physical or signed acceptance.

Remote Create Branch command

Single remote references now offer Create Branch… with the upstream Copy artwork after the working-tree Merge/Switch group. Local branches and tags do not receive this single-ref command. The action captures the displayed object hash, matching BrowseRefsDlg::eCmd_CreateBranch, and opens the owned native Branch creation dialog in explicit Commit mode. Moving the remote while the dialog is open does not change its base. Source CreateBranchTag callbacks are shared with the standalone factory, and remote-only tracking pickers inherit configuration.

The browser blocks competing actions and normal close/Quit while the child is owned, releases on Cancel/rejected presentation, and refreshes its catalog after the child closes, matching source Refresh. Identity checks prevent an old child from releasing a newer one. Forced browser closure closes the child without refreshing a closed owner. Branch/Tag metadata and creation now own cancellation tokens; creation validation, reference lookup, mutation and description use the repository token. Closed models reject late metadata/errors/post-actions.

test-reference-create-branch.py checks actual native menu/icon, namespace gates, private configuration, a moved remote after presentation, real branch creation at the captured hash, refreshed browser catalog, Cancel/rejection/duplicate/ competing/close/Quit and live creation-process termination with unchanged refs. Existing creation-picker and browser-Merge receivers remain regression gates. Physical sheets/focus, bare/non-commit acceptance, every slow validation/metadata stage, routed post-creation checkout/description recovery and signed scope remain pending. Remaining source browser commands and full parity are still incomplete.

Fetch from a remote reference

Remote references with a matching configured remote now offer Fetch from "remote", with upstream Update artwork before Merge/Switch. The source helper SplitRemoteBranchName matches configured names in returned order, using an exact name or name-plus-slash prefix. The native snapshot captures those names and uses byte-exact matching; overlapping remote names therefore follow the source's first match rather than a longest-prefix rule. Local and unconfigured remote refs do not receive this action. Bare/other-object remote refs retain the source gate.

The browser owns native Fetch, presets the matched remote (not the selected branch suffix), and shares standalone status/log/Rebase/post-action configuration. It blocks competing commands, normal close and Quit through progress acknowledgement, then refreshes the catalog, including after Cancel/rejected presentation. Tracking pickers inherit configuration. Identity checks and invalidated-owner checks protect child release/refresh. Forced closure cancels Fetch metadata/transport and closes owned progress; late output, confirmation answers and callbacks are discarded.

Core Fetch/Pull defaults, parent-submodule metadata, remote branch browsing and plain Fetch validation now accept owned cancellation. Fetch settings/browse reads also validate AppStore repository access. Independent remote reads are superseded by generation/token identity. Fetch progress cancels its process on invalidation, fences post-await results and owns recovery/reset metadata tokens. Pending confirmation and standalone Fetch/result windows now participate in Quit guards.

test-reference-fetch.py checks shipping menu/icon/order, matched remote preset, actual local transport, held result/acknowledgement/catalog refresh, competing and Cancel/rejected/close/Quit gates, forced metadata/transport leader/helper exit and ignored late Cancel answers, plus standalone result Quit/acknowledgement. HEAD, index and working tree remain unchanged. Streaming, Fetch/Rebase decisions, cancellation and submodule-default receivers are regression gates. Physical sheets/focus/gestures/light-dark/VoiceOver, signed Finder/AppStore acceptance, every slow metadata/failure/prompt variant and external remote-config races remain unverified; full menus/application parity remain incomplete.

Single-reference deletion

The single-selection browser now offers source Delete branch, Delete tag and Delete remote branch commands after the existing namespace actions, with original Delete artwork. These depend on namespace, not working-tree or commit object type. The current branch still receives the source command; Git rejects deleting a checked-out branch. Notes/custom namespaces do not receive deletion.

The owned native Yes/No confirmation uses the selected canonical name. Branches are checked for full merge into HEAD; an unmerged or unreadable relationship adds the source warning. Tags omit that check. Remote branches also warn that the branch will be removed on the remote. Local branches use force deletion, tags use tag deletion, and remote branches use deletion Push refspecs against the first matching configured remote, following source order/prefix semantics. An unconfigured remote reference performs no deletion, matching source's empty batch. Git argv precomposition is disabled for canonical reference operations so canonically equivalent names remain distinct. Both deletion APIs select the POSIX argv process path even when a caller omits a cancellation token; this avoids Foundation Process spelling conversion for composed reference arguments.

The browser stays busy through preflight, confirmation and mutation. F5 cannot supersede a deletion; selection, competing actions, normal close and Quit are blocked. Yes, No and failures all refresh the catalog; failures retain diagnostics. Forced close cancels owned Git work, aborts an attached confirmation and ignores late answers/results. Completed Git mutations are not rolled back. Store builds validate retained repository scope before beginning this workflow.

test-reference-delete.py checks hidden shipping menus/icons and captured confirmations, No/Yes, unmerged branch/tag/local-remote deletion and Refresh, namespace and parent/close/Quit/late-answer gates. It also pauses and terminates recorded preflight, local deletion and Push leader/helper processes. Core tests cover checked-out failure, bare repositories, packed canonical-equivalent names, remote namespace effects and cancellation. See qa/reference-delete-2026-10-09.json for results and limits.

Physical Yes/No/default/Escape/error sheets, every validation timing, real remote authentication/progress, linked-worktree deletion failures and signed sandbox/Finder acceptance still need work. Full browser and application parity remain incomplete.

Standalone Browse References and range Log

Browse References is now a repository action in the application menu/sidebar and Finder menu projection, using source IDI_REPOBROWSE artwork and placement after Reflog. Source RefBrowseCommand accepts working-tree and bare repositories. Its shell clauses require one folder-in-Git selection, or a bare repository flag. The native action/request, metadata gate and menu builder follow those clauses. Application routing retains one standalone controller per repository, shares the existing interaction factory, and releases it by controller identity on close.

Standalone mode allows multiple table selections. The picker-only Select menu is omitted. Double-click opens Log for the first selected reference, or the repository browser for a tree object, without closing this browser. OK closes with zero, one or multiple selections; chooser mode still returns exactly one canonical name. Busy/owned-child/rename gates remain shared.

Two selected references now offer source .. and ... Log ranges. The last newly selected reference is the right endpoint, independently of displayed sorting. Labels strip source namespaces; callbacks pass canonical typed HistoryRevisionRange values through the shared Log factory to avoid branch/tag ambiguity and the single-revision field rejecting range expressions. Copy ref names exports all selected source-short names in displayed order using native LF. Local branches omit refs/heads/; other namespaces omit refs/. Canonical names remain unchanged for backend operations. Filtering discards hidden selections and keeps visible ones. Single-reference actions remain available only with one selected row.

test-reference-standalone.py checks hidden actual table/menu actions, last-selected range direction, real Log query, copy interception, sort/filter, standalone double-click and close, owned-child gates and unchanged chooser selection policy. The existing browser, deletion and actual Finder menu receivers are regressions. See qa/reference-standalone-2026-10-09.json for results and remaining limits.

Two-reference Compare/Unified Diff and batch deletion are recorded below. Branch/tag folder commands are recorded below; remote folder dialogs remain pending. This checkpoint does not establish complete standalone menus, physical app/Finder activation, range Log window rendering, screenshots, signed sandbox or full application parity.

Batch branch, tag and remote deletion

Multiple references from one supported namespace now receive source Delete N branches, Delete N tags or Delete N remote branches with original Delete artwork. Mixed namespaces and custom refs do not receive deletion. The batch confirmation names the selected count; branch batches explicitly warn that merge status has not been checked, and remote batches also warn about removing branches on the remote. Tags omit both branch warnings. Single-ref confirmation retains its existing HEAD reachability check.

Local branches/tags are deleted in displayed order and stop at the first failure; earlier successful deletions remain completed. Remote references resolve configured names with source first-prefix semantics and are grouped into one deletion Push per remote. Groups follow source CString map order using UTF-16 units while names retain byte-exact identity. Unconfigured remote refs do no work. Selection is captured before preflight and confirmation; changing sort/filter cannot retarget an active batch. Existing busy/close/Quit and forced-process/late-answer fences apply to the whole request. No, Yes and failures refresh the browser.

test-reference-batch-delete.py uses hidden shipping menus/controllers and private Git repositories. It checks batch titles/icons/warnings, No/Yes/Refresh, real local and local-remote effects, mixed namespaces, held late Yes, close/Quit gates and recorded preflight/local/Push leader-helper termination. Core tests check grouped Push command counts/order and stopping after a checked-out branch failure. See qa/reference-batch-delete-2026-10-09.json for final evidence and limits.

Physical confirmations/errors/progress, failure or cancellation after each remote group, all Unicode/config/transport races, linked-worktree variants and signed acceptance remain pending. Complete browser/whole-app parity remains incomplete.

Two-reference comparisons

Exactly two selected references receive Compare selected refs and Show changes as unified diff before both Log ranges, with the source Diff artwork. Both comparisons use displayed list order, independently of the last-selected reference used to direct Log ranges. The changed-files dialog receives canonical reference names and resolves their current values. Unified Diff receives the captured catalog object IDs, so moving a branch does not retarget its patch. The unified backend accepts tree objects too and preserves raw patch bytes/statistics.

Compare opens a browser-owned native Changed Files sheet with shared Log/file-log and submodule routing. Closing it releases the parent without refreshing refs. A failed sheet presentation releases ownership. Unified Diff uses the existing built-in/external viewer preference, with Shift reversing the choice. The request blocks duplicate actions, F5, acceptance, ordinary close and Quit until completion. Forced browser closure cancels owned reads and rejects late results, errors or built-in viewer presentation. Changed Files catalog, comparison and patch reads also receive cancellation tokens and closed-window publication fences.

test-reference-comparison.py runs hidden shipping controllers and menus. It checks source command order/icons, canonical names versus captured hashes, actual changed files, parent ownership, intercepted viewer bytes/Shift, duplicate/F5/close/Quit gates and recorded leader/helper cleanup while resolving revisions or generating a unified patch. Core tests cover moving names, forward/reverse bytes, tree/bare patches, invalid selection counts and pre-cancelled comparison/patch operations. Final results and limits are recorded in qa/reference-comparison-2026-10-09.json.

Physical sheets, full Changed Files dialog parity, every patch/child-editor cleanup timing, external viewer launch, Unicode/config races and signed Finder/App Store acceptance are not established by hidden checks. Remote folder menus and full app parity remain unfinished.

Branch and tag folder menus

The native namespace outline now has context menus; an empty list selection uses the same folder commands. Branch folders and descendants offer Create Branch… with Copy artwork. Tag folders and descendants offer Create Tag… with Tag artwork and Delete all tags with Delete artwork. Both end with source Copy ref names; folder Copy writes an empty value, independently of selected leaves, matching the source's empty folder-command leaf vector. Prefix boundaries are checked, so refs/headsh cannot become a branch namespace.

Folder creation uses the shared browser-owned Branch/Tag dialog with HEAD selected and no guessed name or row base. Source folder commands do not require a working tree; bare creation remains available. Existing creation configuration/transaction, picker, close and refresh behavior is shared with the remote-row creation route. A rejected presenter releases the child and refreshes the catalog.

Delete all tags selects and captures the currently displayed rows, including the folder scope, nested setting and text filter. It uses existing cancellable tag confirmation/deletion/Refresh behavior. A single-selection chooser can still run this folder command against all displayed tags. Empty results disable deletion instead of passing an empty vector to source confirmation. Pending confirmation blocks duplicate/F5/close/Quit; forced close rejects late Yes. Undisplayed tags remain intact. Remote management, remote Fetch/tag folder commands and remote tag dialogs are still pending; this is not complete folder or whole-app parity.

The source review also corrected copied leaf names from canonical to source-short spelling, and the menu title to Copy ref names. The existing standalone native receiver now checks left\nright, while backend Log/selection references stay canonical. Core checks preserve namespace boundaries and byte-exact Unicode names.

test-reference-folders.py checks hidden actual outline menus/icons, real branch and tag creation from HEAD, filtered/scoped deletion No/Yes/Refresh, parent gates, rejected creation, late confirmation, single-picker deletion and bare tag creation. Standalone, batch deletion and comparison receivers remain regressions. Final results and limits are in qa/reference-folders-2026-10-09.json. Physical tree right-click/hit-testing/focus/light-dark/VoiceOver, all mutation/creation error variants, Unicode whitespace trimming and signed acceptance remain unverified.

Remote tag dialog and folder Fetch

Tag folders now offer Delete remote tags on "remote"… for each configured remote, with original Delete artwork. Remote folders/descendants offer Fetch from "remote" and Delete remote tags…, resolving configured first-prefix names consistently with source SplitRemoteBranchName. The owned Delete Remote Tag dialog has its source tag list, tri-state selection, Delete/Abort and refresh behavior, plus token-owned progress sheets. Its closure releases BrowseRefs without refreshing the catalog. Folder Fetch uses the existing owned preset/progress/Refresh path. The bare metadata read now also receives the browser's request token.

See remote tag parity and qa/remote-tags-2026-10-09.json for checks and limits. Manage Remotes now has a native AppKit page on remote folders, with the original Settings icon and shared owned Fetch offer. See remote settings parity. Per-remote native SSH identity selection and transport preparation now exist; physical/signed acceptance remains pending. Physical/signed/full browser and application parity is incomplete.

Remote-branch deletion now awaits native key preparation per sorted remote batch, as source DoDeleteRefs does before each Push. Local branch/tag deletion does not load keys. Native operation closure cancels pending encrypted responses and owns agent cleanup. This does not cover Log's separate reference-deletion route or prove displayed sheets, signed scopes or real SSH authentication. See SSH-TRANSPORT-PARITY.md.

Deletion fixture follow-up, 11 October 2026

The completed hosted run at 76824a5793 failed with “Owned deletion process absent”. Its log did not report the parsed PIDs. The fixture now atomically publishes its helper/child PID marker before readiness, retains the original live-process assertions and reports PID diagnostics. Its driver links the fresh Debug objects, runs the native event loop, and bounds/terminates/reaps private receivers. Both Git engines passed the original confirmation/mutation/refresh and forced-close checks after this change. This does not prove the exact old CI root cause or a new hosted pass. CI now uses the documented standard Intel runner and preserves an active workflow while superseding future pending runs on the same branch. See the scoped record.

Comparison/Fetch/Create Branch PID publication — 2026-10-12

Hosted macOS run 38267892128 at b15e5e5 is terminal failed. Job 114882846326 passed reference folders, then the grouped comparison receiver failed with “Owned comparison process absent”; GitHub subsequently cleaned a shell and sleep helper. The old diagnostic omitted parsed PIDs, so the exact historical cause is not proven. Its readiness protocol allowed observing a PID file after shell redirection created it but before printf completed, which is consistent with the failure and matches the earlier deletion fixture follow-up.

Comparison now writes the private PID pair to a temporary sibling and renames it into readiness only after completion. The reference-family audit found the same protocol in Fetch and Create Branch and corrected those too. All original live process, cancellation, late publication and repository-preservation assertions remain; failures include parsed PID diagnostics. Each affected forced-close section has asynchronous failure cleanup: close the owner, recover its private marker if needed, terminate only recorded fixture PIDs and wait, with a bounded kill/wait fallback and a diagnostic if they remain. The original failure is still reported; cleanup does not turn it into a pass. The failure-only fallback itself was not artificially injected in this checkpoint.

Focused comparison passed with system and bundled Git, including rev-parse and diff-tree forced-close cleanup. Then the newly changed Fetch/Create Branch cases passed with both engines, including metadata/transport/creation forced cleanup and their existing real mutation/refresh/confirmation checks. Total: two cached receiver compilations after the respective fixture changes, six owned receiver processes, all reaped. Hidden native hosts logged existing zero-size NSSplitView layout diagnostics; this is not physical layout acceptance. No app/Core rebuild or full sixteen-family group was run for these fixture-only edits. Newer hosted macOS work was observed active/queued and was not restarted; no complete hosted pass is claimed. SVN scoped acceptance was deferred to address this actual CI failure. See qa/reference-pid-publication-ci-2026-10-12.json.

The shared Input dialog batch replaces the separate Log/description forms. The follow-up host consolidation brings Rebase and Synchronization onto that same form with effective project settings. Full SciEdit and physical acceptance remain open.

The connected Log reference-navigation family adds source reference-set scopes, multi-tip/range picking, bounded exact recents and owned browser/preparation lifetime. ChooseVersion histories remain session-only.

Connected reference mutations — October 14, 2026

Inline Rename, Set/Drop tracked branch and local/remote deletion now use the same bounded colored output area, retained result, cancellation preference and action log. Deletion owns its confirmation through a once-only settlement; forced closure settles the answer as No. Lifetime counters retain preparation, confirmation, transport, mutation and cancellation questions after forced closure. Final reads refresh the catalog after success, failure or cancellation, including partial multi-reference deletion or a partly completed two-key tracking drop. Captured reference hashes are checked before mutation; local deletions recheck each target. Description keeps the upstream multiline Input dialog and its write lock, checks the captured branch tip and refreshes other repository windows after saving.

These hash checks are preflight guards, not an atomic ref/config transaction. Remote deletion retains upstream's ordinary Push semantics; it does not add a force-with-lease guarantee. Cancellation cannot undo a completed Git operation. Physical menus/pickers/themes, signed transport/sandbox acceptance and all pinned control-by-control parity remain separate open gates. Grouped results are recorded with the Rename family after implementation is complete.

The expanded grouped checkpoint passed 36 Core tests and fourteen native cases across system and bundled Git. Initial compilation and fixture failures, scoped repairs, actual process cleanup and acceptance limits are recorded in the batch evidence.

Source filter forms and accepted ranges — October 17, 2026

The source filter cue now lists its active Refname, Subject, Authors and SHA-1 fields. The native search field, field-mask menu, nested-reference checkbox and merged-branch selector use captured form lifetimes. Query/mask updates refilter synchronously; nested and branch-filter choices synchronously start their catalog read, avoiding delayed SwiftUI change handlers. Child, refresh, namespace and rename/question transitions retire older form callbacks. The native search control also retires when dismantled or its window closes.

The multi-selection OK menu now accepts the pair displayed when its range choices were built, even if the list selection moves before dispatch. The existing menu catalog/view epoch rejects choices after a refresh, filter, sort or child change. Standalone and single-selection OK behavior retains its existing routing. Explicit host sizing keeps the namespace/list, filter and source footer visible. This review maps all eleven static source controls; mappings remain partial and are not a certificate of complete menu, visual or workflow parity.

Grouped source batch evidence records ten distinct case/engine passes, the layout/fixture repairs and owned process absence. Six unaffected passes precede the final layout-only build.

Native dialog, light

Native dialog, dark

Connected source actions and native popup entry — October 18, 2026

The pinned src/TortoiseProc/BrowseRefsDlg.cpp::ShowContextMenu separates local branch Rename/Description from tracked-branch commands. The native reference popup now retains that separator and its existing source command/icon order. Namespace popups capture an empty leaf set, as the source tree handler does, even while the contents list has selected references. Their folder commands cannot accidentally inherit the selected leaf payload. The native Add Remote convenience remains an explicit extension to the source folder menu, where Manage Remotes supplies Add.

Both the namespace outline and reference list now expose native accessibility Show Menu and Shift-F10. They open at the selected row through the existing source menu builder; they do not reuse a stale mouse clickedRow. Menu preparation occurs before popup tracking. Pointer menus keep their existing clicked-row policy. Attached children, unavailable/hidden views, detached or retired owners refuse these entry points. A shared counted Git worker is not needed merely to display a synchronous popup; actual child/Git commands retain their existing ownership. Physical key delivery and VoiceOver discovery are separate acceptance work.

A displayed menu command can be claimed once. Repeated delayed target/action invocations cannot replay clipboard writes, native child launches or mutations. Captured selection and existing catalog/view epochs continue to govern dispatch; normal selection movement does not retarget an older menu or replace the user's visible selection. Folder/range and column commands share this one-shot wrapper.

An owned Compare selected refs child now receives the browser's actual preference store before its model, controls and Git read are created. Its nested history, file actions and presentation consequently inherit the same owner settings rather than silently falling back to standard defaults. Existing child close, cancellation, comparison snapshot and application-Quit gates remain unchanged.

Reference catalog descriptions now use the same local-only, first-duplicate reader as Log (Git.cpp::GetBranchDescriptions). Deliberately empty first values remain empty; global descriptions do not appear as repository descriptions. Multiline values and exact valid UTF-8 branch identities remain separate. ReferenceBrowserTests.testCatalogDescriptionsUseLocalFirstEntryAndPreserveEmptyAndExactNames uses a private global-config wrapper, paired packed UTF-8 references and unchanged HEAD/index/config assertions. The new native receiver is qa/reference-action-family-native-2026-10-18.swift; it covers actual source section order, selected-row popup entry, leaf/folder capture, one-shot callbacks, child preference identity and stale/closed-owner refusal. The parent records actual grouped outcomes; fixture existence is not a pass.

This continuation does not certify every source menu/object/error variant, physical keyboard/mouse/VoiceOver, signed Finder/App Store grants, or whole-dialog parity. Working-tree comparison's Shift/alternative route, all Windows menu/default painting details and platform-specific shortcuts remain separate review work.

Actual grouped build/check outcomes: history action batch evidence.

Optional remote-reference engine — October 19, 2026

The remote-reference workflow continuation connects source-selected libgit2 reference/tag reads and remote-ref deletions, including the branch picker's source-specific HEAD choice, captured native preferences and post-delete refill. Ordinary Push stays on its CLI source path. Actual grouped evidence and remaining certificate-cache, public/signed/physical and complete source acceptance are recorded with that family.