TurtleGit for Mac

View Markdown source ↗

Unified diff viewer selection

Baseline: TortoiseGit 7338078f8ddd924b8cddee35f512f2286072136d.

This is a partial port of the unified patch viewer setting and the Shift actions in Format Patch, Log, Commit, Working Tree and Changed Files. It does not complete TortoiseGitUDiff, external revision comparison tools, extension-specific tools or all unified-diff callers.

Source Blob Native replacement
src/TortoiseProc/AppUtils.cpp, StartUnifiedDiffViewer ad5cf29edc933f6469fb9a961b84e8251f5fc563 UnifiedDiffViewer.swift, Format Patch viewer dispatch
src/TortoiseProc/Settings/SettingsProgsDiff.cpp, GNU patch viewer controls 9cd5e7c658477fd8120a7f521c8ce0f16cc10175 UnifiedDiffViewerSettings.swift
src/TortoiseProc/FormatPatchDlg.cpp, unified diff button b8ad0c02bb27397700a6aee773d87ce7656d62c8 FormatPatchWindow.swift
src/TortoiseProc/GitLogListAction.cpp, ID_GNUDIFF1/ID_GNUDIFF2 88c255c4c80578c099bbcd4604f6e088f2f9d40a LogWindow.swift, byte-preserving CommitHistory.swift APIs
src/Git/GitStatusListCtrl.cpp, IDGITLC_GNUDIFF1 bb3424966659715269d38fca610c8d46810b0b15 CommitWindow.swift, StatusWindow.swift, raw patch/working-tree diff APIs
src/TortoiseProc/FileDiffDlg.cpp, ID_GNUDIFFCOMPARE/CheckMultipleDiffs fe4171a852023344cac5af1711873104393e1b0a RevisionComparisonWindow.swift, raw comparison patch API
src/TortoiseUDiff/MainWindow.cpp, SaveFile fc1d4053019e4b8bc5c2a1c08837d19c7c506751 UnifiedDiffDocument, read-only PatchWindow Save As

Selection rules

Upstream keeps an external viewer command in DiffViewer. A leading # disables it while retaining its configuration. Shift reverses that choice. Native preferences represent enabled/disabled explicitly and preserve the saved application and security bookmark independently of Alternative Editor settings.

Saved external viewer Ordinary click Shift click
None Built-in Built-in
Configured and enabled External Built-in
Configured and disabled Built-in External

Invalid configured application paths report an error when the external viewer is requested; choosing the built-in viewer does not launch them. Settings use the source's GNU patch viewer group, built-in/external radio choices, path field and Browse button. Disabling the external viewer keeps its path/bookmark. Application bundles retain native document opening when their Arguments field is empty. Configured argument templates and executable tools are supported by the external-document continuation below; complete Windows command/environment and signed macOS launch equivalence remain unverified.

Native handoff and lifetime

Format Patch samples Shift before its asynchronous HEAD/working-tree diff runs. The built-in choice displays the existing read-only syntax-colored Patch window. Its Refresh updates that same window directly; it does not reselect an external viewer from changed preferences. External selection writes the original Git stdout bytes into a unique private directory (0700) and diff.patch (0444), without a lossy text conversion. A failed launch discards only that owned preview; a successful launch retains it until TurtleGit exits. Output files in the user's repository are unaffected.

Log revision and selected-file context actions also sample Shift before their asynchronous diff request, and share the external launch/preview lifetime. A whole working-tree row now uses that same external/built-in selection instead of always opening the built-in viewer. Revision, working-tree and selected-file reads discard their viewer handoff when selection changes or Log is invalidated; late errors from those stale requests are discarded too. An external launch already handed to macOS is not recalled by changing selection. A single revision retains first-parent/root behavior; two revisions retain the older-to-newer comparison. Selected files retain visible order, duplicate suppression and both paths for renames. Core now exposes Data-returning diff APIs; existing String callers keep their prior UTF-8 presentation. Native Log now uses the shared colored read-only patch window for its built-in choice. Close and Quit are blocked while Log's diff request/open callback is busy, and all external viewer callbacks have a shared pending-request Quit guard. Merge-parent/combined variants and other callers remain pending.

The October 7 hidden native check exercises whole working-tree external selection with an invalid configured application, including a disabled viewer requested with Shift. Those requests report the configuration error without launching an app or opening the built-in viewer. It checks enabled-plus-Shift built-in choice through the shared selector, exact literal-path patch bytes through an injected viewer, and stale working/revision/selected-file reads after selection changes or invalidation. HEAD, index and working bytes remain unchanged across all four Git versions. This does not establish successful external application launch or displayed viewer acceptance.

