TurtleGit for Mac

View Markdown source ↗

Commit dialog parity

The category check-link continuation covers the source action masks, copy membership, submodule exceptions and shared native checkbox/staging routes. Its grouped checkpoint is separate from older evidence.

Current light/dark main-window review: primary dialog visual review.

Issue-matching runtime, native property/field/warning behavior and remaining work are tracked in issue tracker parity.

The reference is src/TortoiseProc/CommitDlg.cpp and IDD_COMMITDLG in src/Resources/TortoiseProcENG.rc, pinned to the commit in upstream.json. PrepareIndexForCommitWithoutStagingSupport implements checked whole-file commits; PrepareStagingSupport enables three-state checkboxes in the same file list and bypasses checked-file index preparation. These are distinct commit modes.

Implemented

Verification

The Swift suite has 95 tests, including real Git commits exercising both modes, unchecked staged changes, unusual literal filenames, unborn HEAD, staged renames and deletions, amend, author and sign-off, later unstaged edits, and hook rejection.

The native checkbox workflow was exercised on the disposable documentation repository: only README.md was checked and committed, while the unchecked staged Sources/Repository.swift remained in the index. Double-click opened its actual Git diff. site/assets/commit.png captures that real native window.

Native staging mode was also exercised on the sample repository. The mixed checkbox displayed a staged file with later working-tree edits. Clicking an unversioned file's checkbox staged it; clicking again unstaged it. A native staging-mode commit included only the previously staged repository model line and left the later working-tree edit, README changes and unversioned file on disk. site/assets/staging.png records the actual mixed-state window.

PatchViewDlg::ShowAndAlignToParent places the upstream partial-staging patch window to the right of Commit. The native attached window now follows that layout and tracks the parent's movement and height. Native UI checks staged one added line, staged a separate hunk, and unstaged the first line while retaining the second hunk and an unrelated staged file. Working-tree edits remained unchanged. site/assets/partial-staging.png captures both actual native windows and Find. The native Find bar was seeded from selected text, returned five matches for Preview, and was dismissed with Escape without closing the patch. Next/Previous shortcuts were exercised. A second Escape closed the patch and restored the button labels. Native Save As output matched the current Git diff byte-for-byte. The closed window wrote its width preference, which is read on reopening.

Integration tests cover line and hunk staging/unstaging, adjusting offsets across multiple hunks, unusual filenames, and rejection of stale diffs. Partial operations currently support ordinary modified UTF-8 text files. The pinned upstream StagingOperations::FindHunkEndGivenHunkStartAndCounts explicitly limits partial staging to modified files and excludes added/deleted files. Those files use whole-file staging in this port too. Renamed, binary, mode-changing and non-UTF-8 files also currently require whole-file staging; their behavior still needs audit. Enabling staging support itself retains the same file list and switches its checkbox semantics. Staged files remain visible outside Finder-requested scope.

Remaining upstream behavior

These entries remain partial in the file/dialog inventory. Passing tests establish these workflows, not full TortoiseGit parity or App Store readiness.

Control audit correction

The user-supplied Commit and action-menu screenshots exposed missing controls. The native window now groups Amend / Set author date / Set author under Message; Staging support, Show Unversioned Files and Do not autoselect submodules sit below the file list inside Changes made. Show Whole Project and Message only sit below that group. Extra Stage/Unstage/Staged diff controls have been removed from the footer; staging commands remain in the file context menu and attached patch links. Files/Submodules category links and conditional category enablement were added.

Set author date uses Git --date. Amend exposes Reset (--date=now); no separate committer-date checkbox exists in the pinned upstream resource. New branch creates and checks out the named branch before committing; a hook failure may leave the new branch selected, as the branch creation precedes the commit.

Message only disables the file list. Checkbox mode creates an empty/message-only commit while preserving staged and working-tree content. Staging mode follows upstream's --allow-empty index commit, so existing staged content is included. ReCommit keeps the window and clears commit-specific message/options; Commit & Push opens the existing native Push window after a successful commit, without sending a push automatically. The native action menu exposes those three actions.

Real tests verify message-only tree/index/worktree preservation, author timestamps, amend date reset, staging-mode message-only index behavior, new-branch history, invalid branch rejection and Gitlink metadata including tab/Unicode paths. Native layout, configured author text, date-control visibility and enabled action menu were observed. Initial menu-opening interruptions prevented execution QA; the native checks recorded below subsequently exercised both actions. site/assets/commit-controls.png is an actual window capture; older screenshots describe earlier layouts. Full Commit parity is still incomplete, including amend comparison and hidden/conditional workflows.

Amend comparison and selection

Amend now displays changes against the previous commit's first parent by default. Show diff to last commit is visible while amending and enabled when HEAD has a parent. CommitDlg temporarily disables it while refreshing, then enables it for non-root revisions. It switches the comparison to HEAD. Root revisions select HEAD comparison in the native window, as upstream does. The backend also supports explicit parent-mode root amendments against the empty tree in its object format. Statistics, three-state checkbox status, whole-file unstaging and partial unstaging use the same comparison base. The patch window labels Parent → Index in that mode.

Checkbox-mode parent-based amendment builds a temporary index from that parent and includes only checked whole-file contents. Unchecked changes from the prior commit are omitted from its replacement and remain in the real index; unrelated staged and working-tree edits are preserved. Git --amend preserves the original commit parents, including both parents of a merge. The existing programmatic CommitOptions default continues to use HEAD unless amendDiffToLastCommit is explicitly false; the native window selects the upstream parent-based mode.

Amend exchanges the draft and amendment messages on toggle rather than discarding the draft. Set author date initializes from HEAD's author timestamp during amend; Reset requests the current author date. Author override initializes from HEAD's identity in amend mode. Separate native date/time fields now expose seconds. The Amend checkbox is disabled for unborn HEAD, and an empty checked parent-based selection needs Message only.

Five real tests cover selective amendment, unchecked index/worktree preservation, root/rename baselines, parent-based unstage/partial patches, hook failure and merge parents. All 87 tests pass. Native QA verified draft restoration, parent/HEAD list switching in both modes, staging enablement without index mutation and a selective amendment. The final checkbox-mode comparison control was checked as enabled for a revision with a parent; both views were exercised after that enablement fix. Git verified its parent hash, committed file contents and preserved mixed edits. site/assets/commit-amend.png captures that actual checked plan before committing. Native root/merge/rename and broader action combinations remain unverified; this is still a partial Commit port. Specific date/override checks are recorded below.

View Patch and repository mode preferences

The normal checkbox mode now exposes View Patch / Hide Patch in the Changes made section. Like CommitDlg::FillPatchView, its attached right-hand window follows highlighted file rows independently of checked commit paths, compares complete working-tree contents with HEAD (or the amend parent), and includes both rename paths. Unversioned/ignored rows are excluded. The read-only view has colored unified patches and Save As, Copy, Select All and Find; no staging actions are exposed. Staging mode retains the separate partial staging/unstaging links and comparisons.

The original local Git settings tgit.commitstagingsupport and tgit.commitshowpatch now restore the mode and patch visibility on opening Commit. Hiding/closing the patch clears its visibility preference; closing the parent preserves it. Changing staging mode closes the existing patch and stores the new mode. Native verification covered staging restoration after Cancel/reopen and restoring an explicitly saved show-patch preference after relaunch. Parent-close/reopen with the patch still open, rapid mode changes, and external config changes still need native lifecycle QA.

Three real Git tests cover combined staged/unstaged previews, explicit parent and rename comparison, unborn-index fallback, preference persistence and preservation of HEAD/index/worktree. All 90 tests pass. Native QA displayed both staged and later working-tree edits against HEAD, confirmed the read-only context menu, and switched staging without modifying the index. The actual attached window is captured in site/assets/commit-view-patch.png. Highlight changes, amend switching while open, non-UTF-8/binary preview and multi-display behavior remain to be verified natively.

Native author date/time controls

Commit now uses separate AppKit text-and-stepper date and time fields, matching IDC_COMMIT_DATEPICKER and IDC_COMMIT_TIMEPICKER. The time field includes seconds; labels identify both controls for accessibility. Reset appears only with Amend and Set author date, and disables both fields while requesting Git --date=now. The controls also honor the Commit window's disabled state during operations.

Native QA used a disposable repository with an author timestamp of 2021-02-03T04:05:37+01:00. Amend loaded that exact local time. Editing its seconds from 37 to 38 and entering an override identity produced an actual commit with Git timestamp 1612321538 and the specified author. Date components and the original parent were preserved. A second native amendment with Reset selected stored the current author timestamp, preserved the author and parent, and left a clean index and worktree. site/assets/commit-author-date.png is the actual pre-commit window. The existing real Git date/message-only/reset integration test also passes.

Broader locale, time-zone/DST and date-component editing checks remain pending; these two native workflows do not establish every date/author combination.

Native completion actions

The action-menu verification gap is now closed for a disposable amendment workflow. Selecting ReCommit completed a real Git amendment, kept the Commit window open, cleared its message, reset Amend/options, reloaded status and disabled Commit until another valid message/selection was supplied. Git reflog records the amendment. Selecting Commit & Push on a subsequent amendment completed the Git operation and opened the native Push window for the same repository with local main and origin selected. The disposable bare destination remained empty before Push's OK button. Broader action combinations (ordinary/staging/failed commits, scoped Finder entry and remote errors) remain pending; these checks do not establish all completion paths or the full upstream progress/post-action workflow.

Message/file divider

Commit now has a draggable divider between the Message and Changes made groups, following IDC_SPLITTER, CommitDlg::DoSize and SaveSplitterPos. Moving it changes the editor height and moves Amend/date/author controls with their group; the remaining height goes to the file group. The chosen message-group height is saved in macOS preferences as Commit.MessagePaneHeight. Smaller windows clamp the visible height without discarding that preference. Both panes retain minimum usable heights.

The divider is implemented in the native SwiftUI layout with an AppKit resize cursor and accessibility Increment/Decrement actions. It does not modify the file selection or draft. Native QA dragged from 300 to 340 points, checked upper/lower limits (372 and 245 points in the default window), exercised adjustment actions, and reopened Commit at the saved 325-point position. A minimum 900-by-680-point outer window clamped it to 260 points and retained usable controls; enlarging restored 325 points. The draft and unchecked second file survived dragging. HEAD, index and working-tree diffs matched their original values. site/assets/commit-resize.png captures that actual adjusted window. Full VoiceOver, dark-mode resizing, conditional controls at the bounds and multi-display checks remain pending.

Commit templates and pending operation messages

The native Commit model now reads commit.template once when the dialog opens, without replacing a draft on Refresh or when changing comparison options. Git resolves the configured path, including ~/; relative paths resolve against the repository root. UTF-8 uses replacement decoding, retains a leading BOM and normalizes CRLF/trailing LF to one final newline, matching the pinned loader. Missing/unreadable templates report their path and error while available operation messages remain usable. The pinned loader does not auto-detect UTF-16 or use the commit-output encoding for these inputs. Sandbox access remains subject to the repository lease and macOS permissions; the October 15 external-template batch below adds owned file authorization and saved bookmark reuse. See the decoding audit.

Upstream CGit::LoadTextFile appends to the message buffer. The port therefore appends SQUASH_MSG and then MERGE_MSG to the template, rather than replacing it. git rev-parse --git-path resolves each worktree's own administrative files. ReCommit reloads only the template after a successful commit. An unchanged nonempty template invokes a native warning with Proceed anyway, No and a suppression checkbox; suppression is remembered only after proceeding.

Three real Git integration tests cover relative Unicode and absolute newline paths, UTF-8 BOM/CRLF normalization, absent/missing/invalid templates, operation message append order, ReCommit's template-only seed, linked-worktree separation, and unchanged index/working-tree diffs. These are backend checks, not native UI parity evidence. Native unchanged-template warning and No were subsequently exercised, and native ReCommit template restoration was verified. Proceed-anyway/suppression and draft-preserving Refresh remain pending. Initial automation failures are recorded in the later native verification notes; they do not establish failed app behavior. Recent-message history and its selection dialog are now implemented as described below; revision-picker insertion commands are now implemented and checked as described below.

Recent-message history and editor commands

The message editor now uses a native plain-text NSTextView, with undo and selection-aware insertion. Its context menu adds Paste file list, plus Paste last message and Recent messages when history exists. Original upstream Copy and Log icons accompany these commands. Paste file list uses the displayed checked paths (or staged paths in staging mode), maps unversioned status to Added, and pads status labels to ten columns. Pick commit hash and Pick commit message open the native Log window in revision-selection mode, as described below.

Log History is a resizable native sheet with a horizontal/vertical scrolling NSTableView, flattened one-line messages, multiple selection, OK and Cancel. OK joins selected messages in displayed order with a blank line; double-click accepts one message. Delete removes one selected entry immediately from persisted history and selects an adjacent row. Recent-message insertion replaces an untouched template, otherwise inserts at the current selection with the upstream trailing newline behavior, and avoids inserting a message already at the start of the draft. Paste last message always inserts at the selection.

History lives in user defaults, keyed by the canonical Git common administrative directory; linked worktrees share it. The default limit is 25, with an internal Commit.MaxHistoryItems preference (settings UI now available in Settings → Commit). Empty entries are ignored and exact duplicates move to the front. Each mutation reloads the current store so other open dialogs' newer entries are retained. Successful commits save the submitted message, and amendments also retain the pre-amend draft. Failed commits do not save a success entry. Cancel and the window close button use the upstream-style confirmation, save a changed draft and any pre-amend draft, and leave Git untouched; the suppression checkbox follows upstream's cancel prompt behavior, including suppressing future prompts when No is selected.

Two persistence tests cover reopen, deduplication, limits, Unicode identity, repository isolation, removal and interleaved dialog mutations. The linked-worktree Git test now checks shared history identity. The running native Commit window was observed with the new editor and loaded template in its original pane. Native insertion, selection, deletion, successful commit history, Cancel Yes/No, keyboard selection, Undo and readable light-mode layout were subsequently verified. Cancel/window-close suppression, failed-commit history, pre-amend draft combinations, dark appearance, saved sheet geometry and broader resize checks remain pending.

Native history and template verification

A cancelled multiline draft in /private/tmp/TurtleGitTemplateQA was retained after choosing No, saved after Yes, and recovered from Recent messages over the untouched template. Paste last message replaced the native context-menu-selected word; the editor value and model character count agreed. HEAD, index diff and working-tree diff remained byte-identical during cancellation and history checks. The unchanged-template warning appeared; choosing No returned without committing.

A two-entry isolated history fixture exposed a real rendering defect: calling NSTableView.sizeToFit() against its initial zero-width frame collapsed the message column to one character. The port now gives the table an explicit full message document width and retains horizontal scrolling. Running native pixels verified the correction. Shift-Down selected both messages; OK inserted them in displayed order with a blank line and native Undo restored the template. Delete removed one entry, selected the adjacent row and persisted removal; cancelling Log History left the editor unchanged. Double-click also accepted one message.

Paste file list inserted only the checked file.txt, with the padded Modified label. Native ReCommit committed that disposable change, retained the window, restored the configured template, cleared the completed checked file and saved the submitted message to history. Git verified the new parent, file contents and clean tracked index/worktree; the unchecked unversioned template remained outside the commit. site/assets/commit-history.png is an actual captured sheet using copies of the natively committed and cancelled messages in an isolated screenshot app preference domain. No user repository was modified.

Automation's physical Y/Z key positions are reversed on the German keyboard; using its Y-position shortcut invoked native Undo. Main Edit-menu automation returned stale IDs, so no main-menu Undo execution is claimed. The Debug-only capture helper now uses Command-Option-Shift-S and resolves sheetParent before capturing, producing the verified parent-and-sheet PNG without screen permission.

Pick commit hash and message

The editor context commands follow CommitDlg.cpp::HandleMenuItemClick: open Log in selection mode, omit the working-tree pseudo revision and accept exactly one commit. LogDlg.cpp::EnableOKButton disables OK for zero or multiple selected rows; native selection can still span rows, with acceptance disabled. Normal Log continues to close on OK. The chooser retains its graph, reference colors, full message, changed files, filters and revision menus. Reference creation, checkout and Push route to the existing native dialogs. Double-click retains Log's diff behavior rather than silently accepting a commit.

OK inserts the full hash or complete subject/body at the message editor's selected range and restores editor focus. Cancel inserts nothing. Only one chooser can be attached to a Commit window, and completion is cleared before closing to avoid duplicate insertion. Commit refreshes status after dismissal so changes initiated from Log can be reflected without replacing the existing message.

Native QA in /private/tmp/TurtleGitRevisionPickerQA verified the six-revision merge history, single-selection OK, multiple-selection disabled OK, complete 145-character merge-message insertion, full 40-character hash replacement, Cancel preserving the draft and checked files, and native Undo restoring the message. A no-match search cleared the selection and disabled OK. HEAD, cached diff and working-tree diff remained byte-identical to the fixture baseline. Picker mutation flows, empty repositories, dark appearance, broader keyboard/resize behavior and full Log parity remain pending.

