#blocks #across #codebase #different #sync #keep #relative-path

bin+lib onchg

在您的代码库的不同文件中保持块同步

7 个版本

0.1.6 2023 年 11 月 18 日
0.1.5 2023 年 10 月 14 日

6#codebase

Download history 21/week @ 2024-03-11 46/week @ 2024-04-01

60 每月下载次数

MIT 许可证

100KB
2.5K SLoC

onchg

Crates.io docs.rs codecov test

一个工具,允许您在代码库的不同文件中保持块同步。

安装

pre-commit 钩子

- repo: https://github.com/aksiksi/onchg-rs
  rev: v0.1.6
  hooks:
    - id: onchg

命令行界面

cargo install onchg

快速入门

视频

https://www.loom.com/share/4018aea2378f4e4e8fcd403a70749cde?sid=19f4c8ec-87b6-4eac-a448-f326695189ee

设置

创建一个空目录

mkdir -p /tmp/onchg/quickstart && cd /tmp/onchg/quickstart

创建两个文件 - docs.mdheader.h

docs.md:

cat >docs.md <<EOL
# Docs

## Supported Services

<!--- LINT.OnChange(supported-services) --->
* Main
* Primary
* Other
<!--- LINT.ThenChange(header.h:supported-services) --->

EOL

header.h:

cat >header.h <<EOL

// LINT.OnChange(supported-services)
typedef enum {
    INVALID = 0,
    MAIN = 1,
    PRIMARY = 2,
    OTHER = 3,
} supported_services_t;
// LINT.ThenChange(docs.md:supported-services)

EOL

初始化 Git 仓库并提交这两个文件

git init . && git add . && git commit -m "first commit"

pre-commit

创建一个 pre-commit 配置并安装钩子

cat >.pre-commit-config.yaml <<EOL
repos:
  - repo: https://github.com/aksiksi/onchg-rs
    rev: v0.1.6
    hooks:
      - id: onchg
EOL

pre-commit install

更改 header.h

--- a/header.h
+++ b/header.h
@@ -5,6 +5,7 @@ typedef enum {
     MAIN = 1,
     PRIMARY = 2,
     OTHER = 3,
+    NEW = 4,
 } supported_services_t;
 // LINT.ThenChange(docs.md:supported-services)

暂存并提交

$ git add . && git commit -m "my commit"
onchg....................................................................Failed
- hook id: onchg
- exit code: 1

Root path: /home/aksiksi/onchg/quickstart

Parsed 2 files (2 blocks total):
  * /home/aksiksi/onchg/quickstart/docs.md
  * /home/aksiksi/onchg/quickstart/header.h

Violations:
  * block "supported-services" at /home/aksiksi/onchg/quickstart/docs.md:5 (due to block "supported-services" at /home/aksiksi/onchg/quickstart/header.h:2)

命令行界面

在目录上运行 onchg

$ onchg directory
Root path: /home/aksiksi/onchg/quickstart

Parsed 2 files (2 blocks total):
  * /home/aksiksi/onchg/quickstart/docs.md
  * /home/aksiksi/onchg/quickstart/header.h

OK.

更改 header.h 中的枚举

--- a/header.h
+++ b/header.h
@@ -5,6 +5,7 @@ typedef enum {
     MAIN = 1,
     PRIMARY = 2,
     OTHER = 3,
+    NEW = 4,
 } supported_services_t;
 // LINT.ThenChange(docs.md:supported-services)

暂存更改并在仓库模式下运行 onchg

$ git add header.h && onchg repo
Root path: /home/aksiksi/onchg/quickstart

Parsed 2 files (2 blocks total):
  * /home/aksiksi/onchg/quickstart/docs.md
  * /home/aksiksi/onchg/quickstart/header.h

Violations:
  * block "supported-services" at /home/aksiksi/onchg/quickstart/docs.md:5 (due to block "supported-services" at /home/aksiksi/onchg/quickstart/header.h:2)

更改 docs.md

--- a/docs.md
+++ b/docs.md
@@ -6,5 +6,6 @@
 * Main
 * Primary
 * Other
+* New
 <!-- LINT.ThenChange(header.h:supported-services) -->

暂存更改并重新运行 onchg

$ git add docs.md && onchg repo
Root path: /home/aksiksi/onchg/quickstart

Parsed 2 files (2 blocks total):
  * /home/aksiksi/onchg/quickstart/docs.md
  * /home/aksiksi/onchg/quickstart/header.h

OK.

文档

示例

双向依赖

alpha.txt:

OnChange(my-block)

ThenChange(beta.txt:their-block)

beta.txt:

OnChange(their-block)

ThenChange(alpha.txt:my-block)

相对路径

alpha.txt:

OnChange(my-block)

ThenChange(subdir/beta.txt:their-block)

subdir/beta.txt:

OnChange(their-block)

ThenChange(../alpha.txt:my-block)

根路径

alpha.txt:

OnChange(my-block)

ThenChange(subdir/beta.txt:their-block)

subdir/beta.txt:

OnChange(their-block)

