From 512d2904d28c12a8687dbebe5b899fabaee65673 Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Sat, 24 Oct 2026 11:58:58 +0900 Subject: [PATCH] =?UTF-8?q?modular=5Fsync=20#4=20CopyPolicy:=20"=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EC=AA=BD=EC=9D=B4=20=EB=8D=94=20=EC=83=88=EA=B2=83?= =?UTF-8?q?=EC=9D=B4=EB=A9=B4=20=EA=B1=B4=EB=84=88=EB=9B=B0=EA=B8=B0"=20?= =?UTF-8?q?=ED=8C=90=EB=8B=A8=EC=9D=84=20SameFile.keepNewer=20=ED=95=98?= =?UTF-8?q?=EB=82=98=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SameFile.keepNewer(skipNewer, targetModified, modified): 설정 (229) 이 켜져 있고 받는 쪽이 2초 넘게 새것일 때만 그대로 둠, 시각을 모르면 덮음 - 부르는 곳: 폴더 합치기 (로컬 isNewerThere · 원격 anyCopy) · 압축 풀기 덮어쓰기 (archive_ops). 같은 이름 창의 "더 새것" 표시 (name_clash) 도 SameFile.targetNewer - 2초 규칙을 따로 계산하던 곳이 없어짐 (robocopy 는 /XO, rsync 는 -u 로 같은 일) - 동작은 그대로 (판단 모으기만). 약속 시험 한 줄 더함 확인: analyze 0 · 단위 1484 ×2 (c4_unit1 · c4_unit3) · 통합 6/6 ×2 · base10 214장 0px · move_left_shot · check_shot ×2 (c4_unit2 에서 ftp_test 1 · l10n_check 1 이 같은 때 실패 - 따로 3회 · 전체 다시 1회 모두 통과, 부하 때의 흔들림으로 보이며 따로 보고) Co-Authored-By: Claude Opus 5.5 --- lib/core/archive_ops.dart | 4 ++-- lib/core/files/any_transfer.dart | 2 +- lib/core/files/local_transfer.dart | 2 +- lib/core/files/same_file.dart | 5 +++++ lib/core/name_clash.dart | 5 +++-- test/same_file_contract_test.dart | 9 +++++++++ 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/core/archive_ops.dart b/lib/core/archive_ops.dart index b2ff067..f60b752 100644 --- a/lib/core/archive_ops.dart +++ b/lib/core/archive_ops.dart @@ -8,6 +8,7 @@ import 'package:path/path.dart' as p; import '../l10n/tr.dart'; import 'file_ops.dart' show NameConflict, nameProblem; import '../services/platform_info.dart'; +import 'files/same_file.dart'; /// 86: 압축 종류 (확장자로) enum ArchiveKind { zip, sevenZip, rar } @@ -142,9 +143,8 @@ class ExtractResult { /// 덮어쓰기에서 건너뛸지: 받는 쪽 파일이 압축 안의 것보다 2초 넘게 새것 bool _targetNewer(String target, DateTime? modified) { - if (modified == null) return false; try { - return File(target).lastModifiedSync().isAfter(modified.add(const Duration(seconds: 2))); + return SameFile.keepNewer(skipNewer: true, targetModified: File(target).lastModifiedSync(), modified: modified); } catch (_) { return false; } diff --git a/lib/core/files/any_transfer.dart b/lib/core/files/any_transfer.dart index 6b8368c..f3f92aa 100644 --- a/lib/core/files/any_transfer.dart +++ b/lib/core/files/any_transfer.dart @@ -95,7 +95,7 @@ Future> anyCopy(TransferControl c, List sources, String des // 229: 합치는 폴더 안에서 받는 쪽이 더 새것이면 예전 것으로 덮지 않는다 (WebDAV 도 로컬과 같게) if (inner && c.skipNewerInMerge) { final t = await vStat(target); - if (t != null && !t.isDir && SameFile.targetNewer(t.modified, st.modified)) { + if (t != null && !t.isDir && SameFile.keepNewer(skipNewer: true, targetModified: t.modified, modified: st.modified)) { c.keptNewer.add(src); return; } diff --git a/lib/core/files/local_transfer.dart b/lib/core/files/local_transfer.dart index 069919d..d965b3d 100644 --- a/lib/core/files/local_transfer.dart +++ b/lib/core/files/local_transfer.dart @@ -129,7 +129,7 @@ Future _deleteExcept(String dir, Set keep) async { Future isNewerThere(String src, String target) async { final t = File(target); if (!await t.exists()) return false; - return SameFile.targetNewer(await t.lastModified(), await File(src).lastModified()); + return SameFile.keepNewer(skipNewer: true, targetModified: await t.lastModified(), modified: await File(src).lastModified()); } /// 229: 같은 이름 창 - 폴더를 합치면 안쪽에서 받는 쪽이 더 새 파일 수 ([limit] 개까지 셈, 로컬만) diff --git a/lib/core/files/same_file.dart b/lib/core/files/same_file.dart index af42443..ed83566 100644 --- a/lib/core/files/same_file.dart +++ b/lib/core/files/same_file.dart @@ -24,4 +24,9 @@ class SameFile { /// 받는 쪽이 [window] 넘게 더 새것인지 (-u 건너뛰기 · 미리 보기 "더 새 것" 표시) static bool targetNewer(DateTime targetModified, DateTime modified) => targetModified.isAfter(modified.add(window)); + + /// modular_sync #4 CopyPolicy: 환경 설정 "받는 쪽이 더 새것이면 건너뛰기" ([skipNewer], 229) 로 받는 쪽을 그대로 둘지. + /// 폴더 합치기 (로컬 · 원격) · 압축 풀기 (덮어쓰기) 가 모두 이것을 부른다 (robocopy 는 /XO, rsync 는 -u 로 같은 일). 받는 쪽이 없으면 false + static bool keepNewer({required bool skipNewer, required DateTime? targetModified, required DateTime? modified}) => + skipNewer && targetModified != null && modified != null && targetNewer(targetModified, modified); } diff --git a/lib/core/name_clash.dart b/lib/core/name_clash.dart index 79eb59c..0d87b1c 100644 --- a/lib/core/name_clash.dart +++ b/lib/core/name_clash.dart @@ -1,6 +1,7 @@ import '../l10n/tr.dart'; import 'file_ops.dart' show formatSize; import 'vfs.dart' show vBasename; +import 'files/same_file.dart'; /// 약속: 같은 이름 비교 글 ([clashCompareText]) · 파일 날짜 글 ([fileDateText]). 순수 함수, 오류 없음. /// 탐색기 복사 확인 창 · 끌어 놓기 · 붙여넣기가 함께 쓴다. @@ -15,9 +16,9 @@ String clashCompareText(NameClash x) { final h = x.here, t = x.there; final newer = h == null || h.modified.year <= 1 || t.modified.year <= 1 ? '' - : h.modified.isAfter(t.modified.add(const Duration(seconds: 2))) + : SameFile.targetNewer(h.modified, t.modified) ? tr(' (보낼 것이 더 새것)') - : t.modified.isAfter(h.modified.add(const Duration(seconds: 2))) + : SameFile.targetNewer(t.modified, h.modified) ? tr(' (있는 것이 더 새것)') : tr(' (바뀐 날 같음)'); return '${vBasename(x.src)}: ${trf('보낼 것 {0} / 있는 것 {1}', [h == null ? '?' : one(h), one(t)])}$newer'; diff --git a/test/same_file_contract_test.dart b/test/same_file_contract_test.dart index a739d7e..b81863e 100644 --- a/test/same_file_contract_test.dart +++ b/test/same_file_contract_test.dart @@ -26,6 +26,15 @@ void main() { expect([SameFile.forAppSync(remote: true, move: false).uploadTime, SameFile.forAppSync(remote: true, move: true).uploadTime, SameFile.forAppSync(remote: false, move: false).uploadTime], [true, false, false]); expect([SameFile.targetNewer(t0.add(const Duration(seconds: 3)), t0), SameFile.targetNewer(t0.add(const Duration(seconds: 2)), t0)], [true, false]); + // #4 CopyPolicy: 설정이 켜져 있고 받는 쪽이 2초 넘게 새것일 때만 그대로 둠 · 시각을 모르면 덮음 + final later = t0.add(const Duration(minutes: 1)); + expect([ + SameFile.keepNewer(skipNewer: true, targetModified: later, modified: t0), + SameFile.keepNewer(skipNewer: false, targetModified: later, modified: t0), + SameFile.keepNewer(skipNewer: true, targetModified: t0, modified: later), + SameFile.keepNewer(skipNewer: true, targetModified: null, modified: t0), + SameFile.keepNewer(skipNewer: true, targetModified: later, modified: null), + ], [true, false, false, false, false]); }); group('미리 보기 = 실제 맞추기 (받는 쪽 같은 크기 · 1분 더 새것)', () {