The documentation capture helper now includes attached-sheet frames when sizing images, including sheets wider than their parent. A fresh capture attempt failed with macOS ScreenCaptureKit's audio/video stream-start error; bounds output is therefore not yet visually verified and no new picker image was published.

File access and clipboard context commands

Audited GitStatusListCtrl.cpp menu construction and command handling for IDGITLC_LOG, LOGOLDNAME, OPEN, OPENWITH, EXPLORE and the clipboard submenu. Commit's native menu now offers Show log for one versioned file, including deleted files, and Show log of old name for a rename. These requests retain the originating repository and security-scoped lease and open Log scoped to the selected path. The shared root/Finder Log dispatch uses the same helper. Submodule-specific Log semantics and rename-following history remain separate outstanding requirements.

For one existing non-deleted file, Open uses the macOS association and Open With opens an application-bundle chooser. Submodules omit file-opening commands. Reveal in Finder replaces upstream Explore to. Original open.ico and explorer.ico artwork is copied byte-for-byte from the pinned source, with SHA-256 provenance. Errors from application launch are surfaced in Commit.

Copy to Clipboard preserves the submenu and offers full paths, relative paths, file/folder names and all displayed information. It operates on context selection in displayed order, independently of Commit checkboxes. All-information output has the visible column headings and tab-separated statistics; path outputs have one selected path per line and the upstream trailing newline, using macOS LF. Copy-current-column and native path-copy shortcuts are now implemented as detailed below.

Native QA on the disposable six-revision fixture verified tracked/untracked menu conditions, absence of single-file actions for multiple selection, relative-path clipboard output and the two-row information table with correct columns/counts. Open With displayed the native application chooser; successful external launch, its cancellation/error variants, Reveal in Finder, deleted/renamed-file variants, scoped Log handoff and sandboxed runtime remain unverified. Repeated Log menu automation returned invalidated targets without opening a verified Log window. No success is inferred from these attempts.

Visual QA found status-colored path/count text unreadable on selected blue rows. Selected rows now use native primary text color, while unselected rows retain status colors. Actual native pixels verified readable white path/count text for two selected modified files and one selected untracked file. site/assets/commit-file-selection.png captures this fix. Dark and inactive selection checks remain pending. The existing icon test passes for all 39 assets.

Rename entry point

A single existing versioned row offers Rename with the original upstream artwork. The native window and collision → retry workflow were exercised, including a rename from Commit that preserved separate staged/worktree contents. Check/selection/scope remapping is implemented; its post-close native observation still needs verification.

File Blame entry point

Commit's single-file context menu now includes Blame with the original artwork. The pinned GitStatusListCtrl.cpp excludes unversioned, ignored, added, deleted and directory rows. The native menu applies those state gates and excludes submodules. The request retains Commit's repository and permission lease and uses the shared native viewer. CAppUtils::LaunchTortoiseBlame omits an empty revision, and TortoiseGitBlameDoc.cpp::OnOpenDocument defaults it to HEAD and invokes blame with that revision. The port likewise annotates committed HEAD contents, preserving independent staged and working-tree edits. This entry point does not add a working-copy annotation mode.

Native QA opened Blame from a modified file with different committed, staged and working-tree text. The viewer showed the committed UTF-8 source, including its turtle emoji, origin hash and two annotation rows. HEAD, exact index bytes, all files and porcelain status matched the pre-test baseline. Closing the child window timed out in the UI tool, and normal Quit did not stop the process; the isolated QA process was terminated and process absence verified. No second instance was launched. Added/deleted/untracked and submodule menu gates, renamed/conflicted files, dark appearance and signed sandbox handoff still need native checks.

Swift and unsigned Debug/AppStore builds passed without compiler warnings. Both bundle audits passed with 62 icons, including the packaged Git 2.55.0 runtime's architecture and local-operation checks. The static documentation build passed. The core reader is unchanged; its preceding 274-test run remains the baseline. See Rename parity.

The file menu now offers upstream Ignore name/extension and containing-folder actions for unversioned/deleted selections. Ignore parity records rule semantics and native Commit/Working Tree handoff evidence; post-close restoration remains unverified.

Index flag context actions

Commit and Working Tree now expose Skip worktree and Assume Unchanged for eligible versioned selections. Working Tree also offers Unflag as skip-worktree and assume-unchanged for selections containing either flag. The pinned GitStatusListCtrl.cpp menu gates, command dispatch and SetGitIndexFlagsForSelectedFiles were reviewed, together with the confirmation strings and resourceshell.rc's exact Assume Unchanged label. The native menus use the existing original Ignore artwork; upstream gives these particular status-list entries no explicit icon resource.

Both mark actions retain the other flag. Unflag clears both, without changing staged object IDs or working bytes. Git accepts one flag mode per invocation, so Unflag holds the real index lock, changes a private index twice and replaces the real index only after both succeed. It preserves index permissions and resolves the per-worktree index path. Eligibility is re-read with optional Git index refresh disabled before mutation; invalid mixed selections fail before a write. App Store builds require the model's active repository security scope.

Native QA used one app at a time. Commit showed both actions for a modified file; Skip worktree's Return-default No preserved the row, while Yes removed it from the Commit list. Working Tree's Show ignore local changes flagged files exposed the skip-worktree row. Its Unflag confirmation restored Modified with the original added/removed line counts. Assume Unchanged then displayed its matching status. Process checks confirmed both test apps exited immediately after their checks. Working bytes, staged bytes and the single original commit were retained. An application-menu handoff attempt produced stale observer targets; Working Tree acceptance therefore used a separately launched status preview after the Commit preview had exited. No menu-handoff success is claimed.

Real Git tests cover both flags simultaneously, clearing both, mixed staged and working contents, newline/Unicode and pathspec-looking filenames, invalid mixed selections, existing index locks, linked-worktree separation and index permissions. The first combined-clear attempt failed these tests because Git retained one flag; the locked private-index implementation fixes it. A second test exposed optional status-refresh writes changing permissions during validation; disabling that refresh fixes it. The 16 Working Tree/Git repository tests passed after those fixes. Final native rendering used the earlier sentence-case menu label; the final exact upstream capitalization and sandbox access guard were compiled and tested afterward; all six final Working Tree tests passed. Dark/multiple-selection/native error paths, staged rename and amend-comparison eligibility, and signed sandbox acceptance remain pending.

Restore after commit

The pinned GitStatusListCtrl.cpp restore commands and CommitDlg::RestoreFiles now have a native implementation. Marking a versioned file saves its current working contents once and adds the unchanged upstream restore overlay to its status icon. The context command then becomes Restore. Marking does not stage the file. A successful commit restores saved working contents automatically; HEAD and the index retain the newly committed contents. ReCommit consumes the saved copy and removes the overlay while retaining the dialog.

Manual Restore asks before replacing later edits. Failed commits offer Keep current state (the default) or Restore old state. Closing Commit also allows Cancel. Application Quit now routes open Commit windows through this cancellation path, pauses mutations in the repository and attached patch windows, and cancels termination when a prompt is declined or restoration fails. Remaining saved copies are retained after restoration errors for retry.

Copies are disk-backed, support binary contents and executable permissions, and preserve symbolic-link target text without reading or overwriting its target. Restoration replaces the working file atomically and rejects another repository, a directory destination or a parent escaping the working tree. It does not change the Git index or HEAD. Copies are owned by the dialog; crash recovery is not yet implemented.

Four real Git tests cover binary bytes, permissions, post-commit index/HEAD preservation, symlinks, unversioned rejection, escaping parents, invalid destinations and retry. The focused restore, icon and rebase suite passed 15 tests. Native QA marked a file, committed later contents through ReCommit, and verified that only the working file returned to the saved contents; HEAD/index held the later contents and the overlay cleared. site/assets/commit-restore.png is the actual native capture before that commit.

A subsequent single-window native Quit scenario reached both sheets. The default No kept Commit and its draft open. After marking a tracked file, later edits were made on disk: Yes followed by Cancel in the restoration sheet retained those edits, the saved copy, index and HEAD. Yes followed by Restore old state returned the saved working bytes while preserving the exact index bytes and HEAD hash. The UI observer then presented a fresh empty draft, consistent with relaunching the preview after termination. That preview was closed through Quit/Yes without another UI observation, and the process list confirmed no TurtleGit process.

Manual Restore, hook-failure choices, Keep current state, window-close gestures, multiple Commit windows, staging-mode combinations, renamed paths, dark appearance and signed sandbox access remain unverified natively. This remains a partial port, not completion of either upstream source file.

The unsigned Xcode Debug app build passed after this addition. Its bundle audit verified the embedded Finder extension, licenses and all 59 upstream icon assets, including both restoration icons. This proves packaging, not signed Finder activation or App Store approval.

Revert selected files

The file menu now includes Revert with the original icon. It resets the selected index and working files, leaves added file contents unversioned and unchecked, and restores renamed files to their old names. Existing replaced file contents go to macOS Trash. The native No/Yes prompt and selected-file acceptance, added file handling and unrelated-content preservation were exercised on a disposable repository. Revert during amend targets the parent even when the list shows HEAD, as in the pinned source. See Revert parity for backend coverage, recovery behavior and remaining dedicated-dialog/Finder/progress work.

Native file comparison routing

Reviewed CGitStatusListCtrl::StartDiff at upstream commit 7338078f8ddd924b8cddee35f512f2286072136d. Commit's Compare with base and file double-click now open the native two-pane viewer instead of the unified patch sheet. The viewer uses HEAD versus working contents, including staged edits; amend-with-parent uses the pinned first parent, while Diff to last commit uses HEAD. A root commit has an empty parent base. Staging-list comparison uses working contents, following upstream file-list behavior. The separate unified command keeps its existing index/working patch selection. Submodule comparisons receive the same pinned base through their dedicated window.

A real-Git test checks root empty-base and parent/HEAD comparison bytes with staged-plus-working changes and verifies that comparisons preserve the index. One native QA process verified Commit double-click opening the two-pane viewer with exact committed and working text. After the viewer was closed, the UI observer timed out; the same disposable process was terminated and absence verified. HEAD, index and working bytes stayed unchanged. Context-menu unified output, staging/amend combinations, multi-file and submodule native acceptance, and signed sandbox execution remain pending. Full Commit parity remains partial.

Validation: all 258 Swift tests passed. Final unsigned Debug and App Store builds, both bundle audits and the static Pages build passed. The App Store audit exercised universal Git 2.55.0 local operations and verified 11 Mach-O files, the Finder extension, licenses and 61 original icons. These checks prove compilation and packaging; signed execution and App Store approval remain unverified.

Working-file Export

Pinned CGitStatusListCtrl::FilesExport copies selected working files into a chosen folder, retaining repository-relative paths and replacing existing copies. The Commit file menu now provides Export… with the original IDI_EXPORT (menuexport.ico) artwork. It acts on highlighted rows independently of commit checkboxes and exports working contents even in staging mode. Deleted/missing selections hide the command; directories/submodules are skipped by the exporter. Symlink sources export their target contents, matching upstream CopyFile.

The native macOS folder chooser offers folder creation and retains its temporary security scope through the copy operation. Errors leave the dialog and selection intact. Each destination replacement is atomic; earlier successful files remain if a later copy fails, as upstream does. Source overwrite, Git metadata paths and destination parent symlinks escaping the chosen folder are rejected. Destination leaf symlinks are replaced rather than writing through them.

Four focused export tests cover binary and Unicode contents, nested paths, untracked files, overwrite, executable permissions, directory skipping, symlink sources/destinations, missing files, source overwrite and metadata/path escape. They verify exact HEAD/index preservation. The original icon decode test passes. Native QA selected both a modified nested text file and an unchecked untracked binary, invoked Export from their shared context menu, selected an existing folder and verified both outputs byte for byte against the working tree. HEAD and raw index were unchanged; the single QA app was quit normally. Export/New Folder were disabled after Go to Folder entered an empty destination, then enabled when its row was selected from the parent folder. Native overwrite, cancellation, staging, dark appearance and signed sandbox permissions remain to be exercised; this folder-chooser result does not resolve the separate Log Save As issue.

Alternative editor

Upstream IDGITLC_VIEWREV calls OpenFile(ALTERNATIVEEDITOR), which uses LaunchAlternativeEditor with Notepad as its fallback. Commit now has the same separate editor/Open/Open With commands and original IDI_NOTEPAD artwork. Export precedes these file-opening commands, matching upstream order. The editor action opens current working contents; it does not extract HEAD or stage the file.

Settings → Alternative Editor ports the upstream Notepad/Custom radio choices, path field, enabled-state dependency, Browse and Apply. TextEdit replaces Notepad; Custom chooses a native .app rather than a Windows executable. Disabling Custom retains the chosen app, and blank Custom falls back to TextEdit as upstream's blank configuration falls back to Notepad. The native picker records an application permission bookmark; editing the path invalidates it only when the path changes. Launch resolves the bookmark and retains its temporary scope through completion. Unavailable apps/bookmarks report an error; they do not silently choose another custom application. Settings Cancel discards the draft.

Two preference tests verify defaults, custom/disabled persistence, path and bookmark retention, blank fallback, and invalid paths. The original icon decoding test passes. Native QA verified the working Unicode file in default TextEdit and a custom app chosen through Browse, closing the QA document without edits each time. Final QA verified Apply disables after saving, tab reload keeps Apply disabled, the real bookmark persists on disk, and launch succeeds with that bookmark. HEAD, raw index and source contents remained exact. Three sequential QA instances were closed; none ran concurrently. site/assets/alternative-editor.png is an inspected actual Settings capture showing the saved Custom choice and disabled Apply.

Other editor applications, missing/moved apps, typing/Cancel combinations, dark appearance, signed sandbox file handoff and editor routes outside Commit remain pending. This implements the Commit command and Settings page, not all upstream external-tool configuration or complete status-list menu parity.

Explicit Add commands and Commit menu mask

The pinned CommitDlg::OnInitDialog calls status-list Init with GITSLC_POPALL ^ (GITSLC_POPCOMMIT | GITSLC_POPSAVEAS | GITSLC_POPPREPAREDIFF). Save As, prepare-diff and the status-list Commit command are deliberately absent from this dialog upstream. Their earlier listing as Commit gaps was incorrect; the corresponding commands still need audit in the other windows that enable them.

Unversioned file selections now expose Add with the original Add icon. Holding Shift when opening the menu also exposes Add as Executable (+x) and Add as Symlink, as upstream does for file selections. Normal Add force-stages the selected paths, including ignored files when explicitly passed. Extended commands retain the staged blob and set only its index mode to 100755 or 120000; they do not chmod the working file or create a filesystem symlink. Directory children retain their normal modes, matching AddProgressCommand::SetFileMode's directory skip.

Successful Add refreshes the list and checks the added files, clearing their old unchecked state as upstream does. Existing checks remain unchanged. Operations retain repository access and serialize through GitRepository. A real index lock is held while a private index is prepared; only successful completion replaces the real index. Missing/invalid paths, mode errors and pre-existing locks preserve it.

Four integration tests verify force-add, all three modes, raw binary/Unicode and literal paths, unchanged working contents/permissions and HEAD, retained staged entries, split indexes, unborn indexes, directory modes, existing locks, failed selections and linked-worktree index isolation. Native light-mode QA selected one unchecked untracked Unicode file, invoked Add, and verified Added status, its checked box and the updated two-file count. Its staged blob and disk bytes matched exactly; HEAD and the previously staged entry stayed unchanged. The single QA app quit normally. site/assets/commit-add.png is the inspected actual refreshed window.

Native Shift-menu exposure, extended-mode actions, staging/dark mode, cancellation and a dedicated upstream-style Add progress window remain pending. Checkbox Commit now preserves staged modes that differ from disk while updating checked-file bytes, as detailed below. An explicit normal Add/Stage recalculates modes from disk; staging mode commits the current index directly. Full Add-dialog/progress and Commit completion parity remain partial.

Staged modes through checkbox Commit

PrepareIndexForCommitWithoutStagingSupport updates checked entries with git_index_add_bypath or CLI update-index, while staging mode skips preparation. Our previous git add plus git commit --only path could discard an explicitly staged executable or symlink mode when disk still held a regular non-executable file. This made the extended Add commands ineffective in default checkbox mode.

Checkbox Commit now captures selected staged regular/executable/symlink modes that differ from disk. It reads the latest checked-file bytes into a separate commit index, reapplies those modes to the commit and real indexes, and commits that prepared tree without --only. The existing separate-index path also handles parent-based amendments. Matching staged modes and wholly unstaged changes keep the ordinary path; unstaged native chmod changes still commit their disk mode. No global core.filemode/core.symlinks configuration is changed. Explicit normal Add/Stage replaces the mode intent with disk's current mode. Staging Commit retains its original behavior of committing the index exactly as supplied.

The 27 focused Add, Commit-selection, amend, mode and removal tests passed. Four new mode tests include both explicit Add modes, later working edits, unchecked staged contents, literal Unicode/comma/newline filenames, unborn HEAD, parent amendments, ordinary unstaged chmod, staged executable-bit removal and resetting with normal Add. Disk-mode comparison follows Git’s owner execute bit, including a file with only another execute bit set. Actual tree/index modes, blobs, working permissions and unaffected staged entries are asserted.

