7 个版本
0.2.0 | 2020 年 4 月 5 日 |
---|---|
0.1.5 | 2016 年 9 月 29 日 |
0.1.3 | 2016 年 6 月 24 日 |
#838 在 文件系统 类别中
61KB
1K SLoC
Ubiquity
Ubiquity 是一个用 Rust 编写的新的文件同步 库。它受到了另一个优秀的同步工具 Unison 的启发,但首先被设计为一个库而不是命令行工具,并且它也能处理无限数量的副本。
特性
- 无限数量的副本(然而,目前,为了性能,这是在编译时指定的,因此代码是针对副本数量进行了优化的)
- 可配置的更新检测,可以由文件系统监视器提供支持(例如:Linux 上的 kqueue 或 OS X 上的 FSEvents)
- 作为其他应用程序内部的库运行,并具有干净的 API。
- 在 '存档目录' 中缓存文件系统状态,加快后续同步操作。
缺点
不能说 Ubiquity 适合每个任务,因为经过几周的工作,Ubiquity 还远未经过实战考验。
- 需要 rsync 二进制文件来实际传播文件更改。
- 这意味着我们可以利用 rsync 的所有优点,但确实存在外部依赖项
- 考虑切换到纯 Rust 实现,如 LuminS
- 需要
cmp
执行文件比较 - 测试覆盖率不高
- 基本功能是有的,我使用它来保持我的文件同步。
- 但是,不太常用的代码路径可能存在错误。
- Ubiquity 可能无法处理符号链接
- 在
ubiqiuity::propagate
模块中的一些代码路径是unimplemented!()
,因为我还没有在副本内部使用足够的符号链接,以至于真正需要显式的 '支持'。
- 在
- 更改传播不是原子的
基本操作
同步过程分为三个阶段。首先,您需要调用 ubiquity::detect::find_updates
函数,并传入一些参数,告诉 Ubiquity 在哪里查找已更改的文件。它将返回一份副本之间不同的文件列表。
您可以对这个列表做任何想做的事情,但通常您需要解决这些差异。您可以使用算法、用户输入或硬编码的值来确定每个差异的“主”副本。'主'副本是正确的、最新的文件版本,它将被传播到所有其他副本。
Ubiquity 包含了 ubiquity::reconcile::guess_operation
函数,该函数将选择自上次运行以来已更改的文件,如果没有文件更改或双方都更改了文件,则返回 None
。
一旦您有了“主”副本,就可以使用 ubiquity::propagate::propagate
函数传播更改。
extern crate ubiquity;
extern crate regex;
extern crate typenum;
#[macro_use]
extern crate generic_array;
use std::path::{Path, PathBuf};
use std::fs;
use ubiquity::{archive, detect, reconcile, propagate};
use ubiquity::config::{SyncInfo};
use regex::Regex;
fn main() {
let archive = archive::Archive::new(Path::new("tests/replicas/archives").to_path_buf()).unwrap();
let a = Path::new("tests/replicas/path_a");
let b = Path::new("tests/replicas/path_b");
if !a.is_dir() {
fs::create_dir(a).unwrap();
}
if !b.is_dir() {
fs::create_dir(b).unwrap();
}
let mut config: SyncInfo = SyncInfo::new(arr![PathBuf; PathBuf::from("tests/replicas/path_a"), PathBuf::from("tests/replicas/path_b")]);
config.ignore.regexes.push(Regex::new(r".DS_Store").unwrap());
config.ignore.paths.push("Microsoft User Data".to_string());
let mut search = detect::SearchDirectories::from_root();
let result = detect::find_updates(&archive, &mut search, &config, &detect::EmptyProgressCallback).expect("Failed to find conflicts");
if result.differences.is_empty() {
println!("All in sync");
}
for difference in result.differences {
let operation = reconcile::guess_operation(&difference);
println!("Difference at {:?}, resolving using {:?}", difference.path, operation);
if let reconcile::Operation::PropagateFromMaster(master) = operation {
propagate::propagate(&difference, master, &archive, &propagate::DefaultPropagationOptions, &propagate::EmptyProgressCallback).unwrap();
}
}
}
运行测试
您可以使用 cargo test
命令来运行测试,就像平常一样,但使用以下命令可能更便于观察发生了什么
$ env RUST_LOG=debug cargo test -- --test-threads 1
待办事项:将手动循环替换为 ArchiveEntryPerReplica::from_roots
依赖项
~3–13MB
~137K SLoC