Commit's explicit unified-diff menu now shares this dispatch, including its staged/unstaged and amend-to-parent comparisons. Working Tree adds the explicit unified-diff menu next to Diff, which continues to open the ordinary comparison viewer. Both unified actions sample Shift before async Git and retain a busy guard through the external receiver callback. Working Tree Close/Quit also blocks while busy or a save sheet is open. Its Save unified diff writes the original Data to the selected file rather than decoding/re-encoding it. Existing partial-staging previews still reject non-UTF-8 patches, avoiding lossy editable patch application. Native menu activation, chooser/receiver completion, row-order and rename variants remain unverified.

Changed Files now separates the explicit unified-diff context command from View Patch. The context command opens one viewer per selected file in visible order, honoring the shared setting and Shift. Each built-in context viewer refreshes its captured resolved revision snapshot instead of following later dialog changes, and is reused by path. External viewers receive the original bytes. Rename patch scope includes old and new names. The multi-diff warning uses upstream's default ten and minimum three, with the optional native NumDiffWarning Advanced setting. The older TurtleGit.NumDiffWarning alias is migrated when the setting is edited or cleared. Individual failures are reported together after trying remaining files. View Patch stays built-in and follows selection. Parent Close/Quit guard busy context viewers, which close with their parent; patch windows also guard their own busy/confirmation/sheet lifetime. These native behaviors compile but have not been interactively verified.

Read-only patch windows retain a UnifiedDiffDocument containing original bytes separately from their UTF-8 display/parser text. Format Patch, Changed Files context viewers, View Patch and Commit's read-only preview now feed this data through refresh. Save As captures the document before showing the panel and writes that immutable snapshot atomically. Direct replacement of the displayed document invalidates the old raw snapshot; switching back to editable staging also drops it. Busy/confirmation/sheet guards prevent overlapping Save panels and protect Close/Quit. Invalid UTF-8 is still shown using replacement characters; encoding selection, editable UDiff behavior and exact native Save panel/refresh acceptance are pending. This ports byte-oriented read-only output, not the full upstream Save/Edit workflow.

The selected application's bookmark is resolved and scoped for the native open request. Store builds require successful scoped access; the error directs the user to Browse when needed. The Format Patch controller blocks close, Quit and input changes while the open request is pending, retaining the preview through NSWorkspace completion. Actual receiver timing and signed document acceptance remain unverified. App termination cleans up retained owned previews. A saved viewer is not actually launched during automated tests.

Settings now reserve 760×700 points for the existing tabs plus Unified Diff Viewer. Actual tab visibility, light/dark layout and keyboard behavior remain unverified. App inventory responded during this turn's QA attempt, but both attempts to inspect the one running preview failed with “Sky Computer Use native pipe closed before response.” No accessibility state or screenshot was returned. The exact owned PID/executable was rechecked and terminated because normal Quit was unavailable through that connection. No screenshot is fabricated for this change.

Evidence and remaining work

Three UnifiedDiffViewerTests verify all selection states, invalid paths, saved disabled configuration, independence from Alternative Editor preferences, exact non-UTF-8 preview bytes, private/read-only modes, independent copies and cleanup. These tests do not verify NSWorkspace launch, security bookmarks, actual Shift clicks, settings geometry or signed App Store behavior.

The six CommitHistoryTests also pass after the byte-preserving refactor. A new real-Git regression generates a patch containing invalid UTF-8 text bytes, verifies selected-file output and duplicate suppression, and passes Git's reverse-apply check on the exact external preview. HEAD-relative working-tree output also retains those bytes; generation/checking leave the index unchanged. No GUI app or external viewer was launched for this Log follow-up.

A further 17 focused tests (UnifiedDiffViewer, GitPatch and WorkingTree) pass. The new real-Git test distinguishes staged, unstaged and complete working-tree changes containing invalid UTF-8 bytes, verifies exact preview applicability against the index, checks partial-staging's encoding refusal, and preserves HEAD/index/working bytes. Existing unborn working-tree and staged-hunk cases also pass. This proves the Core byte routes, not native Commit/status menu or Save panel acceptance. No GUI app or external viewer was launched for this follow-up.