Native light-mode checkbox Commit was exercised with pre-staged executable and symlink entries backed by regular files and edited after staging. Both were checked; a third staged file was unchecked. The resulting commit contained 100755 and 120000 with the latest exact working bytes. The unchecked file was excluded from the commit and retained its staged entry. Disk contents and permissions were unchanged; the only QA app quit normally. This verifies native Commit completion with the prepared modes, not Shift-menu invocation of extended Add.

Native Shift extended Add, staging/dark mode, root/amend mode combinations, signed sandbox behavior and hook-driven changes to prepared indexes remain to be checked. Later unstaged disk type/permission changes after a successful commit still follow Git's macOS behavior; broader subsequent-commit virtual-link and executable-file usability remains under audit. Passing these cases does not establish full dialog parity or distribution readiness.

Delete from the Commit file list

Upstream GitStatusListCtrl::OnContextMenuList offers Delete when the selection mark is unversioned, ignored or missing. DeleteSelectedFiles removes selected exact index entries and sends existing paths through the shell’s Recycle Bin; Shift requests permanent deletion. The command appears after Open/Explore and before Ignore. This differs from the separate Delete/keep-local dialog.

Commit now offers Delete with the original delete icon when the selection mark is unversioned, ignored or missing, including mixed selections. Its native confirmation defaults to No. Normal deletion uses macOS Trash; Shift at invocation selects a separately worded permanent-delete confirmation. Missing paths only lose their index entries. No commits or HEAD changes are made. The list refreshes after success or error; success clears selected paths’ commit checks. A staged deletion remains visible.

The repository operation rechecks the status snapshot, validates paths and holds the real index lock while preparing exact removals in a private index. It publishes the prepared index only after file operations succeed. Trash failure never falls back to permanent deletion. If a later operation fails, the error lists any files already moved to Trash so they can be recovered. After publishing, Add and Delete no longer attempt to remove a lock path that a subsequent writer might acquire.

Six integration tests passed: recoverable binary/Unicode/literal-path contents, raw-index preservation for untracked-only deletion, missing tracked entries with a split index, mixed tracked/untracked selections, stale snapshots, cancellation, existing locks, a trashed symlink with an unchanged outside target, and a broken symlink in a linked worktree. Permanent deletion is exercised only on a test-created fixture. Native light-mode QA verified No leaves the exact file/index unchanged, Yes removes an untracked binary from the list, and Delete of a missing tracked file removes its index entry. HEAD and an unrelated staged entry were verified unchanged. The single QA app quit normally, with no remaining TurtleGit processes. The 14 focused Add/Delete/Commit-mode tests, both unsigned Xcode builds, both bundle audits and the site build passed.

Delete selection mark and keyboard behavior

The native file list now retains a selection mark separately from selected paths. Plain clicks and non-range keyboard moves establish the mark. Shift range selection and right-clicking an already selected row preserve it. The menu uses that marked entry’s status to enable Delete, matching upstream’s focused-row gate rather than requiring every selected path to qualify. A mark can remain outside the selected set after Command deselection; the core verifies that marked status snapshot before using it to authorize deletion of the selected paths.

An AppKit event observer is scoped to each Commit table and removed when its view is detached. Delete/forward Delete and Shift-Delete run only while the table itself has keyboard focus. The keyboard gate accepts unversioned/ignored marks, including an index-deleted path that retains an unversioned working copy; missing tracked paths remain menu-only as in upstream PreTranslateMessage. Text editors and other controls retain their normal Delete behavior. Shift opens the explicit permanent-deletion prompt; plain Delete uses Trash. The three staging/checkbox lists keep separate marks.

Native light-mode QA verified both directions of a mixed three-row range: untracked anchor exposes Delete and opens a three-item keyboard confirmation; tracked modified anchor hides Delete and ignores the key. No preserves all exact working bytes and the raw index. The message editor changed abc to ab without a file prompt. A missing tracked row ignored the Delete key. Shift-Delete on the untracked row showed the permanent-delete warning; No preserved the file. Ordinary keyboard Delete followed by Yes removed that untracked file and refreshed the list, preserving HEAD, the raw index and unrelated working edits. The single QA app quit normally, and no TurtleGit process remained.

Eight Delete integration tests now include a cached removal with an unversioned copy, and a marked unversioned entry outside a tracked selection. A stale marked entry is rejected before deleting the tracked file or updating its index entry. The 16 focused Add/Delete/Commit-mode tests, both unsigned Xcode builds, both bundle audits and the site build passed. Native Command deselection, forward-Delete hardware, ignored directories, staging/dark mode, partial filesystem failures and signed sandbox checks remain pending. Other context-menu gates still need their own focus-sensitive audit; full status-list and Commit parity remain partial.

Clipboard column selection and native shortcuts

Upstream IDGITLC_COPYEXT is named misleadingly: its resource text is “Copy all information to clipboard,” and it copies all visible columns. It is not a separate extension-only command. IDGITLC_COPYCOL copies the column hit by the context-menu request without a heading. The menu label is column '<name>' and uses the original clipboard icon. PreTranslateMessage also maps Control-Insert to relative paths and Shift-Control-Insert to relative paths with status and headings.

Commit now supplies that column item through the native menu tracking notification. The AppKit table’s public geometry determines the clicked row/column. It is added before the menu is displayed, avoiding SwiftUI’s cached menu content, which initially required reopening the menu. The item follows a subsequent request on a different column. Clicking an unselected row uses that context row; an already selected row copies the current selection in displayed order. The checkbox column has no text command. Observers are removed when their Commit view is detached.

Command-C is the macOS shortcut for relative paths; Shift-Command-C copies paths and status. Control-Insert variants are also handled for keyboards that supply the Insert/Help key. These shortcuts run only with keyboard focus in the file table; message and other text editors retain their native copy behavior. The shared clipboard formatter supplies full/relative/name, all-information and single-column text with macOS LF and the upstream trailing newline. Only multi-column output has headings. Native count placeholders remain the displayed en dash.

The extension column now includes its leading dot, matching GetFileExtension, including dotfiles; directories/submodules have no extension. Renamed paths display the upstream (from old-path) suffix. Column/all-information copying retains that visible suffix; raw full/relative/name commands and path/status shortcuts do not. This updates Commit’s display and formatter, not all other file lists yet.

Three formatter tests cover Unicode paths and selected order, raw path commands, rename annotations/status, dotfiles/directories, missing and staged statistics, header rules, displayed column order and empty selections. Together with the eight Delete tests, all 11 focused tests passed. Native light-mode QA verified Command-C raw paths, Shift-Command-C paths/status, first-request column 'Status', then column 'Extension', headerless status/leading-dot extension values, and the all-information headings/renamed path/counts. Values were pasted into the actual Commit message editor for verification; no commit was made. HEAD, raw index and working bytes stayed exact. QA apps ran sequentially and each quit normally. site/assets/commit-clipboard.png is the inspected actual 2000 × 1584 final window. Both unsigned Xcode builds, both bundle audits and the site build passed; the App Store bundle audit verified the pinned universal Git runtime and original icons.

Native keyboard navigation with four Down presses and Return copied the expected headerless Status values without changing the two-row selection. Control-Insert hardware, checkbox/empty-area menus, column reordering/hiding, optional filename/date/size/LFS columns, staging/dark mode and signed sandbox execution remain pending. Full status-list, clipboard and Commit parity remain partial.

Changelist commands and successful-commit cleanup

GitStatusListCtrl.cpp's changelist commands and CreateChangelistDlg now have native Commit counterparts: Remove from changelist, Move to changelist with <new changelist>, ignore-on-commit and existing names, and Keep changelists. The Create Changelist sheet has the original prompt, an initially focused name field, Cancel and OK. OK stays disabled until the name is nonempty. Names are not trimmed. Cancel discards the draft without writing metadata.

Assignments are local to the worktree. An atomic, locked turtlegit-changelists.json file in Git's worktree administration directory retains Unicode and literal newline filenames. A preexisting tgitchangelist file is imported when no native file exists, accepting UTF-8 and BOM-marked UTF-16 LE/BE, without rewriting the original file. Other legacy encodings and Windows path conversion remain pending. Mutations reload assignments under the lock; invalid paths, symlink metadata, corrupt or unsupported documents and existing locks fail without overwriting metadata or touching the index.

Moving a file to ignore-on-commit unchecks it; staging mode also unstages the selected paths. Removing membership does not automatically check the file. Freshly opened checkbox-mode dialogs omit ignored paths from automatic checks; users can still check them explicitly. Keep changelists is persisted. After a successful commit, with Keep disabled, cleanup retains visible unchecked files, restore-after-commit paths and assignments outside the displayed directory scope. File-only scopes do not prune, matching upstream. A cleanup failure reports that the commit succeeded and keeps the dialog open rather than claiming rollback.

The pinned upstream menu gate compares the action bitfield with the older git_wc_status_unversioned enum. Both that enum value and the modern ADDED bit are 1, so the native gate excludes a pure Added marked row, rather than silently changing this observable upstream behavior. Broader combined-action gates still need native comparison.

Six real Git tests cover assignment persistence, unusual literal names, reloads across repository actors, mutation validation and existing locks, legacy import, linked worktrees, scoped cleanup, and corrupt/unknown/symlink metadata. Native QA verified initial focus and disabled/enabled OK, Cancel without metadata/index changes, exact Feature 雪 creation, ignored-file unchecking, and membership-only removal. A real checkbox commit pruned the committed assignment and retained the unchecked ignored file; its HEAD, index and disk contents were checked directly. Reopening Commit kept the ignored file unchecked. Explicitly checking it and committing with Keep enabled retained its assignment. All QA instances were closed after testing. site/assets/commit-changelist.png is the inspected actual Create Changelist sheet, captured before dismissal.

The initial Create Changelist screenshot predates grouped rows. The grouped-list work and its separate native captures are described below. ReCommit, scoped, restore-after-commit and broader signed sandbox changelist workflows still need native acceptance.

Status/changelist group layout and actions

PrepareGroups, GetChangeListIdForPath, SetItemGroup and OnContextMenuGroup now have native Commit counterparts. Grouping activates when there are changelist assignments or displayed unversioned/ignored/locally ignored paths. Explicit changelist membership takes precedence over status and index flags. Unassigned files appear in Modified Files, Not Versioned Files, Ignored Files or Local changes ignored; named changelists follow in name order, and ignore-on-commit is last. Empty headings are omitted. The upstream (no changelist) heading is inserted but SetItemGroup routes unassigned files to their status categories; the native list follows that effective placement.

Headers appear within the original columned file list with accent-colored names and horizontal rules in light and dark modes. Their context menu has the upstream Check group and Uncheck group commands. Checkbox mode changes the group's checks; staging mode stages or unstages its paths. These actions retain other groups, working contents and unrelated staged edits. Membership tooltips remain available.

Headers have IDs containing NUL, which cannot be Git paths. Every file command filters header IDs, and the AppKit interaction bridge maps actual table row positions through the grouped row model. This prevents extra headings from shifting the files targeted by keyboard Delete and clicked-column clipboard commands. Plain header clicks are consumed; ordinary Up/Down and Home/End skip headers. Range selection retains native AppKit behavior and may highlight a header, while file commands resolve only the real file rows.

Four model tests cover category precedence, name ordering, ignore-last placement, hidden memberships, empty groups, duplicate-looking heading names, literal newline filenames, index-based file target resolution and nonwrapping navigation. Together with clipboard/Delete regression coverage, 15 focused tests pass. Both app build configurations and bundle audits pass.

Native QA verified the status/Alpha/Beta/ignored order, Check and Uncheck changing only the two Alpha files, initial Down and Home skipping headings, selection of both Alpha files and exact Command-C path output. Staging-mode Check group staged both Alpha files; Uncheck group restored their original index blobs, retaining the unrelated staged a-modified.txt blob, HEAD and all working bytes. Keyboard Trash showed a one-file prompt for the untracked row; No preserved it and Yes removed only that file while leaving the exact raw index, HEAD and all other files intact. The unversioned heading disappeared when its last row was removed.

site/assets/commit-groups-light.png and commit-groups-dark.png are inspected actual 2000 × 1584 captures after that disposable Trash check. Dark appearance was verified in a fresh preview after an appearance-menu automation failure; this does not establish native menu-switch acceptance. QA instances ran sequentially and were closed. The stuck menu instance required SIGTERM of its identified disposable process after normal Quit failed; no system services were terminated.

Audit correction: changelist drag/drop is disabled behind #if 0 in the pinned CGitStatusListCtrlDropTarget::OnDrop, and DragOver rejects drops on group headers. We do not count disabled group movement as an active upstream workflow. The later file-drop batch implements ordinary same-repository file/folder drops; physical Finder acceptance remains open. Merge-parent grouping belongs to historical comparison views and is not implemented here. Broader range selection, first-request grouped clipboard-column acceptance, ignored/local-flag native variants, accessibility group semantics and signed sandbox workflows remain partial.

Unversioned preview and first-request comparison menu gates

The pinned OnNMDblclk routes unversioned/ignored rows to StartDiffWC. CGitDiff::Diff permits an absent repository blob (mustExist is false), while retaining the working file. The native two-pane viewer follows that behavior: explicit untracked and ignored selections have an empty base when the path is absent from the chosen revision. The existing Commit double-click route was verified natively with a Unicode untracked filename and exact working text.

workingFileComparison now reuses the selected-file mode lookup. This handles ignored files below an ignored directory, ignored binary content and symlink contents without staging them. A file removed from the index with git rm --cached but retained on disk uses its actual working bytes; a cached deletion is no longer misrepresented as an absent destination. The status-list route continues to omit clean tracked files, and ordinary staged/working, renamed, amend-to-first-parent and root/empty-base behavior retains its regression coverage.

Commit's Compare with base and unified-diff menu entries now follow the marked row's tracked/unversioned gate. Unversioned/ignored marks and retained unversioned copies omit those tracked-only menu commands. Double-click preview remains available. Unified diff is omitted without HEAD, matching the upstream init-repo gate. Full raw missing/action-mask and alternative-tool variants remain under audit.

Native QA exposed a first-request context bug: right-clicking an unselected tracked row could use the previously highlighted untracked row's gates, hiding Compare and showing Delete. StatusListSelection resolves the requested clicked row immediately when it differs from the existing highlight; existing mixed-selection marks, including a mark outside the highlight, remain available. The interaction bridge preserves the real highlighted selection's anchor during contextual clicks, so canceling another row's menu does not silently move that anchor.

Three additional tests cover ignored text/binary/broken-symlink and cached-removal bytes with raw index/HEAD preservation, comparison state gates, and first-request mark resolution while retaining existing mixed/outside anchors. Together with file comparison, grouped rows and Delete regressions, 28 focused tests pass. Both build configurations and bundle audits pass.

Native checks verified the first untracked menu omits tracked-only diffs and Double-click opens an empty left side with the two original working lines on the right. After the fix, the first tracked menu with a prior untracked highlight shows Compare/unified diff and omits Delete. Canceling it leaves the prior selection intact, whose untracked menu still has its own gates. The reverse first-request case, an unselected untracked file with a tracked highlight, also has the expected Add/Delete and comparison omission. Exact HEAD, raw index, working files and the unrelated staged/later edits remained unchanged throughout. Both QA apps were closed normally and no TurtleGit test processes remained.

site/assets/commit-unversioned-preview.png is the inspected actual 2240 × 1504 native viewer capture. Native ignored/binary/symlink/cached-removal preview variants, unborn menu acceptance, mixed selections, alternative tools and signed sandbox handoff remain pending; this capture establishes only the tested untracked text workflow, not full comparison parity.

Compare two selected files

The pinned GitStatusListCtrl.cpp menu and dispatch at lines 1795–1811 and 2260–2280 enable Compare two files for two selected non-directory rows. Commit's context mask includes this action. Each path independently uses working contents when present or HEAD when deleted; this is distinct from two separate base diffs.

Commit now exposes the original Diff icon action and sends the two paths in visible table order to one native two-pane viewer. The core reads exact working bytes, including literal Unicode/newline paths, binary and broken symlinks, and pins HEAD for missing sides. Directories, gitlinks, duplicate paths, metadata paths and invalid selections are rejected. Viewer reuse includes both paths to prevent a different left-hand file from reusing an unrelated comparison window.

Four file-comparison tests pass, including a new real Git regression covering both deleted-side directions, pinned history after HEAD advances, staged versus working bytes, symlinks and invalid paths. Debug and unsigned App Store builds and both bundle audits pass. Native QA selected two tracked rows, invoked the first context menu action, and verified the actual working contents and colored inline differences on opposite sides. HEAD, raw index and all working file bytes remained identical; the QA app quit normally and no test instances remained.

site/assets/commit-file-pair.png is the inspected original 2240 × 1504 native capture. Native unversioned/deleted/symlink/binary pair variants, alternative diff tools, historical status-list integration and signed sandbox execution remain pending. This is a partial port of the status-list action, not full dialog parity.

