TurtleGit for Mac

View Markdown source ↗

Standalone two-file Merge-tool comparison

This connected family uses pinned TortoiseGit 7338078f8ddd924b8cddee35f512f2286072136d, specifically src/TortoiseMerge/TortoiseMerge.cpp argument setup, MainFrm.cpp::LoadViews (two-file/one-way setup around 886–950), MainFrm.cpp::FileSave, MainFrm.cpp::CheckForSave, MainFrm.cpp::HasUnsavedEdits, and WorkingFile.cpp::GetWindowName. It is a partial native port record, not certification of every TortoiseMerge dialog or option.

FileComparisonWindowModel.configureMergeTool(_:) applies the parsed invocation before the deferred first load. It forces the built-in Merge viewer, applies the initial one-way view and one-based source line, and retains descriptive/reflected names separately from real filesystem paths. Descriptive names label the native panes; path/reflected metadata stays available in pane help. Switching sides preserves each input's metadata. The invocation's initial one-way choice does not rewrite the user's saved default; normal view switching remains available.

The source Blame conversion/key behavior is recorded in Blame review. It retains original metadata in displayed text and compares the fixed-width source payload only when the upstream converted-file path applies. Readonly and Blame invocations cannot enable editing, type, transfer blocks, alter encoding/endings, Undo/Redo, Save, Save As, export/rebind a pane, or use patch-save mutation entry points. Navigation, comparisons and view changes remain available. Existing patch-review child editors can receive the same policy through configureMergeToolReadOnly(_:) without replacing the patch-owned document. Creating a separate unified patch remains a comparison operation.

Configured Merge-tool launches opt into the source missing-input reader: named absent Base/Mine files become empty native panes with the source (not found) indicator. Normal working-file comparisons retain their strict reader. Each original input has an independent absent/existing identity snapshot; a late appearance or replacement refuses a separate-output Save. Successful owned writes that deliberately alias an input advance only that input's baseline. Explicit Reload acknowledges external input changes.

An explicit merged output uses MergeToolOutputTarget to capture the absent/existing output baseline. Save writes through that target, retaining the original Mine input path and leaving input bytes alone unless the caller deliberately names the same output file. The output's bytes/codec become the clean draft baseline; subsequent Save continues to the explicit output. An external output replacement fails rather than silently overwriting it. Explicit Reload acknowledges a replacement before a later Save. Readonly output permissions route to the existing owned Save As chooser. A chosen Save As establishes its own output target.

Save-required participates in the native close/open/reload unsaved-work policy even before the user edits the loaded Mine. Save All and Quit Save include that clean required result. Successful saving clears the requirement; failed/cancelled saves retain it. mergeToolSaved is true only after an accepted result write with no subsequent unsaved pane work. Parent launch integration can use this state to distinguish saved results from discarded output work. mergeToolHasPendingWork retains instance ownership until load/alignment, EditorConfig, output and Save work has drained, including cancelled work after forced close. Parent integration owns actual process exit, input deletion and launch permissions.

File/Open replacement ownership

MergeEditorReplacementOwner.configureBeforeAdopt applies launch policy to a prepared editor before detaching the accepted Open sheet, native window, or old controller. A thrown policy error closes only the prepared replacement. onAdopt(next, previous) publishes the new actual controller before its deferred first read; launch-session completion and Quit must follow that new owner. Replaced models remain retained while their cancelled reads/alignment/output work drains, exposed by hasPendingRetiredWork. The same receipt retains cancelled patch-preparation workflows until their actual loading task ends, even when their window's immediate onFinished callback has already run. configurePatchWorkflow installs patch readonly/reverse/label policy before preparation creates its embedded editor, so adoption never starts with an already-writable child.

Pinned MainFrm.cpp::OnFileOpen resets the explicit merged file and save-required flag. WorkingFile.cpp::SetFileName clears descriptive names but preserves reflected metadata; frame readonly/Blame and one-way state remain. configureMergeToolReplacement therefore carries immutable policy, current view mode and optional reflected metadata into a fresh comparison model. It does not reapply the original invocation's input paths, output, descriptive labels or line. The native launch protocol separately retains failure when the caller's originally requested result was discarded; ordinary replacement ownership does not erase that command obligation.

Grouped native fixture

docs/qa/merge-tool-comparison-native-2026-10-18.swift supplies TwoFileMergeToolVerification.verify() for the parent's grouped native runner. It uses private files/preferences and owned hidden windows. The final parent checks cover descriptive/reflected metadata without path replacement, source one-way/line configuration, readonly/Blame mutation refusal, clean required output creation, unchanged input bytes, subsequent edits/output saves, changed-existing-output refusal and explicit Reload recovery, missing Base/Mine native empty panes and not-found indicators, late-input appearance refusal and clean independent output creation, a held empty-result choice cancelled by retirement, pre-adopt policy rejection preserving the original editor, actual same-window replacement with current-controller publication before load, retained readonly/view/reflected metadata without stale output/name/line, retired-model drain before final completion, and an actual comparison-to-patch replacement whose readonly policy reaches the embedded child before its first load. Every owned window closes, and pending instance work drains before temporary fixtures are removed.

The parent grouped checkpoint passes this fixture with both engines after the enum-label and accepted-Open injection corrections. Exact evidence records failed attempts, all retained assertions and process cleanup. This fixture checks model/native ownership and save effects; it does not establish physical focus, displayed light/dark parity, accessibility, signed sandbox deployment, all filesystem aliases, or hosted CI acceptance.

Remaining source gaps

Blame uses the source no-edit policy and fixed-width comparison keys; the pinned Merge source does not add dedicated Blame text columns. Reflected names are retained as metadata; editor unified-patch generation now uses the separately reviewed Git-relative header helper. Its source entry is CMainFrame::OnEditCreateunifieddifffile, rather than the previously recorded TryGitUnifiedDiff name. Windows HWND notification options have no native equivalent. This batch does not expand source Open/replacement permission handling, input-cleanup authorization or the three-way editor, which have separate owners and validation.