Changed Files follow-up: 19 focused RevisionComparison/UnifiedDiffViewer tests pass. The new real-Git regression verifies exact invalid-UTF-8 patch bytes, reverse-apply checking, unchanged index/working bytes and a resolved snapshot that remains stable after HEAD advances. Paths absent from that snapshot are rejected. This does not verify native per-file ordering, warning, Shift, receiver handoff or context-viewer refresh. No GUI app was launched for this follow-up.

Read-only Save follow-up: 25 focused UnifiedDiffViewer/GitPatch/RevisionComparison tests pass. A new filesystem test verifies empty content, UTF-8 BOM/CRLF/no final newline and invalid UTF-8 Save output, captured-document stability after a newer document replaces it, replacement of an existing saved file and directory-target failure without losing that file. The real-Git staged/working test also covers raw read-only working-tree patch bytes. These are portable Core checks; native model invalidation, panel Cancel, keyboard/menu invocation and signed output grants remain unverified. No GUI app was launched for this follow-up.

Remaining: native settings/application-launch and Shift QA; failed/stale bookmark and missing-app acceptance; signed document handoff; command argument templates; other unified-diff entry points; full TortoiseGitUDiff behavior; the other groups and Advanced options in upstream's Diff Viewer settings page. See also FORMAT-PATCH-PARITY.md.

Built-in presentation follow-up: Log revision/selected-file, Commit and Working Tree explicit unified actions now use the shared syntax-colored patch window with Find and original-byte Save As instead of plain OutputView sheets. A parent retains one context viewer, reuses it for explicit new requests, and closes it on parent close. Active Save sheets guard parent close and replacement. These context windows keep their generated snapshot and hide Refresh, matching the upstream UDiff menu rather than silently following selection changes. Embedded staging/selection previews keep their existing Refresh behavior. Save As is visible with the existing original icon and responds to Command-Shift-S; the context action remains. Source accelerator pin: TortoiseUDiff/TortoiseUDiff.rc, blob 1a5c590842d2d99b38eb53a6f11aa640f0d1cbca. Working Tree presentation and basic Save/Find/Escape acceptance are recorded below; the other routes, focus variants and dark mode remain unverified. Full File/Open/Save/editing/encoding/Print/Apply Patch and settings remain pending. The Core implementation is unchanged from the preceding 25-test acceptance; that evidence does not prove the new native routes.

Native acceptance for this presentation follow-up: Working Tree unified menu opened the colored read-only viewer; green additions, blue hunk headers and the original Save As icon were visually inspected. Toolbar Save As and Command-Shift-S opened the native chooser; Cancel returned to the unchanged viewer. Keyboard filename editing was needed after AX setValue to trigger Save validation. Actual export matched Git output exactly (254 bytes, SHA-256 f6713365999ae746ec2c0d892ed86a4f7eb89cc416ec375cc12960b21b6d96dc) and preserved HEAD/index/working bytes. Command-F opened Find; Escape closed Find, then the viewer. A transient ScreenCaptureKit -3811 error did not prevent later AX verification. Actual native capture. Commit menu appeared but activation did not establish a viewer; its close confirmation inspection then timed out. Normal Quit did not end the process; the exact owned PID/executable was rechecked and SIGTERM sent. No preview remains. Log/Commit route activation, dark mode, Find matching, repeated-viewer reuse, non-UTF-8 native Save and signed sandbox acceptance remain unverified.

A fresh single-instance acceptance retry at 07e8895 verified Commit Cancel’s No keeps the dialog open and Yes closes it normally (three changed files, empty message, staging off, no restore copies). The earlier close/inspection timeout did not reproduce, and no cause or code defect was established. In that same instance, Log double-click on cc4ec2840a76b97fe6f7c46debd7a0288c11913c opened the shared read-only viewer with the expected newly added Repository.swift patch. Normal Command-Q exited with Log/viewer open; no process remained and no signal was needed. Exact HEAD/index/working state still matched the preceding owned fixture snapshot. Acceptance record. This verifies the double-click route only; explicit Log/Commit menus, multi-row variants, changed-message/restore/suppression cancellation and signed acceptance remain pending. The earlier timeout record remains valid historical evidence.

Appearance settings (native acceptance pending)

The native Appearance tab ports the six foreground/background pairs from IDD_SETTINGSUDIFF: command, position, header, comment, added lines and removed lines. Light and dark palettes can be configured separately. Restore Default resets the selected palette only; font and tab size remain unchanged. The Font group offers fixed-pitch fonts, font size and tab size. Menlo replaces the Windows Consolas default. Apply persists preferences and updates open patch viewers.