Highlighted checkbox changes and keyboard refresh

Pinned GitStatusListCtrl.cpp::OnLvnItemchanged applies a clicked row's new check state to all highlighted rows when that row is highlighted, and changes only the clicked row otherwise. The native ordinary and three-state staging checkboxes now follow this rule. StatusListSelection.checkboxEntries resolves only actual file IDs from the displayed list, excluding stale selections and presentation headings. Table-focused Space applies the focused highlighted row's next checkbox state to the highlight; mixed staging state stages remaining changes, and fully staged state unstages. Message-editor Space continues inserting ordinary spaces.

The pinned file context menu contains no Check selected files, Uncheck selected files, Stage selected files or Unstage selected files items. Those native additions were removed now that checkbox selection works directly. Upstream group-header Check group and Uncheck group remain available. Commit's native window handles F5 through the same reload path as Refresh, including message-editor focus, with busy, quit-confirmation and attached-sheet gates. Command-Return remains the macOS adaptation of upstream Control-Return.

A regression covers highlighted/unhighlighted checkbox targets, literal Unicode and newline paths, stale IDs and group IDs. Together with revision comparisons, file comparisons and groups, 22 focused tests pass. Debug and unsigned App Store builds and both resource/bundled-Git audits pass.

Native QA verified one checkbox unchecked both highlighted tracked files; checking an unhighlighted untracked file preserved those highlights and checks. Space checked the two highlighted files; message-editor Space remained ordinary text. Creating a disposable untracked file outside the app and pressing F5 in the message editor refreshed the list while preserving message preserved and all checks. The first selected-file menu omitted the four extra commands. These checks left HEAD, the raw index and original working bytes unchanged.

In staging mode, Space staged two highlighted untracked files with their exact working blobs. Clicking either highlighted staging checkbox then unstaged both, restoring the original index entries while preserving unrelated staged contents, HEAD and every working file. The test app quit normally and no instances remained. site/assets/commit-checkbox-selection.png is the inspected original 2000 × 1584 ordinary checkbox/F5 capture. Native conflict/dirty-submodule and mixed-state bulk variants, alternate input devices, full accessibility and signed sandbox workflows remain pending; this does not establish full checkbox or Commit parity.

Marked-row index flag actions

Pinned GitStatusListCtrl.cpp lines 1815–1832 enable Skip worktree, Assume Unchanged and Unflag using the selection mark's action mask and flags. Its SetGitIndexFlagsForSelectedFiles (2863–2917) independently looks up selected index paths, updates found entries and reports missing ones before writing the index. Commit now uses its resolved mark for flag-menu visibility and confirmation, including mixed selections and a mark outside the highlight.

The core accepts an explicit marked path for this route, revalidates that mark under an exclusive real-index lock, and changes flags in a private index before atomic publication. Selected stage-zero entries are updated, including idempotent flags and added entries permitted by a different eligible mark; unavailable paths are reported with the updated count. The existing Status-window route retains its whole-selection eligibility check. Clearing both flags and setting either flag now share this transaction and preserve index permissions. Added/deleted combined actions and retained unversioned copies are excluded from their upstream marked menu cases rather than relying only on the displayed state.

Eight WorkingTree regressions pass, including two new checks for action-mask gates, marked mixed-selection updates, missing-path reporting, a mark outside the highlight, literal paths and unchanged index blobs/HEAD/working bytes. Existing lock rejection, both-flag clearing and linked-worktree permission checks also pass. Both native build configurations and bundle/Git resource audits pass.

Native QA highlighted a staged/modified marked file and an untracked file. The first context menu offered Skip worktree and Assume Unchanged. Its confirmation then updated only the indexed path and displayed the unavailable untracked path in a native result sheet. Index blobs, HEAD and all working bytes remained unchanged. The flagged row moved to Local changes ignored, whose selected-file menu omitted Skip worktree and offered Assume Unchanged and Unflag. Confirmed Unflag returned the row to Modified Files and removed the flag without changing those contents. The same QA process quit normally; no test apps remained.

site/assets/commit-index-flags.png is the inspected original 2000 × 1584 native partial-result sheet capture. Native No/cancel, assume-valid, outside-highlight mark, conflicted/added/deleted flag variants, split-index and signed sandbox execution remain under audit. This is partial status-list parity, not completion of all flag action masks or dialogs.

Checked-file commits during pending operations

The pinned CommitDlg.cpp prepares checked entries against HEAD, resets unchecked entries for the selected commit tree, and invokes ordinary git commit; its merge condition permits a commit without checked changes. New branch is disabled during a merge, with the merge-active indicator visible. The old native checkbox path rejected active merges and used --only, which Git also rejects during a cherry-pick.

commitOperation() reads Git-resolved MERGE_HEAD/CHERRY_PICK_HEAD/REVERT_HEAD locations. Checked-file mode now uses the existing selected temporary index against HEAD and runs normal commit against that index. Git retains merge parents or the cherry-pick author/date and clears the operation state on success. The real index stages checked whole-file contents and keeps unchecked entries. Git hooks see the selected temporary index. A rejection retains HEAD and the operation; checked contents can remain staged, as in other checkbox paths. The working tree is not reset. Core preflight rejects amend/new branch during pending operations.

Native Commit shows the operation indicator with original command artwork, disables new branch/amend, and enables a merge commit with no checked file changes. Settings and menu/progress parity remain partial. The later conflicted selection family supersedes this section's original unresolved-conflict limitation: OK/Ignore is mapped, and Ignore allows source checkbox preparation of current conflict content and comparison-base resets of unchecked tracked conflicts. Staging support still commits its actual index, so unresolved stages can make Git reject that mode.

Thirty-four Commit regression tests pass. New cases verify exact merge parents, selected whole-file bytes, retained unchecked index and later working edits, cherry-pick author/date, revert single parent and state cleanup, branch preflight, hook rejection and an empty selected merge tree. Native QA committed checked.txt while unchecked Unicode-path staged/working versions stayed intact, verified both parents and cleared MERGE_HEAD. Both unsigned builds and resource audits passed. The QA app quit normally and the final process scan was empty.

Actual native resolved-merge Commit

See recorded acceptance. Native cherry-pick/ revert and hook-failure UI, operation changes while open, dark/narrow layouts, linked worktrees, octopus merges, multi-step sequencers and signed sandbox execution remain pending. This is not proof of full Commit dialog parity.

Issue field and preflight warnings

The configured issue label/field now occupy the upper-right branch row, matching IDD_COMMITDLG's BUGIDLABEL/BUGID placement. Configuration precedence, simple template extraction/insertion, C++ regex detection, numeric validation, and missing-issue/template/sign-off warning order are mapped. Native cancellation preserves HEAD/index; native commit acceptance verifies the inserted issue line, sign-off, selected working contents and retained unchecked index/working edits. Dark-mode acceptance verifies seeded ID extraction and initial issue focus. See full audit and remaining gaps and control evidence.

Checked-file Message only now excludes checked paths from changelist removal; staging mode still commits indexed paths and uses them for cleanup. Native changelist cleanup combinations remain under audit. A preflight rejection no longer offers saved-copy restoration before a commit has been attempted.

The editor now uses the upstream UTF-8 byte regex styling algorithm separately from Commit's UTF-16 ID validation. Matched context is bold, IDs bold italic, and configured tracker targets open through native link clicks. Native checks verified Undo, no stale links after replacement, Recent-message issue updates, retention for messages with no ID, and unchanged issue fields after revision insertion. Light/dark captures and preserved HEAD/index/working contents are recorded in link acceptance. See algorithm mapping and remaining editor gaps. Full incremental SciEdit behavior, provider plugins and signed sandbox invocation remain pending; this is partial Commit parity.

URLFinder's punctuation/bracket scanner and its upstream fixtures are now ported for ordinary URLs and email addresses. URLs override issue styles in the same order as SciEdit, splitting any affected ID hotspots. Native checks without tracker configuration verified regular-font links, email targets, plain SSH addresses, Undo, replacement clearing and exact local file opening. See source mapping and limitations and recorded native acceptance. Unusual Windows scheme classification, incremental styles and full editor parity remain pending.

Marker formatting

TortoiseGit's per-line *bold*, ^italic^ and _underlined_ scanner and its overwrite order are now mapped to native attributed text. The default-on Style commit messages preference appears in the native Commit settings tab; changing it restyles open drafts, retains Undo and leaves links enabled. Native light/dark acceptance and a real commit verify that literal markers and message contents remain intact. See source mapping and classification gaps and recorded evidence. The source supplementary-character boundary quirk is retained. Exact Windows locale classification, incremental editor state, spelling/snippets and full Advanced settings remain pending.

Filename completion in the message editor

Displayed filenames and path suffixes now feed a native completion popup with the original file icon. Automatic/default-minimum and manual keyboard requests, unchecked unversioned candidates, saved preferences, extension-free names and Undo have native acceptance evidence. See completion audit for source mapping, screenshots and the remaining code/snippet/spelling work. This does not establish complete SciEdit or Commit parity.

User/shipped snippets now use the original snippet icon and source parsing, priority and word-expansion rules. Native multiline/Undo/collision/refresh acceptance is recorded in the completion audit. Code symbols are now integrated as described below; complete decoding, spelling and full editor behavior remain pending.

Keyboard dispatch now gates automatic completion on typed insertions, with native Tab/Shift-Tab focus routing. Builds and completion/snippet regressions pass. Initial keyboard QA was interrupted by a locked Mac; subsequent native arrow/deletion/typing/paste and Tab/Shift-Tab checks passed during code-scanner QA. Complete keyboard, spelling and input-method acceptance remains pending. See the completion audit.

Code-symbol completion in the message editor

The changed-file scanner now feeds the Commit popup alongside filenames and snippets, using original code.ico artwork. Source definition loading, file gates, raw UTF-16 decoding/capture transport and row insertion priority are implemented. Native Tab/mouse acceptance, snippet collision, Undo, unchecked-file contribution and F5 rescanning were verified. See code-symbol audit for evidence and remaining visual, decoder and signed sandbox acceptance.

Native cancellation follow-up at 07e8895: No retained the unchanged dialog; Yes closed it normally for an empty message with three changed files, staging off and no restore copies. Normal Quit also passed later with a Log unified viewer open. The previous intermittent close/inspection timeout did not reproduce; no root cause or speculative code fix is claimed. See acceptance record for exact scope and remaining changed-message, restore-copy, suppression and signed cases.

Shared unversioned-file preference

Show Unversioned Files now defaults to enabled in both Commit and Log, matching upstream AddBeforeCommit. Both dialogs read and save the same application-wide preference, so reopening either dialog uses the last choice made in the other. Existing open dialogs retain their own current switch state. Log updates loaded working rows without a history reload; Commit filters its loaded status list. Busy/closing guards refuse preference changes. The native receiver uses a private preference domain and real tracked/untracked files to check default visibility, hide/show, reopen in both directions and exact repository preservation. See the shared unversioned QA record.

Visible checked rows form the non-staging commit selection

The non-staging Commit button, checked-file count and commit request now use the checked rows currently shown in the file list. This matches upstream WriteCheckedNamesToPathList, which iterates displayed list items. Hiding unversioned files or restricting the project scope can no longer silently commit cached checks outside that view; ignored and stale paths are excluded too. The cached checks are retained so revealing a row restores its checkbox. The checked-file clipboard already uses the same visible-row filter. Staging mode continues to commit the whole index, independently of checkbox state.

A native fixture makes an actual scoped tracked-file commit while hidden checks include unversioned, out-of-scope, ignored and stale paths. It checks committed paths, the untracked path’s absence from the index, unchanged outside index bytes and working-file bytes, then makes a full-index staging commit outside the scope. The fixture also verifies button eligibility and reveal/Whole Project behavior. Run it with scripts/check-cherry-pick-native.py --commit-selection-only; it is included in the normal native checks as well. See the visible-selection QA record. These model/real-Git checks do not prove displayed gestures or signed sandbox execution. Full Commit acceptance remains incomplete.

Checked dirty-submodule preflight

Before preparing the parent index, Commit now checks selected visible directory rows for dirty child repositories, matching CCommitDlg::OnOK. The native sheet uses the source warning and Commit, Ignore, Cancel choices. Commit hands the validated child checkout to a separate native Commit window and stops the parent attempt; Ignore continues to the next warning, then the parent commit; Cancel retains the parent draft. It never commits child files automatically.

Existing gitlinks use the source working/index diff's -dirty suffix, including staged child edits and honoring Git's diff ignore configuration. Untracked-only child changes and new child commits alone do not produce that marker. An unversioned nested repository uses its own status, including untracked files. Missing/uninitialized child checkouts are not initialized. Paths are literal, deduplicated in display order and validated before handoff. Staging mode checks visible staged rows; Message only skips these warnings, as upstream does.

Native workflow evidence records Cancel, child handoff, Ignore, staging and Message only across four Git versions, with original parent/child bytes preserved for refused attempts. This proves programmatic model decisions and actual Git effects. Displayed sheet buttons, actual child-window focus and parent refresh after a child commit, multiple prompt sequencing, scoped staging combinations and signed sandbox handoff still need acceptance. Full Commit and application parity remain incomplete.

Automatic-selection and history settings

Settings → Commit now exposes the source Select items automatically (on), Do not auto-select "missing" files (deleted, but unstaged) (off), Max. items to keep in the log message history (25) and Timeout in seconds to stop the auto-completion parsing (5). Native steppers use the same Settings Dialogs 2 validation range, 1–100. The completion timeout uses the existing code scanner preference. macOS settings save immediately; there is no Windows Apply step.

New Commit dialogs capture the automatic-selection and missing-file preferences. Automatic selection excludes unversioned descendants, ignores the ignore-on-commit changelist, respects the submodule switch and skips conflicted rows. A missing row is a deletion in the worktree; an already staged deletion remains eligible. Explicit file paths from the entry point override automatic selection and missing exclusion. Selecting a folder does not make every child a direct selection. Refresh preserves existing manual checks. ReCommit enables automatic selection for the next attempt, as CAppUtils::Commit does, without changing the saved preference. Replay split selection retains its forced-on rule.

The history limit is read when message history is initialized; changing it affects newly opened dialogs. The existing persisted Commit.MaxHistoryItems key is retained. Native QA records actual status rows, direct-file/folder distinctions, manual Refresh, saved preference snapshots, two-entry history and a real ReCommit. Hidden settings layout is not physical control acceptance. Displayed checkbox/stepper input, keyboard and VoiceOver, scope/missing combinations involving renames, full replay selection, completion timeout UI changes and signed sandbox acceptance remain pending. The remaining Dialogs 2 controls are still outstanding; this does not establish full Commit parity. Comment stripping is described below.

Commit-message file formatting and comment stripping

Settings → Commit now includes Strip lines starting with "#" in commit message, off by default. The shared message-file formatter follows CAppUtils::SaveCommitUnicodeFile. When enabled it reads core.commentchar, defaults an absent/empty value to #, and removes lines starting with that exact UTF-16 prefix. Indented later lines remain. The pinned source treats auto as a literal prefix; this port retains that behavior rather than interpreting it as Git's automatic comment-character selection.

The existing Advanced SanitizeCommitMsg option now controls the formatter, on by default. It trims outer ASCII spaces/CR/LF from the draft (not tabs), trims trailing spaces/CR from each output line, collapses runs of blank lines to one and drops trailing blank output lines. With sanitization disabled, line endings and trailing spaces/CR are still processed as source does, but blank lines are kept. Each written line ends in LF. Ordinary comments remain when stripping is off. Git's configured final cleanup can independently alter the supplied text.

The visible editor retains the user's raw draft and Undo. Recent messages retain the separately prepared, outer-trimmed attempt, including stripped comments; Git receives the formatted contents. The connected result-lifecycle family fixes the earlier conflation of those buffers: client PreCommit output, issue insertion and file serialization do not overwrite the editor. Commit formats after its preflight prompts, before calling the existing selected/full-index engines. Rebase edit, squash and checked-conflict continuation consume the same formatter. Comment stripping also bypasses the source conflict-hint warning because those comment lines are being removed. The existing Core APIs remain explicit and do not read global app preferences themselves.

QA evidence covers exact formatted bytes, ordinal Unicode prefix comparison, custom prefix configuration and read-only preparation. Native actual commits with commit.cleanup=verbatim verify default comments, enabled/custom stripping, sanitization on/off, retained draft/history and stripped-empty refusal. A real Rebase edit pause verifies continued message bytes and unchanged file contents. Programmatic model and hidden settings layout are not displayed checkbox or physical keyboard evidence. Uncommon-codec and Windows best-fit acceptance, full NUL/tab-only validation, other SaveCommitUnicodeFile callers, complete squash/checked-conflict combinations, displayed controls/VoiceOver and signed sandbox execution remain pending. The full port remains incomplete.

Encoded commit-message files

