8个版本 (4 个重大更改)

0.4.0 2024年6月14日
0.3.0 2022年8月29日
0.2.2 2022年2月1日
0.2.1 2021年1月27日
0.0.0 2020年6月8日

#49 in 文本处理

Download history 63328/week @ 2024-05-04 60148/week @ 2024-05-11 58482/week @ 2024-05-18 52114/week @ 2024-05-25 48503/week @ 2024-06-01 52793/week @ 2024-06-08 53936/week @ 2024-06-15 57837/week @ 2024-06-22 46146/week @ 2024-06-29 51193/week @ 2024-07-06 52694/week @ 2024-07-13 50647/week @ 2024-07-20 47061/week @ 2024-07-27 41684/week @ 2024-08-03 44811/week @ 2024-08-10 43210/week @ 2024-08-17

每月下载量 186,872
用于 90 个crate (23 直接)

MIT/Apache

150KB
4K SLoC

diffy

diffy on crates.io Documentation (latest release) Documentation (master) License License

用于查找和操作文件之间差异的工具

许可证

本项目可在Apache 2.0许可证或MIT许可证的条款下使用。


lib.rs:

用于查找和操作文件之间差异的工具

概述

这个库旨在成为一组工具的集合,用于查找和操作文件之间的差异,这些工具受到了LibXDiffGNU Diffutils的启发。版本控制系统(如GitMercurial)通常使用diffpatch来在两个版本的文件之间传递差异。

当前的diff实现基于Myers' diff算法

UTF-8和非UTF-8

此库支持同时处理utf8和非utf8文本。大多数API有两个不同的变体,一个用于处理utf8 str文本(例如create_patch),另一个用于处理字节[u8],这些字节可能是utf8,也可能不是(例如create_patch_bytes)。

创建补丁

可以通过以下方式创建两个文本之间的Patch

use diffy::create_patch;

let original = "The Way of Kings\nWords of Radiance\n";
let modified = "The Way of Kings\nWords of Radiance\nOathbringer\n";

let patch = create_patch(original, modified);
#
#

Patch可以通过其Display实现或使用PatchFormatter来以彩色输出diff。

#
#
#
#
#
// Without color
print!("{}", patch);

// With color
let f = PatchFormatter::new().with_color();
print!("{}", f.fmt_patch(&patch));
--- original
+++ modified
@@ -1,2 +1,3 @@
 The Way of Kings
 Words of Radiance
+Oathbringer

应用补丁

一旦你有了Patch,就可以将其应用到基础镜像上以恢复新文本。每个块将按顺序应用到基础镜像上。类似于GNU patch,此实现可以检测补丁中指定的行号是否错误,并将尝试通过从前向后和从后向前迭代给定位置,直到每个块的上下文行与基础镜像匹配为止,来找到应用每个块的正确位置。

use diffy::{apply, Patch};

let s = "\
--- a/skybreaker-ideals
+++ b/skybreaker-ideals
@@ -10,6 +10,8 @@
 First:
     Life before death,
     strength before weakness,
     journey before destination.
 Second:
-    I will put the law before all else.
+    I swear to seek justice,
+    to let it guide me,
+    until I find a more perfect Ideal.
";

let patch = Patch::from_str(s).unwrap();

let base_image = "\
First:
    Life before death,
    strength before weakness,
    journey before destination.
Second:
    I will put the law before all else.
";

let expected = "\
First:
    Life before death,
    strength before weakness,
    journey before destination.
Second:
    I swear to seek justice,
    to let it guide me,
    until I find a more perfect Ideal.
";

assert_eq!(apply(base_image, &patch).unwrap(), expected);

执行三次合并

给定一个共同的祖先或原始文件O,两个文件AB可以合并在一起以生成一个文件C,类似于diff3执行三次合并的方式。

    --- A ---
  /           \
 /             \
O               C
 \             /
  \           /
    --- B ---

如果文件AB修改了原始文件O的不同区域(或在相同区域以相同方式),则可以在没有冲突的情况下合并这些文件。

use diffy::merge;

let original = "the final empire\nThe Well of Ascension\nThe hero of ages\n";
let a = "The Final Empire\nThe Well of Ascension\nThe Hero of Ages\n";
let b = "The Final Empire\nThe Well of Ascension\nThe hero of ages\n";
let expected = "\
The Final Empire
The Well of Ascension
The Hero of Ages
";

assert_eq!(merge(original, a, b).unwrap(), expected);

如果文件AB都修改了原始文件O的相同区域(且这些修改不同),则会导致冲突,因为在合并结果中不清楚应该使用哪些修改。

use diffy::merge;

let original = "The Final Empire\nThe Well of Ascension\nThe hero of ages\n";
let a = "The Final Empire\nThe Well of Ascension\nThe Hero of Ages\nSecret History\n";
let b = "The Final Empire\nThe Well of Ascension\nThe hero of ages\nThe Alloy of Law\n";
let expected = "\
The Final Empire
The Well of Ascension
<<<<<<< ours
The Hero of Ages
Secret History
||||||| original
The hero of ages
=======
The hero of ages
The Alloy of Law
>>>>>>> theirs
";

assert_eq!(merge(original, a, b).unwrap_err(), expected);

依赖关系

~0.1–7MB
~35K SLoC