Source Blob Replacement
src/TortoiseProc/Settings/SettingsTUDiff.cpp f7013ed3cb19b7c3c625d98c8d15e437a6862da6 Native appearance settings
src/TortoiseProc/Settings/SettingsTUDiff.h f4ab493bfaf15d299da6c47a0ffe0ef0ef775968 Native settings state
src/TortoiseUDiff/UDiffColors.h 65d7e61aa154245193bce08fbbea75d2f4c49034 Light/dark default RGB palettes
src/TortoiseUDiff/MainWindow.cpp fc1d4053019e4b8bc5c2a1c08837d19c7c506751 Line styles, font and tab spacing

The line classifier follows pinned LexDiff.cxx. The renderer retains line termination for classifier decisions, including CRLF. Combined added/removed styles share their corresponding palettes. Comments are bold. Increased contrast uses native system text/background colors. Lexilla's copyright and permission notice are included in the bundled NOTICE.

Style Light foreground/background Dark foreground/background
Command 0A2436 / FFFFFF C9E2F5 / 202020
Position FF0000 / FFFFFF FF2020 / 202020
Header 800000 / FFFF80 C00000 / 303000
Comment 008000 / FFFFFF 008000 / 202020
Added 000000 / CCFFCC DDDDDD / 104010
Removed 000000 / FFDDDD DDDDDD / 402020

Focused tests cover classification, palette defaults, separate-theme restoration, preference persistence and invalid-value recovery. Native layout, live Apply, light/dark rendering, high contrast and full-width line backgrounds still need acceptance. Font size preset dropdown and owner-drawn font preview remain pending. Existing viewer screenshots precede these palette changes. Lexer folding, editable UDiff, printing and full encoding controls remain incomplete.

Native settings navigation acceptance

Native testing exposed Settings toolbar promotion of the inner TabView: its Appearance entry selected the global Appearance page. Unified Diff now uses an in-page segmented selector. The corrected preview showed all twelve default color wells and font/tab controls while retaining the Unified Diff toolbar tab. The actual settings capture was inspected. Full footer geometry and live Apply remain pending; a Settings-close AX timeout prevented those checks. Both isolated previews quit normally and no TurtleGit process remained. No Core code changed in this navigation fix. See acceptance record.

Scrollable settings and editable size presets

Appearance settings now place color/font controls in a scroll view and reserve the footer outside it, so content growth can scroll independently of Cancel and Apply. The editable native font-size combo offers 6, 8, …, 30 and accepts typed sizes, following SettingsTUDiff.cpp. Blame reuses the same existing combo through NativeFontSizeChoice; its preset/delegate behavior is unchanged.

Both Debug and unsigned App Store builds and bundle audits pass. A single native preview opened a Log diff and reached Unified Diff settings, but the computer-use native pipe closed before the new Appearance page could be observed. A reconnect failed too. The exact owned process was verified and terminated with SIGTERM; no app process remained. No fresh screenshot, geometry acceptance, preset selection, dark rendering or live Apply pass is claimed. The prior screenshot precedes this scroll/preset change. Owner-drawn font preview remains pending. See verification record.

Native Print command (partial port)

PatchPrinting.swift adapts MainWindow.cpp ID_FILE_PRINT at blob fc1d4053019e4b8bc5c2a1c08837d19c7c506751. Command-P replaces the source Control-P accelerator from TortoiseUDiff.rc. Toolbar and context Print actions share the same handler, including the native responder print: action.

Printing captures an independent attributed snapshot and the current selection. The native sheet's Unified Diff accessory selects printed text by default when a nonempty range is selected; without a selection it prints the whole diff and disables the selection choice. The accessory can switch to the whole diff. Native copies, page range, paper size, orientation, scaling, preview and PDF output replace the Windows printing interfaces. A retained operation and busy state block Close, Quit, staging, refresh and duplicate printing until its callback finishes. The displayed diff, selection and original Save As bytes are not passed to pagination as mutable storage.

The implementation uses Apple's print sheet callback and native print responder action. python3 scripts/test-patch-printing.py compiles the real print helper and checks PDF output without opening TurtleGit or a print sheet: a one-page selected snippet excludes whole-document markers; a fresh whole-diff operation produces six pages retaining the first and last markers; changing the caller's mutable source after Print cannot alter the snapshot. Each real print request creates a new operation; the test also uses independent operations for its saved PDFs.