Commit and Rebase now send messages through private -F files rather than UTF-8 process arguments. The encoder reads i18n.commitencoding and preserves all 156 ordered aliases from UnicodeUtils::GetCPCode; the first case-insensitive match wins. Missing/empty/unlisted names retain the source UTF-8 fallback. Recognized code pages use installed CoreFoundation codecs and per-line conversion, without a BOM. Replacement uses ?; exact Windows best-fit tables remain under audit. Recognized codecs unavailable on macOS report an error. UTF-16/32 aliases are rejected because these Windows wide-character API code pages are not supported by this message-file conversion path.

Each owned temporary directory is mode 0700 and its message file mode 0600; success and failure remove it. The same transport is used by checked-file and full-index commits, parent/separate-index amendments, rebase edits, squash continuation and the shared ordinary commit API. Encoding is prepared before mutating a real branch/index. Git still owns its configured final cleanup and encoding header; the native draft and message history retain Unicode text. Ordinary commit APIs retain their former final-newline behavior. The raw squash continuation API retains the exact supplied ending under verbatim cleanup; the native formatter already supplies its source-compatible final newline.

Log, Blame history, comparison and submodule metadata readers explicitly request UTF-8 from Git so configured legacy output does not become replacement glyphs in native strings. Our Rebase commands temporarily request UTF-8 log output for the generated todo/editor metadata. The paused message file itself remains in commit encoding and is decoded accordingly; the durable squash-editor configuration carries that encoding, with backward-compatible UTF-8 for old records.

Encoding QA records fixed byte vectors, private-file permissions/removal, actual commit paths and legacy Edit/Squash continuation. This is partial encoding parity. Full Windows best-fit/default-byte behavior, all recognized codecs and stateful encodings, externally started or encoding-changed Rebase sessions, mixed-encoding author headers and RefLog subjects, non-UTF-8 templates and other message-file callers still need audit. Physical dialogs, signed sandbox execution and the full application port remain incomplete.

Conflict-hint warning before Commit

Commit now calls the same AppUtils::MessageContainsConflictHints detector as Rebase, after missing-sign-off handling and before issue-message insertion, submodule preflight, message-file preparation, branch creation or staging. The source pattern is a newline followed by the configured comment prefix, Conflicts:, another newline, and the prefix plus a tab. The match must occur after the beginning of the message. Missing core.commentchar uses #.

The native sheet provides Ignore, Abort (the default button), and Do not show again. Only Ignore remembers suppression, under the same CommitMessageContainsConflictHint preference used by Rebase. Abort retains the draft and stops the attempt before Git/index/branch mutation or history updates. A sign-off added at the preceding prompt remains in the draft if the user then aborts at this warning, matching the upstream editor update. StripCommentedLines and source core.cleanup values verbatim, whitespace and scissors bypass the warning. This intentionally follows upstream's core.cleanup lookup rather than inferring behavior from commit.cleanup.

Conflict-hint QA records native model callbacks with actual checked-file/staged commits, canceled message-only amendment, remembered Ignore, custom prefix, comment stripping, cleanup exemptions and unchanged repository/draft/history on Abort across four Git versions. These are programmatic native-model/Git checks. Displayed sheet, keyboard/VoiceOver, all preflight combinations and signed sandbox acceptance remain pending; the full Commit and application port remain incomplete.

Owned Commit progress and post-actions

After preflight and message preparation, Commit now opens an owned resizable native progress sheet. The captured checked paths, staging mode, message/options and completion action execute once; the Commit owner remains locked until the result closes. Ordinary successful Commit offers Push, Pull, ReCommit and Create Tag in source order, with original artwork. Close finishes without a follow-up. Push and Tag open their native dialogs; Pull requests its source post-commit Push follow-up. ReCommit resets the draft/options and refreshes automatic selection. The footer ReCommit and Commit & Push automatically close successful progress and preserve their original behavior. A failed command has no success actions; closing progress retains the Commit draft for correction and retry. If branch creation succeeded before a hook failure, the new-branch controls reset and retry uses that selected branch, as upstream does. An invalid uncreated branch retains its input for correction.

Cancellation stops the owned Git process group, including hook children. The source ConfirmKillProcess preference offers Yes/No with Yes as default. No leaves the command running. Accepted cancellation closes progress after cleanup and retains the Commit owner/draft. It does not undo branch/index changes already made before cancellation. Optional cancellation reaches ordinary checked-file, full-index, parent/separate-index commits, branch creation, staging and staged mode restoration. Existing callers retain their nil-token behavior.

Rebase split Commit suppresses post-actions and closes successful progress, but its cancellation is temporarily disabled until sequencer metadata can be audited after an interrupted commit. Ordinary progress does not show the message-file command, as upstream disables command display. Ordinary checked-file and staged commits now stream their final Git commit command output; split replay and preparatory metadata/staging output remain separate. A Git error is presented in progress; the owner does not repeat that error after Close. Saved-copy restoration and changelist cleanup still run after result acknowledgement, and their separate failures retain their existing owner warning.

Progress QA records relevant Core regression checks and native models with real repository effects. Native window controllers always supply the progress presenter. Headless callers without a presenter acknowledge results immediately, preserving earlier programmatic tests; the new receiver supplies the presenter hook and explicitly checks retained production result semantics. Physical progress/sheet/buttons/default/Escape/resize/theme/ accessibility, native destination factories, signing/credentials, git-svn DCommit, app hooks, split cancellation and signed sandbox/Finder/App Store acceptance remain pending. Existing screenshots predate this progress sheet. Full Commit, application and distribution parity remain incomplete.

Remembered Commit split-button action

The main button now displays the last selected Commit, ReCommit or Commit & Push action and repeats it on click or Command-Return. The menu changes that current action when starting an allowed attempt. The source application-wide CommitLastAction values are retained: 0 = Commit, 1 = ReCommit, 2 = Commit & Push. Missing and out-of-range values display Commit without rewriting the preference during load. Newly opened dialogs read the saved action; existing windows retain their own selection.

Persistence occurs when progress finishes and is acknowledged, including a failed command. Aborted preflight questions do not save the changed action. Choosing ReCommit from ordinary Commit's progress does not change its footer action. Rebase split Commit forces the plain Commit button, blocks footer post-actions and leaves the saved action untouched, matching PrepareOkButton's no-post-actions branch. Explicit programmatic commit(action) retains its existing meaning; the native button calls commitCurrentAction.

Action QA records native model and actual Git checks for fallback, reopening across repositories, existing-window snapshots, repeated Push/ReCommit, result-acknowledgement timing, hook failure, preflight Abort and an actual paused Rebase split commit. These checks do not establish physical split-button/menu/default or keyboard acceptance. The upstream m_bAutoClose field is a caller flag, not a new user-facing setting. Full Commit/dialog persistence, progress settings, physical/signed acceptance and full application parity remain incomplete; existing screenshots predate the changed dynamic button label.

Settings → Dialogs now exposes AutoCloseGitProgress. Its three source policies apply to ordinary Commit results after their post-actions are assembled; failures remain open. Footer/Rebase overrides are preserved. See progress close policies for scope and remaining acceptance.

Shared history picker row identity

Commit and Merge use the same native history table. Selection is stored as row indexes, matching HistoryDlg.cpp rather than treating message text as identity. This prevents two Merge messages with canonically equivalent Unicode spellings from selecting each other. OK joins selected messages in table order with two newlines. Delete removes one row immediately from persisted history and selects the next available row; deleting the final row leaves an empty selection. The list receives initial keyboard focus, and both sheets attach to the shared source HistoryDlg geometry setting after establishing their default/minimum size.

After a Debug build, python3 scripts/test-history-picker.py verifies actual hidden native tables, binding synchronization, exact selected UTF-16 text, multiple selection, deletion through empty and initial focus for both backends. It also checks a hidden shared-identifier geometry close/reopen/reset fixture. This does not establish physical keyboard/double-click/button/sheet interactions, visible light/dark appearance or signed acceptance. The record is history picker QA.

Message editor font

Commit and Merge now share configurable native font family and size, with live updates that preserve text, selection and undo history. Bold/italic styling uses the selected base font. See Merge message font notes for source defaults, native differences and pending consumers.

File-list column sorting

Commit now exposes native sort headers for Path, Extension, Status, Lines added and Lines removed. The source OnHdnItemclick/CSorter behavior is retained: changing the column starts ascending, reversing it reverses the full comparison including the path tie, and only one column is retained. Paths/extensions use numeric, case-insensitive Foundation comparison in place of Windows StrCmpLogicalW. Status uses the displayed action label; line counts use integers rather than their formatted strings, with absent statistics before binary statistics before ordinary counts. Native collation is not asserted to match every Windows locale/policy edge case. Byte-distinct canonical/case ties have a deterministic Core order; full path-identity acceptance remains pending.

Sorting occurs before files enter their existing groups, so Modified, Not Versioned and changelist header order and membership stay fixed. The file IDs, checked paths, highlighted selection and focus anchor stay attached to their paths. The same column/direction stays selected across refresh and checkbox/ staging modes; staging uses the current staged or unstaged statistics. Busy and Quit confirmation refuse sort changes. No preference is written for the order. The original status colors, icons and file commands remain on the sorted rows.

Column sorting QA records Core comparison/group/clipboard regressions and actual hidden Commit tables. The receiver uses each native header's sort descriptor prototype and the table data source callback to exercise its SwiftUI binding, then verifies rendered interaction-probe row order, groups, checked/highlighted/focus identity and repository bytes. No mouse/key events are synthesized. Physical header arrows/ click modifiers, light/dark screenshots, accessibility, optional source columns, completion catalog order and other status-list consumers remain pending. This is a Commit sorting increment, not full Commit or application parity.

Optional file columns and header choices

The native Commit table now also offers Filename, Last modified and File size. They start hidden, matching CommitDlg's source default mask. Right-click the native header to choose columns. Path and the checkbox remain visible; Path is omitted from the choices, matching ColumnManager::AddMenuItem. The current native order and widths are also retained across SwiftUI redraws within the open window; this prevents busy/confirmation updates from silently reordering a user-arranged header. Visibility is saved in versioned native preferences and restored in newly opened Commit dialogs. Existing windows retain their own loaded choices. A schema mismatch restores source defaults without rewriting settings merely on load.

Metadata is read once per refresh on the repository actor, after the App Store lease gate. The shared location check blocks invalid paths and escaping parent symlinks. Final symlinks display their own metadata; missing/unavailable paths show a dash, directory sizes are zero. Date and byte text use macOS formatters. The optional columns sort by basename, date and raw byte count with path ties. Missing dates precede valid dates, including pre-1970 dates. Metadata reads do not open symlink targets or alter repository content.

Reset columns asks the source Yes/No question in an owned native sheet. No retains choices and layout. Yes restores default visibility and the current table's initial column order/widths. The Commit owner stays busy until the answer; stale visibility/sort/reset actions are refused. This increment originally saved visibility; the saved-layout follow-up below also persists column order and adjusted widths. Complete source autosizing remains pending. Native moved-column identity is tracked independently of indexes so the clicked-column clipboard route remains aligned. Copy All now emits only visible column headings and values, including the same date/size text displayed in the table. Other consumers keep their original five-column clipboard default.

Optional columns QA records Core tests and the strengthened hidden native Commit receiver with all four Git engines. It checks real header menus and targets, saved choices/reopening, native hiding, injected Reset No/Yes and owner locking, moved-column identity, customized width retention and startup-width reset (180 versus a later 215.5-point layout), all eight sort bindings, groups/selection/checks, metadata clipboard text and repository byte preservation. Native move/resize plus the production capture method exercise restoration; pointer tracking itself is not exercised. Physical header tracking/confirmation/keyboard/ resize, screenshots, signed lease behavior, source autosizing and LFS Lock remain pending. Earlier screenshots show the source default columns and are not evidence of these new optional controls. Full Commit and app parity remain incomplete.

Saved Commit column layout

Commit now saves the full native column order and only user-adjusted widths, alongside visibility in its versioned column preferences. Existing visibility-only records remain valid; absent layout fields use the source/native defaults. Unknown columns and duplicate order entries are removed, missing known columns are appended, and nonpositive/nonfinite widths are ignored. Stored widths are bounded and clamped to each native column's constraints when restored. Widths use macOS points; Windows registry/DPI pixel values are not imported.

A newly opened native table restores the saved order, widths and visibility. The leading checkbox stays separate and leading; header choices still protect Path. Current windows retain their own state. Sorting remains attached to column identity; clicked-column clipboard lookup and Copy All use the displayed order. Reset No retains the layout; Yes clears adjusted widths and order as well as visibility and restores the table's native defaults. Busy/Quit confirmation reject layout persistence and reset changes. Unadjusted columns retain their native widths rather than saving every autosized value as a user adjustment.

Saved layout QA records preference migration/normalization tests and actual hidden native table reopening, direct AppKit move/resize followed by the production capture method, copied visible headings, Reset and repository preservation across four Git engines. Pointer notification capture, physical dragging/resize, autosizing, changed fonts, multiple displays/DPI, themes and signed acceptance remain pending. Full ColumnManager, Commit and application parity are still incomplete.

Commit column fitting

Unadjusted visible columns now fit their headers and displayed file values, including rows outside the viewport. A divider double-click fits file contents and saves an adjusted width. Shift-double-click clears that column's adjustment and restores automatic header/content fitting. Refresh and visibility changes recalculate unadjusted widths while preserving adjusted widths. Group headings do not affect individual file-column measurements. The Path column includes padding for its original status icon. Native fonts, points and minimum/maximum constraints replace Windows list-view metrics. The formerly fixed Extension, Status, Lines added/removed and File size columns now allow width adjustment.

The existing native input observer routes divider gestures without replacing SwiftUI's table delegate or header. Checkbox/hidden columns, busy operations and Quit confirmation reject fitting. Reset still restores order and source default visibility, with visible unadjusted columns automatically fitted; hidden columns retain native startup widths until shown.

Column fitting QA records direct production fitting, actual native widths and divider hit geometry, content-only versus header/content sizing, saved adjustment removal, offscreen rows, refresh retention and operation locks. These checks do not prove physical pointer tracking, platform font/display variants or visual acceptance. LFS Lock, other shared-control consumers and full Commit/application parity remain incomplete.

Exact status row color roles

Commit's text columns now share the pinned default CColors roles and source combined action priority, including rename/copy and type-change distinctions. Line counts follow the row action instead of a fixed blue. Selected text stays semantic primary across all text columns. See Appearance and status color QA; current pixel/contrast and custom status color settings remain unverified or pending.

Message caret indicator

The native AppKit Message status label now follows CommitDlg::OnScnUpdateUI: one-based logical line/column (1/1 initially) below the editor at the right, replacing the port's unrelated character count. The Changes group caption includes the existing F5 refresh shortcut. Logical columns count Unicode scalars, not Swift grapheme clusters or UTF-16 code units, and use Scintilla's default eight-column tabs; CR, LF and CRLF line handling follows the pinned Document code. AppKit paragraph tab intervals follow eight base-font space advances and update with font settings. Canonically equivalent spelling changes are applied byte-exactly to the editor and styling cache.

Selection and text notifications update a coalesced native indicator without publishing inside a SwiftUI view transaction. Ordinary forward/backward extension keeps a tracked anchor; unrelated programmatic ranges establish a start anchor/end caret. Apple's selection affinity documentation describes wrapped-line placement, not a public active-selection endpoint, so the implementation does not infer endpoint direction from affinity. Physical mouse/word/multiple selection, wrapping/IME, glyph-layout equivalence and signed execution still need broader checks.

The compiled-source oracle scripts/test-message-caret-oracle.py compares 3,646 scalar-boundary positions against unchanged pinned Scintilla Document/CellBuffer code. Platform diagnostic functions alone are supplied by the runner; assertions abort. This proves logical coordinate math for those cases, not physical Windows/AppKit rendering or selection tracking. See docs/qa/commit-caret-2026-10-09.json for native/build evidence and limits. Full Commit/app parity remains incomplete.

New-branch focus and selection

The pinned CCommitDlg::OnBnClickedCheckNewBranch hides the destination label, shows the new-branch edit control, focuses it and sends EM_SETSEL(0, -1). Commit now uses a native AppKit branch field that makes the attached window's field editor first responder and selects the whole UTF-16 draft once when the field is inserted. Ordinary model updates do not repeat selection. Disabling and re-enabling the option retains the draft and selects it again. A removed or disabled field cannot claim focus through the queued attachment request.

python3 scripts/test-commit-branch-focus.py exercises the actual full hidden Commit dialog, including native text replacement, caret preservation, Unicode selection length and off/on draft retention. Evidence and limitations are recorded in branch focus QA. Physical checkbox activation, tab navigation, default-button behavior, signed execution and full dialog/application parity remain unverified.

Selectable read-only author identity

CCommitDlg::OnBnClickedCommitSetauthor sends EM_SETREADONLY to the existing author edit control, then resets its contents from the configured identity or HEAD when amending. The port previously disabled the field when Set author was unchecked, which also prevented ordinary selection/copy access. It now uses a native AppKit field that keeps the identity selectable while read-only, enables editing when checked and follows the inherited busy/confirmation lock. The active field editor's editability changes with the checkbox, and native change notifications update the author binding only while editing is allowed. Ordinary unrelated updates retain the selection. The existing configured/HEAD reseeding logic remains in authorChanged.

