102 lines
4.4 KiB
Dart
102 lines
4.4 KiB
Dart
import 'dart:convert' show utf8;
|
|
import 'dart:io';
|
|
|
|
import 'package:jj_mkvmaker/core/sync_tools.dart' show splitOptions, toCygwinPath;
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 28 (10/19) 이 원격 맞추기 vs 실제 rsync 비교에 쓴 장면 · 옵션 (rsync_filter_test 의 "28 의 장면" 과 같은 것).
|
|
/// 원격 (WebDAV · SFTP · FTP · SMB) 맞추기의 결과 트리를 실제 rsync 를 로컬에서 돌린 결과 트리와 비교할 때 쓴다.
|
|
|
|
/// 보내는 쪽 파일
|
|
const sceneSrc = [
|
|
'a.txt', 'b.log', 'build/x.o', 'build/sub/y.o', 'sub/c.txt', 'sub/deep/d.txt', 'sub/deep/e.log', 'node_modules/m/i.js',
|
|
'src/node_modules/n.js', 'docs/readme.md', 'docs/img/p.png', '한글 폴더/가.txt', 'space dir/s.bak', 'Keep.TXT', 'tmp/t.txt',
|
|
'x/tmp/t2.txt', 'foo/bar', 'foo/q/bar',
|
|
];
|
|
|
|
/// 받는 쪽에만 있는 파일 (지우기 · 거른 것 지키기 · 거른 것이 든 폴더)
|
|
const sceneDst = [
|
|
'extra.txt', 'keep.log', 'build/old.o', 'old/keep.log', 'old/gone.txt', 'sub/deep/z.txt', 'tmp/old.txt', 'onlydst/a.bak',
|
|
'old2/deep/x/keep.log', 'old2/deep/x/g.txt', 'old2/deep/y/h.txt', 'old2/e.txt',
|
|
];
|
|
|
|
/// 옵션 (그대로 rsync 에 · 원격 맞추기는 그 옵션의 지우기 · 거르기)
|
|
const sceneOptions = [
|
|
'-a --delete --exclude=*.log --exclude=build/ --exclude=/sub/deep/',
|
|
'-a --delete --exclude=*.log',
|
|
'-a --delete --include=*/ --include=*.txt --exclude=*',
|
|
'-a --delete --exclude=node_modules/',
|
|
'-a --delete --exclude=/tmp/',
|
|
'-a --delete --exclude=tmp/',
|
|
'-a --delete --exclude=sub/deep',
|
|
'-a --delete --exclude=docs/***',
|
|
'-a --delete --exclude=foo/**/bar',
|
|
'-a --delete --exclude=**/bar',
|
|
'-a --delete --exclude=*.[lb][oa][gk]',
|
|
'-a --delete --exclude="space dir/"',
|
|
'-a --delete --exclude=한글*/',
|
|
'-a --delete --exclude=*.bak --delete-excluded',
|
|
'-a --delete --exclude=*.log --delete-excluded',
|
|
'-a --delete --filter="- *.o" --filter="+ build/"',
|
|
'-a --delete --include=sub/deep/d.txt --exclude=sub/deep/*',
|
|
'-a --delete --exclude=?.txt',
|
|
'-a --delete --exclude=*.TXT',
|
|
'-a --delete --exclude=deep/',
|
|
'-a --delete --exclude=/old2/deep/x/keep.log',
|
|
'-a --del',
|
|
'-a --delete-after --exclude=*.log',
|
|
'-a --delete --exclude=**/x/',
|
|
'-a --delete --include=*.txt --exclude=*.log --exclude=*/',
|
|
'-a --delete -f"+ *.txt" --exclude=*',
|
|
'-a --delete -f"- *.log"',
|
|
// 지우기 없음 (거른 것만)
|
|
'-a --exclude=*.log --exclude=build/',
|
|
];
|
|
|
|
/// 앱에 들어 있는 rsync (Windows). 없으면 null
|
|
String? get bundledRsync {
|
|
final f = p.join(Directory.current.path, 'third_party', 'rsync', 'windows', 'rsync.exe');
|
|
return Platform.isWindows && File(f).existsSync() ? f : null;
|
|
}
|
|
|
|
/// 장면을 만든다 (있던 것은 지우고). 보내는 쪽 글은 "s:경로", 받는 쪽은 "d:경로"
|
|
void writeScene(String src, String dst) {
|
|
for (final (dir, list, tag) in [(src, sceneSrc, 's'), (dst, sceneDst, 'd')]) {
|
|
final d = Directory(dir);
|
|
if (d.existsSync()) d.deleteSync(recursive: true);
|
|
d.createSync(recursive: true);
|
|
for (final r in list) {
|
|
File(p.join(dir, r))
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('$tag:$r');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 폴더 안의 트리: 상대 경로 (/) → 폴더면 "/", 파일이면 그 글
|
|
Map<String, String> treeOf(String dir) {
|
|
final out = <String, String>{};
|
|
final root = Directory(dir);
|
|
if (!root.existsSync()) return out;
|
|
for (final e in root.listSync(recursive: true)) {
|
|
final rel = p.relative(e.path, from: dir).replaceAll(r'\', '/');
|
|
out[rel] = e is Directory ? '/' : File(e.path).readAsStringSync();
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/// 실제 rsync 로 src/ → dst/ 를 맞춘 뒤 dst 의 트리 (src · dst 는 writeScene 으로 만든 로컬 폴더)
|
|
Future<Map<String, String>> realRsyncTree(String rsync, String src, String dst, String opts) async {
|
|
final r = await Process.run(rsync, ['-8', ...splitOptions(opts), '${toCygwinPath(src)}/', '${toCygwinPath(dst)}/'],
|
|
stdoutEncoding: utf8, stderrEncoding: utf8);
|
|
if (r.exitCode != 0) throw StateError('rsync $opts → ${r.exitCode}: ${r.stderr}');
|
|
return treeOf(dst);
|
|
}
|
|
|
|
/// 두 트리의 다른 점 (없으면 빈 목록)
|
|
List<String> treeDiff(Map<String, String> want, Map<String, String> got) => [
|
|
for (final k in {...want.keys, ...got.keys}.toList()..sort())
|
|
if (want[k] != got[k])
|
|
'$k: rsync=${want[k] == null ? '없음' : want[k] == '/' ? '폴더' : '"${want[k]}"'} · 앱=${got[k] == null ? '없음' : got[k] == '/' ? '폴더' : '"${got[k]}"'}',
|
|
];
|