ThenChange(//alpha.txt:my-block)

单向 OnChange 和 ThenChange

alpha.txt:

OnChange()

ThenChange(beta.txt:their-block)

beta.txt:

OnChange(their-block)

ThenChange()

多重依赖

alpha.txt:

OnChange(my-block)

ThenChange(beta.txt:their-block, gamma.txt:another)

beta.txt:

OnChange(their-block)

ThenChange()

gamma.txt:

OnChange(another)

ThenChange(alpha.txt:my-block, beta.txt:their-block)

嵌套块

alpha.txt:

OnChange(my-block)

OnChange(inner-block)

ThenChange(beta.txt:their-block)

ThenChange()

beta.txt:

OnChange(their-block)

ThenChange()

详细信息

onchg 使用 来捕获不同文件中代码(或更一般地说文本)部分之间的依赖关系。

块看起来像这样

OnChange( [name] )

ThenChange( [<target>[, ...]] )

OnChangeThenChange 部分可以位于行上的任何位置。这允许您将部分放在任何类型的代码注释内部。

OnChange 接受可选的 name。如果一个块没有指定名称,它不能被其他块用作目标。这在您想要单向依赖的情况下很有用 - 也就是说,如果这个块发生变化,其他块应该发生变化,但反过来则不成立。

ThenChange 接受零个或多个 target。块目标具有以下语法

[file][:[block]]

就像 OnChange 一样,如果目标列表为空,ThenChange 也允许单向依赖。

如果指定了目标,它可以是文件或文件中的块。块只是块名称。文件路径必须是以下之一

  1. 相对:路径相对于当前文件的路径(例如,abc / / .txt)。
  2. 相对于根目录:路径以//开始,表示该路径相对于根目录。当运行onchg时,您需要指定此路径。通常,根目录是Git仓库的根目录。

基准测试

合成

设置

  • 操作系统:Ubuntu 22.04 虚拟机
  • 处理器:10核心AMD 3900x等效(虚拟化)
  • 磁盘:Corsair Force MP510 PCIe Gen3 NVMe驱动器

基准名称中的1501000表示分析文件的数量。

grep相比,除了在所有文件中找到匹配项外,onchg还需要

  1. 将所有块的状态加载到内存中。
  2. 解析并提取捕获组内容以确保块有效。
  3. 对所有解析的块运行验证步骤。

[!NOTE] 所有基准测试都经过播种,以便可重复。

稀疏

[!NOTE] 这是一个更现实的基准测试。

每个文件有0-10个块,每个块最多有100行。每行的长度从0-100个字符。

grep慢约2倍

directory-sparse/150    time:   [2.5643 ms 2.5974 ms 2.6338 ms]
grep-sparse/150         time:   [1.6161 ms 1.6241 ms 1.6328 ms]
ripgrep-sparse/150      time:   [4.9077 ms 4.9354 ms 4.9640 ms]

directory-sparse/1000   time:   [15.186 ms 15.271 ms 15.359 ms]
grep-sparse/1000        time:   [6.4750 ms 6.5380 ms 6.6048 ms]
ripgrep-sparse/1000     time:   [7.6132 ms 7.6550 ms 7.6980 ms]

密集

[!NOTE] 这更像是一个病理性的最坏情况基准测试。

每个文件有50-100个块。与稀疏基准测试相同的行数和行长设置。

grep慢5-6倍

directory-dense/150     time:   [11.388 ms 11.469 ms 11.554 ms]
grep-dense/150          time:   [3.1692 ms 3.1907 ms 3.2138 ms]
ripgrep-dense/150       time:   [6.7027 ms 6.7731 ms 6.8621 ms]

directory-dense/1000    time:   [83.987 ms 84.581 ms 85.224 ms]
grep-dense/1000         time:   [15.269 ms 15.349 ms 15.430 ms]
ripgrep-dense/1000      time:   [15.800 ms 15.901 ms 16.004 ms]

Git仓库

[!NOTE] 这也是一个病理性的最坏情况基准测试。块随机依赖于其他块,即使更改的块相对较少,图也很大。

这与上面的密集基准测试相同,但我们随机修改200个块,将其暂存,并运行onchg repo。基准测试最终解析了约9500个文件;正如上面所述,块/文件连接度很高。请注意,基准测试在1000个文件中总共生成了约75000个块。

git-repo/200            time:   [339.91 ms 340.70 ms 341.53 ms]
为什么这么慢??

两个原因

  1. 文件遍历是单线程的。
  2. 仅将暂存的差异渲染到stdout就需要250毫秒!

一个有趣的发现:当通过git功能使用libgit2时,基准测试需要额外约250毫秒。在深入研究后,似乎延迟的原因是libgit2中的差异行迭代器。不知何故,它比将差异渲染到stdout和解析它的时间长2倍!

真实代码库

Linux

[!WARNING] 这将在当前工作目录中克隆完整的Linux内核树(~2GB)。

$ ./benches/linux.sh
Number of lines in Linux kernel: 38566988
Root path: /home/aksiksi/repos/onchg/linux

Parsed 82259 files (2 blocks total):
  * /home/aksiksi/repos/onchg/linux/.clang-format
  * /home/aksiksi/repos/onchg/linux/.cocciconfig
  * /home/aksiksi/repos/onchg/linux/.get_maintainer.ignore
  * /home/aksiksi/repos/onchg/linux/.gitattributes
  * /home/aksiksi/repos/onchg/linux/.gitignore
  * /home/aksiksi/repos/onchg/linux/.mailmap
  * /home/aksiksi/repos/onchg/linux/.rustfmt.toml
  * /home/aksiksi/repos/onchg/linux/COPYING
  * /home/aksiksi/repos/onchg/linux/CREDITS
  * /home/aksiksi/repos/onchg/linux/Documentation/.gitignore
  * /home/aksiksi/repos/onchg/linux/Documentation/ABI/README
  * /home/aksiksi/repos/onchg/linux/Documentation/ABI/obsolete/o2cb
  * /home/aksiksi/repos/onchg/linux/Documentation/ABI/obsolete/procfs-i8k
  * /home/aksiksi/repos/onchg/linux/Documentation/ABI/obsolete/sysfs-bus-iio
  * /home/aksiksi/repos/onchg/linux/Documentation/ABI/obsolete/sysfs-bus-usb
  ... 82244 files omitted

OK.

real  0m0.628s
user  0m0.779s
sys   0m0.974s

依赖项

~10-23MB
~340K SLoC