FIX: sync asymmetric — left unconditionally wins over right-newer and content-diff files

In asymmetric/mirror mode the left panel is the authoritative source:
right-newer files and content-only diffs should always be overwritten by
left, not skipped or left ambiguous.

Two bugs fixed, one UX addition:

1. Main compare (TFileSyncRec.Recalc): srsCopyLeft (right is newer) was
   mapped to srsDoNothing instead of srsCopyRight.  Now both srsCopyLeft
   and srsNotEq map to srsCopyRight in asymmetric mode — left wins.

2. Content-compare thread: after byte-by-byte comparison confirmed a
   difference (srsNotEq), FAction was set directly from FState, bypassing
   the asymmetric rules entirely.  The thread now mirrors the same logic
   as Recalc and assigns srsCopyRight when asymmetric is active.

3. UX: added a tooltip to the 'asymmetric' checkbox clarifying that the
   right panel will become an exact copy of the left (right-only files
   deleted, right-newer files overwritten).
This commit is contained in:
heredie 2026-05-27 16:27:12 -06:00
commit d114984124
2 changed files with 21 additions and 5 deletions

View file

@ -197,7 +197,10 @@ object frmSyncDirsDlg: TfrmSyncDirsDlg
Width = 82
Caption = 'asymmetric'
Enabled = False
Hint = 'One-way mirror: right panel becomes an exact copy of the left. Right-only files are deleted; right-newer files are overwritten by left.'
ParentFont = False
ParentShowHint = False
ShowHint = True
TabOrder = 0
end
object chkSubDirs: TCheckBox

View file

@ -465,7 +465,12 @@ begin
end;
if R.FAction = srsUnknown then
begin
R.FAction := R.FState;
// Mirror asymmetric logic from TFileSyncRec.Recalc:
// in asymmetric mode srsNotEq means left wins → srsCopyRight.
if chkAsymmetric.Checked and (R.FState = srsNotEq) then
R.FAction := srsCopyRight
else
R.FAction := R.FState;
end;
except
on E: Exception do
@ -562,11 +567,19 @@ begin
if FileTimeDiff < 0 then
FState := srsCopyLeft;
end;
if FForm.chkAsymmetric.Checked and (FState = srsCopyLeft) then
FAction := srsDoNothing
else begin
if FForm.chkAsymmetric.Checked then
begin
// In asymmetric/mirror mode left is unconditionally authoritative.
// srsCopyLeft means right is newer — left still wins (overwrite right).
// srsNotEq means ambiguous diff (e.g. content check) — left still wins.
if FState in [srsCopyLeft, srsNotEq] then
FAction := srsCopyRight
else
FAction := FState;
end
else
FAction := FState;
end;
end;
end;
{ TfrmSyncDirsDlg }