The hidden full-dialog receiver scripts/test-commit-author-field.py checks native field/editor flags, Unicode identity selection and copying to a private pasteboard, replacement typing, selection retention, configured-identity reseeding after unchecking and coexistence with the new-branch focus/select-all behavior. The receiver uses the field editor's advertised writablePasteboardTypes, matching native Copy negotiation: its legacy NSStringPboardType is not the same raw type as .string. See author field QA. Physical keyboard/IME, amend-load races, accessibility navigation, signed execution and complete dialog/application parity remain unverified.

Amend metadata transitions and asynchronous replies

The pinned OnBnClickedCommitAmend calls OnBnClickedCommitSetDateTime and OnBnClickedCommitSetauthor in both directions. Turning Amend off therefore reinitializes a checked author date to current time as well as restoring the configured identity. The port previously retained HEAD's old author date in that case; it now refreshes both defaults. Non-amend and amend message drafts remain separate, and a cached amend draft is reused without another initial-message fetch, matching the source's empty-cache condition.

The Windows handlers read Git synchronously. Native asynchronous reads now carry independent message/author/date generations so older replies and errors cannot overwrite the current choice or clear a newer request's loading state. Draft comparisons use exact UTF-8 identity for author text, preserving a deliberately replaced canonically equivalent spelling. Commit is unavailable while the selected defaults load; the affected author/date/message control temporarily rejects editing, while a read-only author remains selectable/copyable. Replay Split installation invalidates ordinary metadata requests and suppresses only the notification produced by installing caller-supplied author/date presets. Later user checkbox changes still run the ordinary configured/HEAD default handlers.

The receiver scripts/test-commit-metadata.py hosts real hidden Commit dialogs and exercises actual Git metadata, date restoration on unamend, controlled out-of-order replies/errors, pending Commit/editor/picker gates and cached on/off/on message transitions. Supplied Replay Split control-state fixtures verify that first author/date values survive obsolete ordinary replies and real installation notifications, while later checkbox changes still refresh defaults; they do not execute a real rebase. See metadata QA. Physical Amend/message-focus/keyboard handling, process cancellation on close, moving HEAD, signed execution and full dialog/application parity remain incomplete.

Amend returns focus to Message

The pinned OnBnClickedCommitAmend calls IDC_LOGMESSAGE->SetFocus() after refreshing author/date defaults and before the status refresh. Native Commit now requests message focus on both toggle directions and on an active initial-message read error. The editor performs the handoff after asynchronous defaults/refresh finish, outside the SwiftUI representable update. It does not select the whole message. Ordinary updates preserve another control's focus, message selection and existing Undo.

The dialog model owns both request and acknowledgement, so rebuilding the editor does not replay a consumed request. Busy/default reads, Quit confirmation, an outstanding error and an attached sheet defer the handoff. An owned didEndSheet notification retries pending focus without requiring an unrelated model update. Removed editors invalidate their coordinator/observers and queued caret publication. Changing the editor's model resets coordinator ownership and invalidates old caret work. The real Commit controller invalidates pending focus when its window closes; late Git replies cannot create a new handoff. Installing Replay Split presets also retires an ordinary pending request. This does not cancel all Git work on close.

python3 scripts/test-commit-amend-focus.py uses real unordered native windows with activation prohibited. It verifies both toggle directions, default/refresh completion, ordinary focus/selection/Undo retention, consumed-editor recreation, removed-editor cancellation, editor-only error gates and a late reply after the real controller closes. Sheet ownership is injected into a test window and the real notification path is posted manually; no physical sheet is presented. See Amend focus QA. Physical checkbox/keyboard/IME, actual error-alert/sheet transitions, signed execution and full dialog/application parity remain incomplete.

Commit progress presentation

Commit progress now uses the shared native ProgressDlg output view: selectable read-only text, Copy/separator/Copy All menu with original icons and menu-icon preference, source warning/error prefix colors, completed URL/email links and success/failure footer colors. Displayed output is bounded by the captured GitOutputLimitinKiB preference and shared CR/UTF-8 parser; the original repository result remains available to completion callbacks. Git failures display their message rather than repeating the command-name wrapper.

The current-work label reports Success, User cancelled, Git exit status or native Operation failed. Completion appends monotonic execution milliseconds and local date/time unless ShowGitexeTimings is disabled. Locale selection uses the shared UseSystemLocaleForDates behavior. The progress bar shows completion and its result color; ordinary commit output now updates live percentage/current-work when Git or a hook emits a source-recognized progress line.

Close is the default button and remains disabled while running or answering a question. Abort/Escape cancels a running cancellable operation, closes a failed result when idle, and Close/Escape resolves a completed result. Successful Abort is disabled. Existing Push/Pull/ReCommit/Create Tag ordering and owned-result acknowledgement stay in place; post-action controls are disabled during a pending cancellation question. Rebase split cancellation retains its existing restriction.

Presentation QA records hidden native real-repository checks plus bounded completed text, links, private copy-all, completed Escape and duplicate resolution. The receiver now throws on invariant failure and cancels/awaits owned Commit operations before exit. This does not prove displayed window/sheet/button/menu/theme/VoiceOver parity. Current screenshots predate these changes; complete progress behavior and signed/App Store acceptance remain unfinished.

Live Commit command output

Optional output callbacks now reach commitSelected/commitIndex and their parent/separate-index final commit invocations without changing existing callers. Native Commit consumes a bounded AsyncStream of notifications around the shared CR/UTF-8 parser, updates output/current work/percentage while Git is running, and flushes the final parser state before completing. A streamed result is not fed back into the display again; repository callbacks still receive the original Git result. Preparatory metadata, branch creation and staging output are not streamed through this callback. Rebase split Commit retains its existing completion-only output and cancellation restriction.

Forced progress-controller closure cancels its owned token, rejects late visible output/completion/post-actions and success callbacks, and resolves the owner's result wait after backend cleanup. Already completed Git/index/branch effects are retained. Abandoned progress does not save a premature action-log entry. Late-history/restoration/changelist races and broader signed/displayed acceptance remain partial.

Live output verification records focused Core regressions and the hidden native receiver. Hooks emit Unicode text and a 42-percent progress line before pausing; ordinary/index/parent-amend paths must show both while still busy before No/Yes cancellation. Hook output appears once. A separate running-hook controller is forcibly closed and must cancel, release its owner without success callbacks, retain HEAD and expose no late footer/post-actions. These checks do not complete full Commit or app parity.

Cancelling obsolete author/date/message reads

The author, author-date and initial Amend-message reads now each own an OperationCancellation token. Replacing a read, switching Amend direction or installing Replay Split presets cancels obsolete tokens as well as advancing the existing generations. The production queries pass these tokens through every Git config/history/show call. Checks before and after the asynchronous query reject cancelled work even if an injected reader ignores cancellation.

The real Commit controller invalidates these reads on window close. Closing clears their loading flags, retires message focus, cancels completion/styling tasks and prevents fresh default-read requests or Commit from the retained model. Late metadata values/errors cannot replace the closed dialog's drafts. This is a macOS lifetime adaptation: the pinned Windows checkbox handlers read their defaults synchronously. Ordinary metadata reseeding, cached drafts and Replay preset rules remain as documented above.

Cancellation QA records the native transition checks and limits. This change does not cancel every Commit task: initial/status refresh, other file operations, history/restore/changelist work and broader forced-close handling still need a lifetime audit. Physical keyboard/sheet behavior, concurrent HEAD changes, signed execution and full Commit/application parity remain incomplete.

Refreshed displayed Commit captures

The light/dark captures at site/assets/commit-light.png and commit-dark.png were refreshed on 10 October using the current displayed native Commit dialog. They show a message above the checked file list, Amend/author/date options, category links, staging/unversioned/submodule/whole-project/message-only controls, and original colored icons for Modified, Added, Deleted and Untracked paths. The displayed split-button menu was inspected for Commit, ReCommit and Commit & Push without invoking those actions. Comparisons used the pinned upstream Commit image, the official manual and the user's supplied control/action screenshots.

Only one isolated documentation-preview process ran at a time; every owned process was closed before the next started. Appearance-menu automation failed, so the dark capture used the preview helper's preset. The fixture HEAD, status and index bytes were unchanged during these UI captures. Earlier screenshots remain historical checkpoints. See capture QA; these inspected states do not prove all option combinations, mutation behavior, other macOS/locales, accessibility or signed/App Store/full-application parity.

Owned initial refresh and cancellation

Commit's initial and F5 refresh now own a task and a cancellation token. The token reaches status, HEAD/parent/config, comparison/statistics, branch/submodule, changelist, operation, LFS and message-seed Git reads. Every asynchronous read checks ownership before and after suspension, so a late value or error cannot publish after the controller closes. Filesystem-only metadata and snippet loading are fenced at their asynchronous boundaries; they are not forcibly interrupted. Closing also cancels the current LFS lock read and retires the refresh state.

The Cancel button remains enabled during refresh while other controls stay disabled. Cancel uses the existing draft confirmation before stopping the owned refresh, then awaits its completion before the history/restore/close flow. Duplicate cancellation is refused. Declining confirmation retains the draft and lets the current refresh continue; a later refresh remains available. Late confirmation responses after closure cannot save history or invoke the close callback. The confirmation suppression preference uses the dialog's supplied defaults store.

This adapts CCommitDlg::OnCancel and StopStatusThread, preserving confirmation before worker cancellation. Physical Cancel/Escape/close/quit gestures, all refresh phases, active filesystem interruption and signed sandbox acceptance remain open. Other Commit asynchronous operations retain their separate guards and are not claimed covered by this refresh change.

The hidden refresh receiver passed with system and packaged Git. It exercises actual controller-close invalidation and the production status query against a live sleeping child, not a stubbed process. No leaves the child running; Yes reaps it before close. Successful/throwing late status replies, duplicate Cancel, declined confirmation/reload and late approval after close are checked. Exact HEAD/index/working contents are preserved. The 55 selected Core regression tests passed, including pre-cancelled read APIs. See refresh QA for the final build records.

Cancellation-pending state is published immediately. The parent controls and Commit/Cancel/Refresh gates remain blocked even if loading finishes before the confirmation is answered. No restores the controls and leaves the completed or running refresh intact. Each response callback accepts one answer, so a stale No callback cannot approve a later confirmation. The final receiver checks this completed-worker timing case, duplicate responses, and default controller close. The suppression checkbox now saves into the same supplied defaults domain read by the model; its physical activation and relaunch acceptance remain pending.

Once cancellation is approved, Commit also retires its author/date/amend reads before awaiting the refresh. This prevents its own blocked metadata child from keeping a refresh queued on the repository actor. A real default author query blocked in a child, with refresh queued behind it, was cancelled and reaped in both Git versions before the controller closed. Other windows' read owners, attached patch-view lifetimes and full multi-window liveness remain separate acceptance work. No cancels no metadata reads.

Recent messages sheet ownership

CommitDlg.cpp's OnBnClickedHistory and HistoryDlg.cpp remain the behavior reference: a selected message replaces an untouched template, otherwise inserts at the caret with a newline when needed; an existing prefix is not inserted again. The native history list flattens CR/LF only for display, retains full Unicode messages, joins multiple selected entries with two newlines in displayed order, and saves single-row Delete immediately even if the picker is cancelled.

The Commit controller now owns the history sheet through parent teardown. History and Log pickers reject competing sheets and busy/quit transitions. Parent close requests leave an active sheet alone; forced parent closure retires it and later requests cannot reopen it. Completion checks the identity of the owned sheet before inserting into the editor. These ownership changes do not alter the upstream message selection and deletion rules.

scripts/test-commit-history.py exercises the actual Commit editor context-menu action, native sheet/table and Return/Escape/Delete keys, template and caret insertion, prefix suppression, focus return, persisted history, modal fences and repository invariants. Physical mouse/key entry, VoiceOver, broader issue configuration/regex acceptance and geometry across displays and signed sandbox acceptance remain open.

The integrated receiver passed with system and bundled Git. Both runs required the final completion marker and verified exact HEAD, raw index, config and working-file bytes. It keeps windows transparent, preserves real AppKit sheet ownership, and explicitly chooses the intended text selection after querying the menu because native context-menu construction can select the clicked word. Direct accessibility-button gestures and physical right-click selection are not covered. See the QA record.

Unsigned Debug and AppStore builds and bundle audits passed for this change, including all 116 original icon resources and the required AppStore Git runtime. Owned receiver processes and temporary fixtures were absent after verification. These checks do not establish signing, physical UI acceptance or full Commit parity.

Issue field updates from Recent messages

CommitDlg.cpp::OnBnClickedHistory and ProjectProperties.cpp::FindBugID extract IDs from the selected history text, deduplicate them, use natural sorting and update the separate field only when a nonempty ID was found. The native workflow keeps those rules. GetBugIDFromLog trims trailing LF from the initial displayed message even without a matching issue line; the raw template remains separate. The history replacement branch still compares against that raw template, matching the pinned source.

History extraction now has a cancellable repository call and a window-owned task. Every new selection invalidates the previous generation. Message, issue-field or property edits cancel the pending result even if a later edit restores the same value. Parent teardown retires it. A delayed provider cannot overwrite newer input or publish to a closed Commit model. Empty/no-match results retain the existing issue field.

The native receiver enables the repository's issue field with its configured label and template, restores the raw template explicitly for the equality branch, and uses real extraction to check naturally sorted duplicate IDs. Controlled queries deliberately ignore cancellation to exercise the separate publication fences for edit-and-restore, superseding requests and parent teardown. This does not establish physical issue-field entry, direct button accessibility, full native ECMAScript configuration acceptance or tracker-plugin parity.

The 16 focused issue tests passed, including repository extraction cancellation. Integrated native checks passed with system and bundled Git, preserving HEAD, raw index, config and working bytes. Unsigned Debug and AppStore builds and bundle audits passed with all 116 original icons and required AppStore runtimes. See the issue-update QA record.

Column-menu owner lifetime, October 11

Commit column layout, visibility, sorting, reset and unversioned-display settings now refuse commands once the owner is invalidated. A Reset columns request also rechecks owner lifetime after its asynchronous confirmation returns. A delayed Yes from a closed Commit window therefore cannot save a default layout over a newer window's preferences or invoke the old native layout callback.

The focused receiver extends the existing native header/sort/layout checks with an actual hidden Commit controller force-close while an injected reset answer is pending, a replacement model using the same private preference domain and a late Yes. This checks the production window-close invalidation path and model fencing; it does not establish physical confirmation-sheet teardown or pointer interaction.

Final two-engine checks pass, including the existing header/sort/fitting/clipboard and repository-preservation cases. See the close-lifetime record for the source hashes and precise verification scope.

Dialogs 2 grouping and shared history-key continuation

The new native Settings host now moves source selection, missing-file, completion, history-limit and comment-strip controls to General → Dialogs 2 → Commit. The Message Editor page keeps formatting/minimum-character/extension extras. Shared OK/Cancel/Apply keeps drafts until applied. Commit now reads MaxHistoryItems, the source key shared with Merge, falling back to Commit.MaxHistoryItems for existing installations. Dialogs 2 migrates the earlier key when no shared value exists. This later continuation is uncompiled/untested; older native QA applies to its recorded revision, not this new grouping or shared-key migration.

Conditional SVN DCommit post-action — 2026-10-12

Compared CommitDlg's IsGitSVN-gated progress callback. Successful ordinary Commit now prepends SVN DCommit with the original Commit icon when the repository's shared Git administration directory has SVN metadata. Push, Pull, ReCommit and Create Tag retain their source order. Normal repositories keep those four actions; failed and split/combine workflows retain their existing post-action suppression. The footer choices remain Commit/ReCommit/Commit & Push.

DCommit closes completed Commit progress through the existing once-only choice path, then calls the native SVN workflow using the Commit controller's captured repository/access lease. The SVN presenter now has a scoped overload; the main window action keeps its existing current-repository gate. Background main-window refresh cannot suppress the captured post-commit handoff. This does not submit anything automatically; the existing SVN options/confirmation workflow remains.

Debug build exited 0. The expanded Commit progress receiver passed with system and bundled Git in one object-linked compilation and two reaped processes. It creates private SVN metadata for the DCommit case, performs a real checked-file commit, checks the five-action order, calls choose twice and observes exactly one DCommit callback. Normal cases retain the four-action order. Existing checked/ staged commits, captured messages/unchecked bytes, other callbacks, ReCommit/ footer behavior, hook rejection/retry/live cancellation, owned helper cleanup, bounded output/private clipboard and hidden Escape checks remain in the same receiver. No Core/full sixteen-family suite was rerun.

The receiver now shares the built-app object cache; its old script delegates to the group, and CI no longer compiles/runs it separately. Actual SVN submission, physical conditional menu activation, scoped SVN receiving-window/grant acceptance and signed distribution remain open. No new screenshot or full Commit/dialog certification is claimed.

The later scoped SVN native checkpoint verifies exact repository/executable/lease retention under another busy main-window repository, same-operation options reuse, conflicting-operation refusal and replacement after closure. It uses private Git and injected SVN responses, rather than an actual remote submission. See SVN workflow acceptance. Physical Commit menu activation and signed handoff remain open.