Print sheet Cancel/completion, preview switching, paper variants, colors/fonts, physical printers, signed sandbox access and File-menu exposure remain pending. No native UI verification is claimed while the computer-use pipe is unavailable. This does not complete UDiff's editable document, Open or all print behavior.

Shared File-menu commands and native print acceptance

PatchFileCommands exposes Save As and Print in the macOS File menu, with the source's Command-Shift-S/Command-P accelerator adaptations. PatchMenuContext observes the key window and its model, disables both actions outside a diff or during an operation/sheet, and rechecks the active controller when dispatching. It holds no strong reference to a closed/background diff controller.

Native acceptance on a single isolated preview verified disabled commands in Log, enabled commands in the active diff, File-menu Save As and Cancel, File-menu Print and Cancel, disabled File commands while the print sheet is active, and usable viewer controls after printing is cancelled. Command-A then Command-P checked that the Unified Diff print accessory defaults to selected text; its checkbox switched to whole diff and refreshed the preview. There was no printer selected, so physical output was not attempted. The app quit normally and no TurtleGit process remained. No new screenshot was captured in this check.

This advances the earlier print-sheet acceptance status. Page Setup's four persistent margins, physical printers, partial-selection preview content, multiple diff windows, Settings focus, queued busy transitions and signed sandbox acceptance remain pending. See native record.

Page Setup margins

The native File → Page Setup sheet adapts MainWindow.cpp ID_FILE_PAGESETUP and its print-margin conversion at blob fc1d4053019e4b8bc5c2a1c08837d19c7c506751. Four saved margins start at one inch (25.4 mm / 72 points). Fields use the local measurement system and decimal separator. Preferences store print points so a locale change does not reinterpret the saved dimensions. Paper/orientation stay in Print, following the source Page Setup's disabled paper/orientation controls. OK persists valid margins; Cancel discards edits. Impossible/nonfinite/negative margins are rejected, and stored malformed data falls back to source defaults.

Printing applies the saved margins and the current printer's minimum imageable bounds. The current PDF fixture produces six pages at the source defaults and eleven with larger top/bottom margins, retaining the first/last text markers. An impossible area fails before showing Print. The three margin tests and eight existing appearance/viewer tests pass. Both targets and bundle audits pass.

Native acceptance verifies metric defaults, negative input disabling OK, Cancel preserving preferences, saving 12.7 mm, and reopening that value. The single isolated preview quit normally; no app process remained. No new screenshot was saved. Inch-locale UI, locale changes, native Print after a saved margin edit, paper/printer changes inside Print, physical printer minimums and signed sandbox acceptance remain pending. See verification record.

Native fixed-pitch font preview menu

NativeFixedFontChoice replaces the font-name picker with a native NSPopUpButton. Each item retains its family name and uses an attributed title rendered in that family's fixed-pitch face. The current saved family remains available if it is missing from font enumeration, with a native monospaced preview fallback. The binding stores the family name; the viewer still resolves the selected family through NSFontManager. This adapts SettingsTUDiff.cpp m_cFontNames.Setup and the font combo's owner-drawn resource style. Core preference behavior is unchanged.

Debug/unsigned App Store builds and bundle audits pass. A single fresh preview opened the default-font Log diff and Settings. Entering the Appearance subpage then lost the computer-use native pipe; retrying the same window failed too. The attempt to quit normally was rejected because computer use was inactive. Only the exact owned preview PID 1283 was revalidated and sent SIGTERM; no TurtleGit process remained. No new screenshot or font-preview/live-Apply/dark acceptance is claimed. The cause of the controller failure is not established. See verification record.

Shared patch read/render lifetime — 2026-10-13

The shared native patch renderer now preserves the display document's final newline state. Default working-tree/index readers own cancellable processes, ignore obsolete results, and retain Quit accounting until every reader/mutation settles, including after forced window closure. Original bytes still drive Copy/Save/export independently of display decoding. These changes also serve Commit's attached partial editor; its source-style bottom Find and Staging menus are documented in Commit parity. Read-only viewers retain their existing toolbar and native macOS Find. External owners' custom loaders and physical print/save/panel acceptance remain separate from the default-read coverage of this batch.

Editable documents, file actions and source Find — October 15, 2026

