7 个版本

0.2.0 2020 年 4 月 5 日
0.1.5 2016 年 9 月 29 日
0.1.3 2016 年 6 月 24 日

#838文件系统 类别中

MIT 许可证

61KB
1K SLoC

Ubiquity

crates.io version crates.io downloads

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