Attached patch window, Find and owned operations — 2026-10-13

The source PatchViewDlg.cpp, FindBar.cpp, MENU IDR_STAGING, and IDD_FINDBAR were checked at the pinned upstream revision. The partial patch window now gives its area to the colored patch, with a bottom Find bar when requested. Extra staging buttons and the comparison/header toolbar were removed from this attached editing mode; shared read-only unified previews retain their own toolbar. Staging and Unstaging app menus contain the source's selected-lines and selected-hunks commands with original icons. Their state follows the focused patch, selected lines, stage/unstage mode, busy state, sheets and retirement. A captured menu for another mode or a background/closed patch cannot apply it. The existing context commands remain available.

Find seeds from selected text, resets to the start on typing, searches forward or backward from the corresponding selection edge, and uses Match case. It does not wrap. A failed search collapses to that edge and pulses the native window border three times, replacing Windows taskbar/window flashing. Command-F, Command-G, Shift-Command-G and Escape route through the patch window even when the field owns focus. Escape first hides Find and returns focus to the patch. Read-only unified previews retain the native macOS Find interface.

Patch rendering no longer adds a newline absent from its document. Selection line mapping uses the actual line lengths and trailing-newline state. Original read-only bytes remain separate from their display text for Copy/Save/export.

Default patch readers now pass cancellation to their HEAD/diff commands with optional index writes disabled. Replacing the selected paths cancels the previous owned read. Cancel publishes pending state until its child has exited; closing a window retires it, cancels its reads, and rejects late results and refreshes. The model retains operation accounting after a forced window close, so Quit stays blocked until owned work finishes. Accepted staging mutations settle without cancellation, preserve their captured inputs and release the original parent gate exactly once; a retired child cannot start its subsequent reload/callback. Commit refreshes its live file list after a successful settlement; failed staging keeps its error visible. It passes its private preferences to the child. Other owners' custom refresh pipelines continue to own their tasks and require their separate lifecycle acceptance.

Validation and remaining acceptance are recorded in partial patch batch QA. Full Commit/application parity, unsupported partial file modes/encodings, physical keyboard/menu/Find acceptance, refreshed screenshots and multi-display placement remain open.

Added/deleted partial staging continuation — 2026-10-13

Attached partial staging now reads selected untracked files through no-index diffs without inserting intent-to-add entries. Added and deleted regular text files accept line and hunk selections. Partial unstage of an added file and partial staging of a deletion rewrite the file header as an ordinary edit when selected contents leave the file present in the index. Whole-file disappearance retains the addition/deletion header. Quoted Git paths, executable modes and no-final-newline markers are retained. Snapshot comparison re-reads the same untracked-inclusive selection before apply; unrelated staged entries and working files remain outside the cached apply.

This extends the pinned StagingOperations line/hunk workflow with explicit metadata handling for surviving partial files. Binary, rename/copy, symlink and mode-only changes remain whole-file operations. Empty files have no selectable text hunk. The grouped 16-case Core check and six native cases passed; the affected four Core lifecycle cases were repeated after the final symlink-mode guard. Native Commit receivers exercised untracked line staging, added hunk unstaging and partial deleted-file staging across system and bundled Git. See dated QA. No physical or full Commit parity certification is claimed.

Rename and regular mode metadata — 2026-10-13

The partial patch selector now preserves regular-file rename and executable-bit metadata with chosen lines/hunks, matching StagingOperations’ retained first file header. Reverse application reverses that metadata together with selected text; unselected text changes can remain indexed. The attached pane states this consequence before applying. Working file bytes, names and permissions are not changed by cached application.

Pure renames/mode changes have no text hunk and still use whole-file controls. Copy metadata, binary payloads, symlink/gitlink and nonregular mode transitions remain excluded. Fifteen grouped Core tests and six native cases passed across system and bundled Git. Native checks exercised the metadata notice, regular mode/text staging and renamed-hunk unstaging; working bytes/paths stayed intact. Debug compilation passed. See dated QA. These checks do not certify all metadata combinations or physical UI equivalence. Earlier rename/mode exclusion statements describe the preceding checkpoints.

Source-aware native spelling — 2026-10-13

Compared SciEdit.cpp::IsMisspelled/CheckSpelling at the pinned source. The native message editor now filters spelling errors for exact completion keywords, known dotted filenames, digit-prefixed identifiers and camel-case/acronym words whose longer components pass the dictionary. URL spans use the existing pinned URL finder. UTF-16 offsets retain correct behavior after emoji and other non-BMP characters. Unknown words and unknown extensions remain checkable.

Commit supplies its current filename/code/snippet completion catalog; replacing that catalog requests a spelling-only check. Merge and Rebase share the project language, disabled-language and identifier/URL rules. The global Spellchecker preference also gates checking. macOS provides dictionaries, correction menus and personal spelling data; Windows Hunspell dictionary files are not loaded. Automatic spelling, quote, dash and text replacement are disabled so suggestions do not rewrite the message. Other text-checking result types remain unchanged.

Seven focused Core tests and four grouped native cases passed across system and bundled Git, including the real Commit progress workflow. Debug compilation and two documentation tests passed; all owned receivers were reaped. See dated spelling QA. This does not certify installed-language coverage, physical spelling menus/VoiceOver, every upstream personal-dictionary behavior or full Commit parity.

The native changes list now implements the source's ordinary file and folder drop workflow, including scoped-list/check refresh and owned cancellation. Full physical/signed acceptance remains open.

The file-drop batch passed eight Core tests and four grouped native cases across system and bundled Git. See the scoped evidence.

Commit preference ownership (2026-10-13)

Commit now consistently uses its supplied preference store for Keep changelists, Do not autoselect submodules, message formatting, completion/scanner options, unedited-template Proceed suppression, and file status colors. The hosting view also supplies that store to native editor/font and pane preferences. Ordinary app controllers continue to use the standard store; private controllers no longer read/write these choices through an unrelated global store.

The submodule checkbox applies its check/uncheck effect synchronously through the model, including configured automatic initial selection. It remains unavailable in staging mode. Keep/submodule changes reject busy and retired owners; a late unedited-template sheet reply cannot save suppression on a closed dialog. The existing warning sequence and Git commit operations remain unchanged.

The Debug build and one grouped native batch passed across system/bundled Git. Native QA uses a real changed submodule and modified parent file, checks initial and reopened choices, actual 24-point editor font and formatting, filename completion extension options, private warning/Keep saves, busy/staging/retired refusal, global preference preservation and byte-exact parent HEAD/index/worktree preservation. The Commit progress regression also exercises actual commits, hooks, cancellation and post-actions. All four receivers were reaped. No Core suite was rerun for this app-only batch. Source color-store wiring is checked; rendered color/contrast and physical warning/checkbox gestures remain open. Batch evidence records the exact scope.

Connected file-menu workflows — October 14, 2026

Commit now uses captured menu ownership and cancellable file-operation lifetime for its connected status-list workflows, including pending Open With/Export/ Delete/Restore replies, staging, flags, Add and changelists. Group and nested menu actions also reject obsolete captures. A source operation cannot clear the busy state of its subsequent refresh. See the workflow audit for exact source gates, behavior and remaining physical/signed acceptance.

The formerly missing IDC_BUGTRAQBUTTON has an embedded native provider workflow: association/fallback resolution, provider text, owned issue selection, returned message/ID, pre-commit check and completion callback. See native issue-provider parity. Vendor COM/native provider ecosystem and physical/signed acceptance remain open.

Connected preview placement and viewport batch — October 14, 2026

Commit's View Patch/Partial Staging/Partial Unstaging now share the existing Log screen-aware placement and sticky-edge geometry. A preview opens to an available side or within a visible screen, reuses its frame across mode changes, snaps near a parent edge and follows parent move/resize only while docked. Moving it away leaves it independent. Minimize hides it; activation/restoration raises it without stealing the parent's focus. Ownership, busy/close guards and preference semantics remain in the Commit controller. Native left-side/screen clamping and the eight point gap are platform adaptations to upstream PatchViewDlg's right-edge frame.

Unwrapped patch text now has a measured document extent after layout and viewport resize, preserving horizontal access to long lines. Replacing the patch resets selection and scroll to the start, matching PatchViewDlg::SetText. Appearance updates retain position. Comparison initial selection/first-difference navigation waits until wrapping, text extent and a nonempty viewport are available before it is consumed. The same path handles queued edit caret and combined-pane requests; closed comparison owners retire these geometry callbacks.

The Debug build and six unique grouped native cases passed with system/bundled Git after the recorded closure-capture and fixture repairs. Passing system viewport checks were not repeated. All owned receivers were reaped; no Core suite was run. Light/dark Partial Staging compositor captures were inspected and published in the gallery. See batch evidence. These changes address the preceding placement and initial-viewport gaps, but do not certify all physical side-by-side placement, keyboard, VoiceOver, all monitor/fullscreen layouts, signed grants or complete dialog parity.

Commit preflight family — October 15, 2026

Commit now checks fresh conflict status before the new-branch and message preflight, following CommitDlg::OnOK. The warning has the source OK/Ignore choices: OK stops and selects/focuses the first conflicted file; Ignore explicitly continues with the checked selection. Checked conflicted files still cannot be committed. In checkbox mode a separate commit index uses the original tree for unchecked paths while retaining their real unmerged index stages and working bytes. This is a macOS implementation adaptation to upstream's temporary-index selection handling. A pending merge retains both parents. Staging mode continues to require a valid Git index.

New-branch names are validated before staging or checkout. Existing branches are rejected; an existing tag prompts Continue/Abort with Abort as the default. The read-only checks leave HEAD and the index unchanged. Existing issue, template, sign-off, conflict-hint, hook, provider and dirty-submodule warnings retain their sequence after these checks.

Preflight reads and issue matching share an owned cancellation token. Every awaited warning checks the owning Commit window before continuing. Closing the owner retires late replies and the progress choice, while repository access and the application Quit guard remain held until owned work settles.

Validation and remaining acceptance limits are recorded in batch evidence.

Byte-preserving legacy-text partial staging — October 15, 2026

The attached staging/unstaging pane no longer requires an entirely UTF-8 diff. GitPatch retains its original byte data and a separate one-byte carrier for selection and hunk/header reconstruction. Display lines decode UTF-8 first, then Windows-1252, with a reversible byte fallback. UTF-8 paths in a legacy-body patch therefore retain their displayed spelling. Display text never supplies cached apply or raw patch export bytes.

Selected line/hunk patches retain exact original context/content bytes, function suffixes, CRLF endings, no-final-newline markers and quoted/unquoted headers. Added/deleted file header rewriting and regular rename/mode metadata use the same byte carrier. Stage/unstage writes selected raw bytes; freshness compares the full original patch bytes. UTF-8 callers keep the existing text API, and byte consumers use selectedPatchData. The read-only unified viewer keeps its BOM handling and uses the same per-line legacy display fallback when there is no BOM.

The native partial pane now reads raw patch data directly. Save As, printing's source document and the custom patch clipboard format retain those bytes. The plain-text clipboard flavor remains decoded Unicode for text applications. Unrelated staged entries, working contents and permissions remain outside cached partial application. Binary/copy/symlink/gitlink and no-text-hunk restrictions remain; this does not add arbitrary multibyte-file editing or encoding detection. UTF-16/32 files that Git classifies as binary still need whole-file controls.

Grouped evidence records legacy line stage/reverse unstage, added/deleted executable text, undefined legacy bytes, actual attached pane rendering/export/copy and unrelated-index preservation. Physical line/hunk gestures, all legacy code pages, visual parity, signed Finder and App Store acceptance remain open. Earlier UTF-8-only descriptions above refer to prior checkpoints.

External template permission and captured loading — October 15, 2026

The initial Commit and ReCommit template paths share a captured template descriptor. Git resolves commit.template once before any permission sheet. The seed reader uses that exact URL, including a captured absence, even when config changes while the sheet is open. Template decoding, operation-message append order and ReCommit's template-only behavior retain the pinned GetCommitTemplate / LoadTextFile rules.

In an App Store build, a template covered by the active repository grant needs no additional selection. An external template opens an owned Allow access to commit template file panel naming the configured path. Select that file to authorize it. Selecting a different file does not redirect the template. Cancel leaves the template unloaded, reports that choice and keeps any existing editor draft; available operation messages remain usable. This macOS permission flow adds no source-layout checkboxes or persistent main-dialog controls.

Template grants are saved separately from repository and Git config-write grants. A later read resolves the bookmark, checks the exact file and starts a scope for the read. Stale bookmarks refresh after successful validation. Missing, invalid, moved-to-another-file or unusable grants return to the owned authorization flow. Scopes end after the read, including failures. Closing Commit cancels its panel; a late injected authorizer cannot read the template, save a grant or publish an error into the retired dialog. ReCommit also suppresses late seed errors after close.

Grouped evidence records focused Core and native checks. Native permission policy/provider injection exercises the owned panel and grant lifecycle without claiming signed sandbox acceptance. Actual Store signing, deployed bookmarks across app upgrades and physical permission-panel selection remain release acceptance work.

Shared repository titles and path-cell tooltips now consume the source Advanced preferences; see repository presentation and its grouped evidence.

Destination and issue header adaptation — October 16, 2026

The current branch is a selectable native read-only field, retaining its full value for copy and tooltip even when visually shortened. A native SwiftUI layout keeps destination and issue/provider controls in source order on one line when they fit, and moves only the issue band below at narrow widths. Long provider and issue labels retain full tooltips. Resizing retains one new-branch field/editor instance and its caret. The normal checkbox, message and file-list arrangement is unchanged. See primary presentation for current checks and remaining physical/signed acceptance.

The current connected checks and capture settings are recorded in dated native evidence.

Shared marked-row file commands — October 16, 2026

Add, Compare with base, Revert, Resolve and Ignore now follow the captured focused row rather than requiring every highlighted row to have the same status. Extended Add modes follow the focused row's actual directory metadata, including submodules. Conflict actions appear first, as in GitStatusListCtrl. The command still targets the captured complete selection and keeps the existing owner, generation and cancellation fences. See Working Tree command family for the companion context Commit/Add progress/index-flag changes and limits.

See grouped native evidence for exact checks, the scope-fixture repair and acceptance limits.

Staging activation and preview preferences — October 16, 2026

PrepareStagingSupport closes the companion patch window, changes checkbox semantics, explains staging and refreshes the list. Native mode activation now owns that sequence. Existing draft text, checked paths and Finder scope survive; the index is unchanged by mode activation. Staged paths outside the requested scope become visible using the existing staging-mode policy. View Patch and the partial Staging/Unstaging controls retain their existing mutually exclusive layout.

The source HintStagingMode choice now has an owned native informational sheet with OK and Do not show this message again. It explains the three checkbox states, whole-file checkbox actions, partial controls and whole-index Commit behavior. The choice uses the owning preference store and the existing Saved Data reset key. It appears on an enabled initial mode or activation; a normal Refresh does not repeat an acknowledged hint. An invisible/presently blocked owner defers it until the window can present it. Forced closure settles its continuation and refuses a late suppression write. At this checkpoint, individual actions still refreshed status/groups; the incremental staging follow-up below replaces that behavior and restores the source performance explanation.

Mode and patch visibility writes now share a serialized, coalescing owned queue. The latest queued visibility request wins. Each write revalidates Store access, passes cancellation to Git and retains its repository lease and Quit count until the worker returns. Commit, file checks and file actions are gated during writes and the hint. Cancel waits for the writer to drain before its ordinary confirmation flow. Owner retirement cancels the current process and drops queued writes without publishing late errors or reopening the view. Completed config writes are retained; no rollback of a completed setting or Git index mutation is implied.

A persistence failure reports the error but retains the requested live mode and draft, matching the source's independent in-memory checkbox mode. A later activation can retry; reopening uses the actual saved repository config. Preview closure and mode changes serialize their two config keys rather than racing Git's config lock. Preference reads now parse stdout separately from warning diagnostics.

Grouped evidence records build/Core/native scope and repairs. Full physical hint/checkbox/partial gestures, all amend and linked-worktree combinations, signed scopes and App Store acceptance remain open. This does not certify complete Commit parity.

Incremental staging list and whole-file targets — October 16, 2026

The source CGitStatusListCtrl::CheckEntry, UncheckEntry and UpdateSelectedFileStagingStatus change staging state independently of m_Action. Native Commit now retains the loaded entries, status colors/text, group membership, line statistics and metadata after whole-file and partial actions. An independent index snapshot drives checked/mixed/off controls, Space-key direction, All/None, category/group/multi-row checks, staged counts, Commit eligibility, hook paths and message file lists. F5 uses the existing full refresh to rebuild source rows and reset that independent snapshot. A row staged outside the initial scope remains in place after unchecking, until a full refresh rebuilds visibility.