Standalone built-in UDiff windows now enable editing after their patch loads, following MainWindow.cpp InitEditor/SetupWindow. Commit's partial patch and embedded import previews retain their separate staging/read-only gates. Native plain-text editing disables automatic quotes/dashes/replacements; syntax style updates change attributes without replacing the edited text or clearing Undo. Loading another document resets selection and Undo, even for identical text. Undo back to the saved display text restores the original export bytes.

The focused UDiff window's File → Open (Command-O), Save (Command-S) and Save As (Command-Shift-S) own the chosen patch file. Open accepts a single patch/diff or other text file, holds its selected permission, refuses files reaching the source 250 MiB limit and fences late reads after forced closure. A file permission does not select or grant a repository. Save As establishes the new destination; later Save uses it. Direct Save refuses an externally changed file instead of replacing it silently. Writes use atomic replacement. Original bytes are retained until editing; edited files preserve detected UTF-8/UTF-16/UTF-32 BOM/endianness or the existing Windows-1252 fallback. Unrepresentable legacy edits report an error and leave the file unchanged. There is no encoding-conversion picker yet.

Close, Open, Refresh/replacement and app Quit resolve modified documents using Save / Don’t Save / Cancel. Cancelled questions/panels retain the edits and Undo. Normal parent closure for Graph, Changed Files, Format Patch and Send Mail first routes an edited child through its own close decision. Forced retirement fences late UI publication and retains operation/Quit accounting through owned file I/O. The file-document session captures its own window; shared menu commands recheck the focused controller and refuse background windows and active sheets.

src/TortoiseUDiff/TortoiseUDiff.rc IDD_FINDBAR now maps its six controls to the shared native bottom bar, in source order: upstream cancel-normal.ico, Find label, search field, Previous, Next and Match case. Command-F seeds selected text and focuses Find; typing resets the cursor to zero and searches forward. Next/Previous use the current selection end/start, stop without wrapping and flash the owning frame on a boundary. Escape hides Find before closing its window. Command-G/Shift-Command-G and F3/Shift-F3 use the same search actions. Match case changes the next query; there is no extra regex or wrap option. The same source bar remains available in attached partial patches. Embedded Import/Review previews have no bottom-bar host and retain AppKit Find, with read-only text and native show/hide checked separately.

The native checkpoint checks actual NSTextView edits/Undo and clean savepoints, raw bytes, UTF-16 saves, legacy-encoding refusal, file-open/save cancellation, external-change refusal, Cancel Quit, focused menu routing, source Find and an uncooperative read settling after forced closure. The retained partial fixture checks real line stage/hunk unstage, index isolation and Git process cleanup. See dated evidence.

This remains a partial port. Source-specific byte/codepage search equivalence, all physical panel/keyboard/context-menu gestures, multi-owner replacement titles and interleavings, all ordinary parent-close decisions, arbitrary encodings, huge/network-file behavior, filesystem publication cancellation races, signed file grants, App Store execution and full UDiff layout/accessibility acceptance remain open. In-flight writes may finish after a forced close; their late UI result is suppressed. This batch does not add Apply Patch routing or certify the dialog complete. Earlier read-only/native-Find descriptions document older states.

Apply Patch, Settings and comparison replacement — October 15, 2026

The standalone document's File/context menus now expose Apply Patch… and Unified Diff Settings… with the existing source artwork. Save remains visible and disabled during owned work/sheets. Commands recheck the focused owner; context actions also expire when their captured document identity changes.

Apply Patch follows MainWindow.cpp ID_FILE_APPLYPATCH: a named patch is passed to the existing TurtleGitMerge review/apply workflow using its saved file, not unsaved editor text. A generated comparison uses its immutable loaded bytes, the native equivalent of TortoiseProc's read-only temporary patch in AppUtils.cpp StartShowUnifiedDiff/StartUnifiedDiffViewer. A genuinely empty, unnamed document uses the workflow's patch-file chooser. Target selection and better-target detection retain their existing permission boundaries. Opening review does not apply changes; the review window owns the subsequent Apply. The originating patch, preferences, Git engine and retained file/repository permissions are captured. Closing the UDiff owner during preparation cancels that preparation and fences late review presentation. A prepared review is independent, as in the source's detached Merge handoff.

Settings opens the existing native settings controller directly on Unified Diff, using the owning preferences and a global-only settings context. Its sheet locks document actions until dismissal. The settings navigator's optional initial-page argument defaults to General for existing callers.

