FIX: sync compare — treat equivalent symlink targets as equal

When syncing with symlink follow mode set to skip, links may be recreated with
different textual targets (e.g. short relative vs longer rebased relative)
while still resolving to the same final destination.

Sync compare previously used mtime + size; for symlinks on Linux size is just
the link text length, so semantically equivalent links were flagged as
different after sync.

Fix: in TFileSyncRec.UpdateState, when both sides are links, compare resolved
absolute targets and treat them as equal if they match.
This commit is contained in:
heredie 2026-05-26 09:36:06 -06:00
commit bb1ad21df0

View file

@ -543,6 +543,30 @@ end;
procedure TFileSyncRec.UpdateState(ignoreDate: Boolean);
var
FileTimeDiff: Integer;
function AreEquivalentLinks: Boolean;
var
LeftTarget, RightTarget: String;
LeftResolved, RightResolved: String;
begin
Result := False;
if not (FFileL.IsLink and FFileR.IsLink) then Exit;
LeftTarget := FFileL.LinkProperty.LinkTo;
RightTarget := FFileR.LinkProperty.LinkTo;
// Fast path: identical link text means semantically identical link.
if LeftTarget = RightTarget then Exit(True);
if (LeftTarget = EmptyStr) or (RightTarget = EmptyStr) then Exit;
// Also accept different textual forms that resolve to the same target.
LeftResolved := GetAbsoluteFileName(FFileL.Path, LeftTarget);
RightResolved := GetAbsoluteFileName(FFileR.Path, RightTarget);
Result := mbCompareFileNames(LeftResolved, RightResolved);
end;
begin
FState := srsNotEq;
if Assigned(FFileR) and not Assigned(FFileL) then
@ -552,7 +576,8 @@ begin
FState := srsCopyRight
else begin
FileTimeDiff := FileTimeCompare(FFileL.ModificationTime, FFileR.ModificationTime, FForm.FNtfsShift);
if ((FileTimeDiff = 0) or ignoreDate) and (FFileL.Size = FFileR.Size) then
if ((FileTimeDiff = 0) or ignoreDate) and
((FFileL.Size = FFileR.Size) or AreEquivalentLinks) then
FState := srsEqual
else
if not ignoreDate then