A narrow cancellable refresh reads index/worktree path differences without rename pairing, unversioned status and staged/unstaged patch statistics without reloading preferences, metadata, hooks, completion scans or LFS locks. Its results publish together. Existing partial patch settlement refreshes the parent index state while the patch owns its own reload; whole-file actions also refresh the companion patch. List line counts use the retained whole-file statistics rather than switching between staged/unstaged counts. The staging hint now explains F5 and retained group/status values.

Raw path differences also handle identical deleted/renamed contents: a new Git rename pairing cannot make the original deletion checkbox appear unchecked.

The refresh holds input and Quit ownership through actual worker drain, including forced owner closure. Retired reads cannot publish. A failed or cancelled index refresh leaves Commit unavailable until a successful refresh, avoiding a commit based on stale checkbox state. Completed index mutations remain in place.

Whole-file rename targets now include both destination and original path when staging or unstaging; copy targets do not include their source. This follows the source's two-part rename unstage intent while using modern Git restore. Unborn unstage removes the selected index entry with rm -f --cached, retaining working bytes even when they differ from the staged blob. Other index paths are retained.

The new native fixture covers retained rows/groups/statistics/draft/scope, actual checkboxes, category/group/highlighted selections, None, partial hunk stage/unstage, the actual F5 window route, a failed-read Commit gate and forced retirement of a held read. The grouped run also retains activation/preferences and raw-byte partial patch workflows. See the validation record for actual results. Full physical input, all amend/linked-worktree combinations, large repositories, visual and signed acceptance remain open. Source unversioned rows retain their existing partial-preview exclusion until F5 changes their status. No full Commit parity certification is claimed.

Checkbox index preparation and restoration — October 16, 2026

The connected checkbox index family now supplies the source's visible unchecked-row preparation and optional add/delete/rename queues. Unchecked modifications reset in the index; default-on restoration re-adds current unchecked new-file contents after success/failure/cancellation. The preference and visible path set are captured per native attempt; staging mode keeps its exact index behavior. The older unchecked-index preservation descriptions above apply to programmatic calls without this explicit dialog policy, not the native checkbox flow. Existing explicit Ignore keeps its real unmerged stages.

The existing native progress assertions now check source-specific index outcomes for ordinary, staged and parent-amend cancellation instead of requiring every mode to preserve the original staged bytes. Working-file, HEAD, process reaping, post-action and duplicate/close assertions remain in that fixture.

Twenty-seven focused Core checks, the Debug build and six native case/engine pairs passed on the first attempt. No additional Core or native reruns were needed. All six receiver processes were reaped and independently absent. See the batch evidence for exact scope, build diagnostics and the separate hosted Log reference-checkbox failure. This does not establish full CI, physical controls or signed acceptance.

Native Commit commands, selection direction and keyboard flow — October 16, 2026

The pinned CommitDlg::PreTranslateMessage consumes Control-Return and invokes the current split-button action when enabled. Commit's native window now routes that shortcut to its own remembered action, while retaining Command-Return. Native Commit actions also offer Command-Option-Return for ReCommit and Command-Shift-Return for Commit & Push. Disabled commit shortcuts are consumed without inserting a newline or dispatching a second attempt. Return from the issue field requests message focus, matching the source field-to-editor flow. macOS Option typing is retained rather than adopting Windows Alt-letter mnemonics.

A window-scoped native Commit menu exposes the current action, source three-action submenu, category checks, message/history/revision insertion, sign-off, existing options, patch controls, Refresh and Help with original artwork. Checked states reflect the actual model. Commands capture the Commit owner, not the repository selected in the main workspace. Busy, preferences, message pickers, sheets, Quit, background/retired windows and replay restrictions use the existing model guards. Detached menu targets carry an installation epoch and cannot act after repair. Menu replacement is repaired idempotently, with structural changes deferred while owned menus are tracking. The existing Commit dialog's split button is unchanged.

CommitDlg::OnCheck also reverses direction when Shift is held: category links now uncheck only matching files on Shift-click. Shift-None checks all eligible files; Shift-All unchecks them. Checkbox mode changes the check set; staging mode applies the same direction through the existing owned whole-file index operation. Other categories, message text, worktree contents and the source's retained staging rows remain independent. Check links include native help describing the reverse action.

Add Signed-off-by now owns its identity lookup through the existing cancellable Commit operation lifecycle. Duplicate actions and Close/Quit are gated while it is pending; forced retirement cancels its token and refuses late message, focus and error updates. The configured identity still comes from the existing Core reader.

This is a native menu/keyboard adaptation, not complete Commit certification. Physical Shift-click/menu gestures, issue-field Return, keyboard layouts, VoiceOver, signing/authentication and signed Finder/Store acceptance remain open.

Grouped evidence records ten passing native case/engine runs: commands, retained staging list, history, remembered actions and focus, each on both Git versions. Control-Return made exactly one real checked-file commit and used the remembered ReCommit action. Sign-off checks cover Close-requested cancellation/retry and forced retirement. Build and fixture repairs are recorded; the failed ownership expectation did not weaken a guard. All eleven owned receivers, including that failed run, were reaped and independently absent. Core sources were unchanged and its suite was not repeated. These checks do not certify every physical gesture, complete Commit parity or signed deployment.

Native file-list refresh position — October 16, 2026

Full and incremental staging refreshes now connect the actual table to the shared owned position restore. Source preference, native multi-selection/focus, checkbox independence, filter changes and user-intent cancellation are covered by the file-list refresh family. Full reloads retain separate Quit/access lifetime after forced closure. Eight grouped case/engine runs pass; full metadata-pipeline, physical and signed acceptance remain open.

Complete refresh, initial metadata and draft publication — October 17, 2026

Full Commit refresh now collects its file/operation/configuration/message family before publishing the accepted display. Scope, checkbox defaults and metadata therefore remain paired through required-read failure/cancellation and Retry. Initial preferences/template/provider/history, optional LFS ownership, start-hook results, user draft precedence and dependent previews share this boundary. See complete Commit publication for behavior and remaining acceptance limits. Source references are CCommitDlg::StatusThread, its disabled read-time controls and initial-message/ hook lifecycle; macOS retains the prior display until the collected read settles. This does not certify complete Commit or concurrent-writer parity.

Batch evidence records twelve native case/engine passes, the fixture correction, build scope and owned cleanup.

Shared patch Find matching and ownership — October 17, 2026

The attached patch and standalone Unified Diff bars now share the pinned Scintilla case-fold table and literal UTF-16 selection matcher, including complete-scalar expansions, direction boundaries and distinct Unicode spellings. Native controls and selection/focus callbacks retain their exact form, document, registration and attachment revision. The compact row keeps all six source controls visible at the small supported width. See shared Find parity and batch evidence. The actual retained line stage/hunk unstage workflow passes with both Git engines; full physical, byte/codepage and signed acceptance remains open.

Exact changelist storage and group identities — October 17, 2026

Changelist assignments and names now use exact UTF-8 identities. Canonically equivalent spellings retain separate memberships, source groups and native menu items. Ambiguous names expose their UTF-8 bytes in help/accessibility labels. Group row IDs encode the exact group name; group equality/hashing no longer merges Swift-equivalent strings. A canonical-only assignment change invalidates captured Commit file menus. Name ordering follows UTF-16 source order.

The native metadata document writes version 2 array records, retaining exact path and name strings without relying on JSON object-key equality. Existing version 1 JSON is imported by enumerating NSString keys, and upstream UTF-8/BOM UTF-16 changelists retain exact keys/names. Mutations upgrade the native document while leaving the original upstream text untouched. Duplicate exact version 2 paths, invalid metadata/paths and unsupported versions fail without replacing the file. Locking, cross-actor reload, atomic rename and cancellation remain in force.

Scoped pruning now accepts exact path-name sets and matches directory boundaries by raw bytes; Commit captures unchecked/restored retention keys before set insertion. The compatibility assignments projection remains for older callers and cannot represent two Swift-equivalent keys; product consumers use exact lookups/storage. The wider status parser/file-table selection, metadata dictionaries, restore-copy and checked-path sets still need their separate exact-name migration. This changelist batch does not certify those wider file workflows.

The Debug build, all sixteen focused Core checks and the grouped native family pass without repair/repetition, using system and bundled Git. Core covers exact path/name persistence across actors, Codable, version 1 JSON and both UTF-16 text imports, version 2 upgrade, duplicate-record refusal, exact scoped pruning and cancellation, plus the retained storage/group regressions. Native Commit and Working Tree preserve two canonical changelist spellings, distinct group row IDs, help, captured membership changes and canonical-only Commit menu invalidation. A real selected-file commit removes its assignment and preserves both unchecked names. All four raw light/dark captures were individually reviewed; both owned receivers were reaped and independently absent. No whole file-selection migration or physical/signed acceptance follows from these checks. See batch evidence.

Commit with byte-distinct changelist names, light Commit with byte-distinct changelist names, dark

Complete physical menus, VoiceOver, upstream legacy codepages and signed AppStore acceptance remain open. Older builds that only read version 1 cannot read a native metadata document after its first successful version 2 mutation.

The working-file identity continuation now preserves distinct valid UTF-8 spellings through status/rename/statistics reads, amend joins, per-path metadata and index flags. Native row IDs and highlighted/ checked String sets still require their separate migration; this is not full filename-selection parity.

Exact Git path selection and commit preparation

The connected working-file selection family retains distinct valid UTF-8 Git names through native row IDs, independent checked/highlighted membership, marked menus, refresh, staging-state lookup and restore-copy metadata. Selected commits, checkbox-index restoration, staged modes, amend preparation and Split handoff retain the same identities. NUL path packets preserve spelling at the Git process boundary. Classic inclusion uses an AppKit checkbox alongside the existing three-state staging control.

The native programmatic replacement adapter clears the previous selection before selecting the requested rows. This addresses an empty SwiftUI binding reproduced with an isolated ASCII table when replacing a multi-selection with its subset. It is shared by refresh and group-skipping keyboard navigation. The original source layout and staging semantics are retained. See batch evidence for actual checks, failures and remaining physical/signed acceptance.

The connected workspace file workflow continuation covers exact overview selection, Finder reopening, shared viewer caches and Add/Commit drop identities. Earlier workspace-selection limitations describe the prior checkpoint; complete physical/signed and application parity remain open.

Connected native message-history continuation — October 17

The shared message-history family now uses full AppKit controls in both production sheets, exact Commit/Merge message storage, captured picker commands and owner retirement. Commit's template/draft retention, message styling and history issue extraction now also distinguish raw Unicode spellings. Earlier history checkpoints remain evidence for their recorded scope; physical/signed and complete Commit parity remain open.

Primary command bands continue the native layout with retained control identity and wrapping at constrained widths. The source command order and existing behavior remain; full dialog acceptance stays open.

Empty changes, hidden unversioned files and submodule selection — October 18

This continuation follows the pinned CommitDlg::StatusThread, OnBnClickedShowunversioned, OnBnClickedNoautoselectsubmodules, the IDS_COMMITDLG_NOTHINGTOCOMMIT* resources and CMessageBox::ShowCheck. The existing Commit checkboxes, split Commit/ReCommit/Commit & Push button, same-list staging controls and right-hand patch companion remain the established layout; this batch adds their missing connected visibility behavior.

An empty successful file list displays the source's “No files were changed or added since the last commit.” text. If unversioned files are hidden within the current scope, Commit offers the source Yes/No explanation and suppression choice. Merge state and unrelated out-of-scope files do not trigger the offer. A remembered Yes or No uses the source NothingToCommitShowUnversioned values 6 and 7, and Settings → Saved Data → Stored decisions already clears this key. Unsuppressed No applies to the current refresh; a later refresh may ask again.

Yes reveals the live unversioned rows without automatically checking them or immediately changing the shared AddBeforeCommit preference. The source saves that preference after an actual Commit attempt, including failure; an early preflight refusal does not save it. Manually changing Show Unversioned Files continues to save the preference immediately. Visibility and automatic-selection settings perform no index or HEAD mutation.

The prompt belongs to the Commit window. An invisible owner or an already attached sheet defers presentation; key-window and sheet-completion events retry it. While it is open, delayed row/column commands, refresh, preferences, Commit and Cancel cannot act behind it. Forced retirement ends the native sheet exactly once, invalidates its request and rejects a late injected answer. Its retained worker continues to count toward application lifetime until it actually returns.

Initial loading, refreshing an empty accepted snapshot, cancelling, and failed reads have distinct empty-list text. Failed refreshes retain the last accepted snapshot and diagnostics; they do not claim a clean working tree. A successful retry clears the read failure/cancellation state. Existing refresh cancellation and snapshot ownership fencing are preserved.

The source keeps Do not autoselect submodules enabled with staging support. The native control now does the same: changing it saves the policy while staging mode keeps checkbox state tied to the actual index. In classic checkbox mode the policy changes automatic checked membership. Neither setting change stages or unstages a gitlink.

The focused receiver fixture is commit-visibility-native-2026-10-18.swift. It covers actual owned Yes/No/suppression sheets, stored-decision reset, visibility and owner-sheet deferral, scope/merge exclusions, read failure/retry/cancellation, retirement and late replies, real selected Commit persistence, and staged gitlink policy without index mutation, plus native author-date bounds and independent clock behavior. Optional TURTLEGIT_COMMIT_VISIBILITY_CAPTURE exports owned empty-list and hidden-file-prompt windows in light and dark. These passed with both Git engines at the parent grouped checkpoint, including the retained staging-mode receiver. Batch evidence records the fixture compile repair and exact validation scope.

The source author-date control's inclusive endpoints are local 1970-01-01 00:00:00 and 2099-12-31 00:00:00; its separate time control has no such range. The native date control now uses those endpoints and merges its selected calendar day with the independent clock, retaining seconds. An out-of-range programmatic author-date load keeps the previous valid calendar day while loading the incoming clock, matching the source's failed date SetTime plus successful time SetTime; it does not clamp to the nearest endpoint. This follows the Windows date-picker range contract. The explicit Commit override also retains the source's pre-epoch “Invalid time” refusal. These editor rules do not restrict dates read from Git history or amend without an author-date override. The focused fixture includes both endpoints, out-of-range loads, preserved day/seconds and the actual native controls.

Remaining Commit work includes complete physical keyboard/default-button and VoiceOver acceptance, and comparison of all other upstream Commit states. This continuation does not establish full dialog, Finder, signed deployment or whole-application parity.

Owned inactive-window captures for this state: empty changes light / dark, hidden-file question light / dark. All four were reviewed for complete captions and controls. They do not establish active-window, physical input or complete Commit visual parity.

Separate Commit index engine — October 19, 2026

The Commit index engine family connects the independent source command3 mask to checkbox Commit, captures it before native preflight and prepares clean-filtered selected bytes only once. Private metadata reconciliation preserves unchecked rows/modes and owns failure, restoration and cancellation. Source defaults remain Git for this command; staging-area commits retain their raw index and partial-file behavior. The dated grouped evidence records actual checks and remaining complete source, physical and signed gates.

Missing staged-file recovery

The connected status-read recovery is tracked in missing staged-file parity, with the source three-choice prompt, per-read apply-to-rest and explicit refresh notice. Its grouped checkpoint remains separate from older dialog checks above.

Initial identity repair and local SSH signing — October 20, 2026

The Commit preparation family connects the source four-value identity check before initial status/template/Start-hook preparation, repeated owned Git Settings repair, captured request/preset preservation and ordinary effective author seeding. Local Git signing commands also inherit the existing OpenSSH response helper without changing transport classification. Its grouped evidence distinguishes actual implementation/checks from physical, signed and full Commit acceptance; older broad identity/signing statements above retain their historical scope.

Detached HEAD and cancelled draft recovery

The connected safety family follows the source Yes/Create Branch, No/detached commit and Cancel decisions, rechecks the branch after child completion and preserves issue-field contents in cancelled draft history. Its record separates native transaction placement from upstream index ordering and retains full-dialog/physical/signed acceptance gaps.

Commit result recovery and next-draft lifecycle

The connected completion family covers restoration before client PostCommit, failed-attempt message history, captured result publication and a deliberate ReCommit editor/checkbox reset boundary. The record retains source ordering, native recovery adaptations and actual validation scope separately from full-dialog/signed acceptance.

Primary conditional control follow-up

The shared conditional UI audit records the Commit split control, Log control states and Working Tree refresh/cancellation follow-up, with focused validation and remaining acceptance kept explicit.

Project message width and selected reflow

The connected message wrapping family maps the source width-dependent editor mode, edited/visible-range splitting and selected Split lines command shared with Rebase. Its implementation and checkpoint are recorded there; a configured border alone does not establish editing parity.

Scoped preparation, file-list report and Push handoff

The scope and report continuation covers the IDD_COMMITDLG whole-project reread/persistence and actual displayed checked-file report. The Push continuation distinguishes captured footer/amend and result-menu intent, explicit source mappings, owned standalone progress, fallback forms and reviewable completion warnings. The shared checkpoint records actual validation and remaining acceptance; these additions do not certify full Commit or Push parity.

Uniform report rows

The report-row continuation covers single-line captions, configured font metrics, full-value help and owned native restoration. It separates the confirmed source layout difference from the previously captured AppKit row-cache warning and its pending affected-workload checkpoint.