Standalone comparison producers now publish bytes, title, comparison labels, paths, information and Refresh closure through one owned replacement request. While an unsaved-change question is active, newer requests replace the pending request; Cancel retains every field of the displayed document. A successful Save or Don’t Save publishes only the latest request. Cancelled Save As also retains the old context. Replacements clear obsolete saved-file identities and retain a supplied source file's existing grant where applicable (Send Mail). Refresh callbacks capture the document identity so an older read cannot replace a different document opened in the same window. Embedded/Commit previews keep their separate presentation paths.

The connected native evidence, including an actual saved-patch review/apply in a private working tree, is recorded in handoff QA. This advances the previous batch's Apply Patch and replacement-metadata gaps. Physical menu/panel gestures, all Settings Apply/font/palette transactions, full producer-specific timing races, signed grants/App Store execution and complete UDiff parity remain unverified. No dialog is certified complete.

External viewer arguments and executable tools — October 15, 2026

Unified Diff Viewer now stores an Arguments field beside its selected app or executable. %1 supplies the patch path; %title supplies the captured comparison title. If %1 is absent, the patch is appended. For example, a tool that accepts those flags can use --title "%title" "%1". Quote grouping is parsed before substitution, so spaces, Unicode, quotes, newlines and placeholder-looking text in a filename/title remain within their original argument. There is no shell or environment evaluation. Unfinished quotes/escapes and NUL values are refused.

This follows AppUtils.cpp StartUnifiedDiffViewer's placeholder and implicit patch argument behavior. Windows combined command text is represented by a separate application/executable path and Arguments field. Existing preferences load an empty Arguments value: app bundles continue native URL document opening. A nonempty template uses a fresh app launch with expanded argv. Executables always receive expanded argv. Built-in/external and Shift inversion retain the selected path, bookmark and template, including while the external choice is disabled. Choosing the built-in viewer does not evaluate invalid external args.

Callers now forward the same title used by their built-in comparison, along with owning preferences and available grants. This includes Log, Commit/Status, Changed Files, RefLog, reference browsers, Blame, Rebase conflict output, Sync, Graph, Add, patch import and Send Mail. Format Patch's direct external launch also forwards its template and HEAD-to-working-tree title.

The shared native launcher resolves the selected program bookmark, starts and retains its scope through app-launch completion or managed executable settlement, and preserves original-byte previews for successful detached viewers. Failed launches remove only that request's preview. Cancellation before launch refuses the tool. Executables use the existing Diff/Merge process pool and its group cancellation/reaping; their caller grants survive a closed initiating window. A lost program permission points to Browse in Settings, independently of the repository-folder recovery used for missing file access. External tools are independent after startup. Quit remains guarded while owned launches or managed children are active, following the existing tool lifetime policy. Signed grant transfer and arbitrary third-party app CLI/document behavior remain unverified.

Alternative Editor uses this same launcher for app bundles or executable files, retaining the source's single literal filename argument and TextEdit fallback. It does not acquire a UDiff argument template. Working-file callers retain their repository grants; historical/configuration previews keep their existing private file lifetimes. Queued editor startup participates in the Quit guard.

See external-document evidence for the 18 focused Core cases and native family check, actual private executable argv, scope-provider simulations, descendant cleanup and current Settings-field scope. No real user editor app was launched. Complete source command/environment equivalence, physical Browse/panel gestures, every caller-specific race, signed App Store/Finder execution and full dialog parity remain open.

The GNU patch group in IDD_SETTINGSPROGSDIFF maps its group title, IDC_DIFFVIEWER_OFF/IDC_DIFFVIEWER_ON, command field and Browse to the native GroupBox, radio picker, separate program/Arguments fields and owned chooser. Those five source controls remain partial mappings; physical radio/Browse, complete Diff settings geometry and signed permission acceptance are pending.

Hosted native input readiness

Run 38792987011 at b5acf758 reached the bundled-Git UDiff document case and failed at fixture line 107, typing immediately after Save. The fixture waited for the operation count, but not AppKit editing readiness after SwiftUI propagated the busy transition. Native input boundaries now wait for both canEditText and isEditable, with matching document text, using the existing bounded wait. Save/encoding/dirty/Undo/preservation assertions remain unchanged. The affected document case passed locally with both engines, with owned windows and receivers closed. This is a fixture synchronization repair; hosted confirmation is still required. See exact failure and local evidence.

The shared source color-button family passed native Settings draft/panel checks; see dated evidence for fixture repairs, exact scope and remaining navigation/physical acceptance.

Pinned literal search and owned Find controls — October 17, 2026

The standalone Unified Diff and attached partial-patch bars share the pinned Scintilla Unicode fold table from ext/scintilla/src/CaseConvert.cxx and literal search semantics from Document::FindText. Canonically distinct spellings remain distinct; case-insensitive search supports full-scalar expansions such as Straße / STRASSE, ligatures and Greek sigma. A partial expansion cannot match inside one source scalar. A bounded-prefix matcher preserves overlaps without repeatedly rescanning long prefixes. Native selection ranges cover the actual matched text in UTF-16, including emoji and fold expansions; the source's byte-selection-length shortcut is replaced by the correct native match extent. Search still stops at either boundary, retains the source cursor behavior/flash and offers no extra regex or wrap option. Displayed Unicode text is searched; this does not certify all source legacy codepage/invalid-byte search behavior.

The generated table is reproducible from blob-verified pinned source and can be compared with the original compiled C++ converter for every valid Unicode scalar. Scintilla attribution and its permissive license remain in NOTICE. The table ships in the native Core module without a subprocess or network request per Find.

All six TortoiseProc IDD_FINDBAR controls receive partial mappings, alongside the existing TortoiseUDiff resource mappings. Original cancel artwork, Find label, query, Previous, Next and Match case remain in source order. Native fields/actions capture their source registration, document and form generation. Hide/reopen, busy/question transitions, replacement text and editor detach/reattach cannot revive old callbacks. Registered selection/focus callbacks also check their exact native editor and attachment revision. Queued focus belongs to that same Find visit and cannot focus a hidden or replaced bar. The query can shrink at narrow widths while navigation and Match case retain their labels. Import/Review embeds retain their existing AppKit Find because they have no bottom-bar host.

Both Debug builds exited 0. The pinned C++ oracle agrees for all 1,112,064 valid Unicode scalars (1,557 nonidentity mappings), and all eight focused Core tests pass. Six distinct native case/engine combinations pass across system and bundled Git: source controls, standalone editing/document actions and actual partial staging. The source-only cases do not run Git and their two environments do not prove additional backend coverage. Raw review found the compressible Find label missing; fixed label sizing and a compact row repaired it. Only the two affected source checks were repeated; Core, oracle and retained document/staging checks were not repeated after this layout-only repair. All four final raw captures were individually reviewed: source-order controls fit at the narrow width and in both appearances. All eight receivers were reaped and independently absent, with no private helper paths left. Builds retain existing warnings and seven/one exit-zero/no-output diagnostics; this is not a clean diagnostics claim. See batch evidence.

Full physical keyboard/VoiceOver, every source byte/codepage and platform metric, very large/network documents, signed file grants and App Store acceptance remain open. No dialog is certified complete.

Captured regional patch codecs — October 17, 2026

Raw patch codecs extends regional decoding to partial staging, Original Patch, serial preview and standalone Open/Save/Save As. Edits and clipboard output retain the loaded source codec, with exact Unicode dirty checks and no-op byte preservation. See that checkpoint for native evidence and remaining mixed-codec editing and byte-editor limits. Earlier fixed-1252 display and editable-document encoding limitations above describe their dated snapshots.

Retained byte fragments and native document Undo — October 17, 2026

Byte-backed editing carries mixed UTF8/regional regions, opaque bytes and NUL through native edits, Save/Save As, clipboard and Undo/Redo across saves. The document session captures immutable bytes plus their accepted display before any chooser/write. Earlier whole-file codepage and dirty NUL limitations are superseded within this documented scope; unknown projections, all physical Scintilla gestures and signed acceptance remain open.

The connected captured Settings sheet family shares guarded Close/OK/Cancel and actual-detachment ownership across Properties, Unified Diff and Rebase, preserving their original preferences and pending work.

Standalone input and local appearance — October 25, 2026

The standalone input family adds explicit empty, file and stdin launch without a Git runtime or repository workspace, captured titles, bounded byte-preserving reads, regular-file admission and worker-owned cancellation/Close/Quit. Explicit command-file authorization uses the document's native chooser when needed. The source File-menu Dark Mode action now changes the current viewer without writing global appearance preferences. The family checkpoint separates its Core/native and real executable pipe checks from broader visual, signed-grant, hosted and full-port acceptance.