1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
[]
= "codediff"
= "0.0.14"
= "2024"
= "1.88"
= "Fast, robust, syntax-aware code diffing using tree-sitter ASTs"
= "AGPL-3.0-or-later"
= "https://github.com/ivankovic/codediff"
= "README.md"
= ["diff", "ast", "tree-sitter", "cli", "git"]
= ["command-line-utilities", "development-tools", "text-processing"]
= [
"research/**",
# Checkout-only: the `stats`- and `test-fixtures`-gated [[bin]] targets below (ten total)
# cannot be built from the published/packaged crate once their source is excluded here
# (confirmed via `cargo package` + a build against the extracted tarball with `--features
# stats`: cargo reports no such bin target at all). That's intentional - these are
# dataset-analysis/dev-fixture-curation tools, not part of the published product (see README's
# Installation section) - but nothing else signals this boundary, so `cargo install codediff
# --features stats` fails with a confusing error rather than an explanatory one. Noted here for
# whoever next touches this.
"src/bin/**",
"src/test/data/**",
# Excluded for the same reason, one step removed: tests/benchmark_other_e2e.rs spawns two of
# those excluded bins through `CARGO_BIN_EXE_*`, so a packaged crate would carry a [[test]]
# target that cannot compile - `cargo package` keeps tests/ and the manifest stanza by default
# even when the binaries the test names are gone (confirmed by unpacking the .crate).
"tests/**",
# Distribution recipes, not crate content: the ebuild alone is ~300 lines of dependency names.
# Both are excluded together on purpose - flake.nix imports packaging/nix/package.nix, so
# shipping one without the other would leave a flake that cannot evaluate.
"packaging/**",
"flake.nix",
]
# No [profile.release] section existed before this (2026-07-25, speed-goal investigation) - Cargo's
# own defaults (codegen-units = 16, lto = false) leave real, free performance on the table for a
# CPU-bound algorithmic tool like this one. `lto = "fat"` lets the optimizer see across crate
# boundaries (tree-sitter, the APTED engine's own module split); `codegen-units = 1` trades slower
# release builds for better cross-function inlining within this crate. Pure compiler-flag change -
# zero source code touched, output is bit-for-bit the same algorithm, so this carries none of the
# correctness risk a concurrency or algorithmic change would.
[]
= "fat"
= 1
# Debian packaging, driven by `cargo deb` (cargo-deb), not by the Debian archive's own toolchain.
# That distinction is deliberate and worth stating: a package in Debian proper would need every one
# of this crate's 293 dependencies packaged as `librust-*-dev` first, including 24 tree-sitter
# grammar crates, almost none of which are in Debian. That path is not reachable. What this
# produces is an unofficial .deb, built by the release workflow and attached to the GitHub release,
# which installs the same files the Arch/Gentoo/Nix recipes under packaging/ do.
#
# `cargo deb` is not vendored here; install it with `cargo install cargo-deb`. See packaging/README.md.
[]
= "Marko Ivankovic <marko@ivankovic.me>"
= "2026 Marko Ivankovic"
= ["LICENSE", "0"]
= """
CodeDiff parses both sides of a diff with tree-sitter and matches their syntax \
trees, so a change is reported as what it structurally is - an insertion, \
deletion, update, or move - rather than as whichever lines happened to align. \
It runs as an interactive terminal UI, a plain-text batch formatter, or a JSON \
emitter for editor integrations, and works as a git difftool backend and a \
Jujutsu (jj) diff formatter."""
= "devel"
= "optional"
# `$auto` lets cargo-deb read the built binary's actual ELF dependencies (libc, libgcc) rather than
# guessing. Every tree-sitter grammar is compiled in statically, so there is nothing else to name.
= "$auto"
= [
["target/release/codediff", "usr/bin/", "755"],
["README.md", "usr/share/doc/codediff/README.md", "644"],
# Generated before packaging by `codediff util man` / `codediff util completions` - see the
# `deb` job in .github/workflows/release.yml and packaging/README.md. cargo-deb copies files
# from disk and cannot run the binary itself, so these must already exist or the build fails
# loudly (which is the intent: a silently man-page-less .deb is worse).
["target/dist/codediff.1", "usr/share/man/man1/", "644"],
["target/dist/codediff.bash", "usr/share/bash-completion/completions/codediff", "644"],
["target/dist/_codediff", "usr/share/zsh/vendor-completions/", "644"],
["target/dist/codediff.fish", "usr/share/fish/vendor_completions.d/", "644"],
]
[]
= "1.0.100"
= { = "4.5.51", = ["derive"] }
# Both generate packaging artifacts (shell completions, a man page) for `codediff util
# completions`/`codediff util man`, and are used only by src/main.rs - so they ride the `tui`
# feature alongside the rest of the product binary's deps rather than being unconditional, keeping
# a `default-features = false` library consumer free of them.
= { = "4.6.9", = true }
# 0.2.x, not 0.3.x, deliberately. clap_mangen 0.3 requires clap >= 4.6.6; depending on it would
# raise this crate's *effective* clap floor from the declared 4.5.51 to 4.6.6 without the manifest
# ever saying so (cargo just unifies the two requirements silently). 0.2.33 accepts clap 4.0+, so
# it imposes no floor of its own - a man-page generator is not a reason to constrain the CLI
# framework every consumer of this crate resolves against.
= { = "0.2.33", = true }
# TUI-only, but also used directly by src/bin/human_solver/'s own small debug UI - gated by the
# `tui` feature (see [features] below), not unconditional: a pure library consumer of
# `codediff::diff`/`codediff::code` has no use for it.
= { = "2.0.0", = true }
# Only used by src/stats/ (feature-gated) and the stats-gated src/bin/ tools that need concurrent
# filesystem walks (file_stats.rs, commit_stats.rs) - gated the same way as git2/rusqlite below.
= { = "0.5.15", = true }
# Used by both the stats-gated dev tools (materialize_test_diffs.rs, sample_code_pairs.rs,
# sample_test_diffs.rs, benchmark_diff_pairs.rs) and the three that only need test fixtures
# (human_solver.rs, benchmark_optimal_solutions.rs, benchmark_other.rs) - gated by `test-fixtures`,
# which `stats` also includes (see [features] below), so either feature alone is sufficient.
= { = "1.4.0", = true }
= { = "0.20.3", = true }
# Only used by src/stats.rs itself - gated the same way as the rest of the stats-only deps.
= { = "2.0.7", = true }
# Only used by src/bin/file_stats.rs (stats-gated).
= { = "0.2", = true }
# Only used by src/bin/file_stats.rs and commit_stats.rs (both stats-gated).
= { = "1.17.0", = true }
# Only used by src/stats/sampling.rs (feature-gated) and the stats-gated sample_*.rs dev tools.
= { = "0.8", = true }
# Only used by src/stats.rs and the fixture-loading helpers under src/test/ (`test-fixtures`);
# also a dev-dependency below because those helpers compile under a plain `cargo test` too.
= { = "1.12.2", = true }
# Terminal cell widths for `text_range::ScreenColumn`. Already in the lockfile via ratatui, so
# declaring it directly adds no new dependency - it makes the existing one honest, since the
# source->screen column conversion needs it outside the TUI too (headless rendering).
= "0.1.14"
# Bundled/C-compiled, and (like git2 just above) only used by two of the stats-gated src/bin/
# tools (commit_stats.rs, file_stats.rs) - gated the same way for the same reason: the main
# codediff binary/library has no use for it, so it shouldn't be a mandatory build-time cost for
# every `cargo install`/library consumer.
= { = "0.37.0", = ["bundled"], = true }
= { = "1.0.228", = ["derive"] }
= "1.0"
# Only used by tests and the `test-fixtures` helpers - gated like regex above, for the same reason.
= { = "3.10.1", = true }
# TUI-only (the event stream) - gated by `tui`.
= { = "0.3", = true }
# TUI-only, but also used directly by src/bin/commit_stats.rs for concurrent I/O - gated by `tui`,
# same reasoning as confy above.
= { = "1.0", = ["full"], = true }
= "0.25"
# TUI-only (see `tui` feature below) - also used directly by src/bin/human_solver/'s own small
# debug UI, same as confy/crossterm's siblings above.
= { = "0.26.3", = ["serde", "macros"], = true }
= { = "0.27.0", = ["serde", "event-stream"], = true }
= "0.25"
= "0.24"
= "0.23"
= "0.23"
= "0.25"
= "0.25"
= "0.23"
= "0.23"
= "0.25"
= "0.24"
= "1.1"
= "0.2"
= "0.24"
= "0.25"
= "1.2"
= "0.23.1"
= "0.24"
= "0.24"
= "0.7"
= "0.23"
= "0.4"
= "0.7.0"
= "0.7"
# Only used by src/stats/filesystem.rs - gated by `stats`.
= { = "2", = true }
= "1.0.7"
= "2.1"
# All three TUI-only (src/tui.rs sets up the subscriber, src/tui/app.rs logs) - gated by `tui`.
= { = "0.1.44", = true }
= { = "0.3.23", = ["env-filter", "serde"], = true }
= { = "0.2.1", = true }
= { = "0.28.0", = ["derive"] }
= { = "5.2", = ["default-fancy"], = true }
= { = "0.5.2", = false, = ["syntect-fancy"], = true }
# Unix only, and only for `libc::raise(SIGTSTP)` in `tui::app::suspend` - the Ctrl-Z half of
# job control, which has no Windows equivalent. Already present in Cargo.lock as a transitive
# dependency of tokio and crossterm, so naming it here does not add a crate to the tree (and so
# leaves packaging/gentoo's generated CRATES= block untouched).
[]
= "0.2"
[]
# The same two crates the `test-fixtures` feature enables, for the `#[cfg(test)]` builds that
# compile src/test/ without that feature (see lib.rs's gate on `mod test`).
= "1.12.2"
= "3.10.1"
[]
# `tui` on by default so a bare `cargo build`/`cargo install codediff` keeps producing the working
# `codediff` TUI binary, exactly as before this feature existed - a pure library consumer can still
# opt out entirely via `default-features = false`, which was not possible at all before (every one
# of these deps used to be unconditional).
= ["tui"]
# Everything the TUI (src/tui/, src/main.rs) needs. Also required by `test-fixtures`/`stats` below,
# since a couple of the dev-tool bins those gate use some of these deps directly for their own
# unrelated purposes (human_solver.rs has its own small ratatui-based debug UI; commit_stats.rs
# uses tokio for concurrent I/O) - simpler to have every dev-tool feature imply `tui` than to track
# exactly which of these 6 deps each individual bin happens to need.
= [
"dep:clap_complete",
"dep:clap_mangen",
"dep:ratatui",
"dep:crossterm",
"dep:tokio",
"dep:syntect",
"dep:two-face",
"dep:confy",
"dep:futures",
"dep:tracing",
"dep:tracing-subscriber",
"dep:tracing-error",
]
# Gates `codediff::test` (fixture-loading helpers whose data files under src/test/data/ are
# excluded from the published crate - see this file's own `exclude` list - so the module itself
# must not be part of the crate's default public surface either, or a real dependent would see
# API that's guaranteed to fail at runtime). Needed by every src/bin/ dev tool, not just the three
# that don't otherwise need `stats` (human_solver.rs, benchmark_optimal_solutions.rs,
# benchmark_other.rs) - `stats` includes it below so the other six don't need to list it twice.
= ["dep:csv", "dep:regex", "dep:tempfile", "tui"]
# Pulls in git2 (and its openssl/libssh2 build-time deps), rusqlite (a bundled, C-compiled SQLite),
# and the rest of the git-history stats/sampling tools' dependencies for the src/bin/ tools gated
# the same way. Not needed by the main codediff binary or a library consumer, so it's opt-in to
# keep `cargo install codediff` and library consumers free of that dependency chain by default.
= [
"dep:git2",
"dep:rusqlite",
"dep:crossbeam-channel",
"dep:libc",
"dep:num_cpus",
"dep:rand",
"dep:indoc",
"dep:walkdir",
"test-fixtures",
]
# The browser front end: `codediff-web` (src/web_main.rs, src/web/) serves the same diff the TUI
# draws, to a page in a local browser. Implies `tui` because it reuses that module's diff-session
# assembly (`tui::app::compute_diff_with_options`), config persistence (`tui::theme`) and syntect
# highlighting (`tui::widgets::code_viewer`) rather than duplicating them. It adds no dependency
# of its own: the HTTP/1.1 subset a single-page app on localhost needs is small enough to sit on
# the tokio `tui` already pulls in (src/web/http.rs says why that beats a server crate here), so
# Cargo.lock - and with it packaging/gentoo's generated CRATES= block - is unchanged by it.
# Off by default: `cargo install codediff` keeps producing exactly the one binary it always has.
= ["tui"]
# The one integration test in this project: `benchmark_other`'s harness end to end, driven through
# a stand-in tool (`fake_diff_tool`) whose answers are known in advance. It has to live in tests/
# rather than beside the binary's own unit tests because `CARGO_BIN_EXE_<name>` - the only way to
# get a built binary's path without shelling out to cargo - is set for integration tests only.
[[]]
= "benchmark_other_e2e"
= "tests/benchmark_other_e2e.rs"
# Both binaries it spawns are gated this way, and so is the `csv` crate it reads results with, so
# a bare `cargo test` skips this target instead of failing to build.
= ["test-fixtures"]
[[]]
= "codediff"
= "src/main.rs"
= ["tui"]
[[]]
= "codediff-web"
= "src/web_main.rs"
= ["web"]
[[]]
= "ascii_visualizer"
= "src/bin/ascii_visualizer.rs"
# Debug/dev tool only (a plain-text AST dump), no test-fixture or stats deps of its own - reuses
# `test-fixtures` as the general "checkout-only dev tool" gate rather than inventing a feature
# just for this one binary.
= ["test-fixtures"]
[[]]
= "human_solver"
= "src/bin/human_solver/main.rs"
= ["test-fixtures"]
[[]]
= "benchmark_optimal_solutions"
= "src/bin/benchmark_optimal_solutions.rs"
= ["test-fixtures"]
[[]]
= "benchmark_other"
= "src/bin/benchmark_other.rs"
= ["test-fixtures"]
[[]]
= "generate_mapping_site"
= "src/bin/generate_mapping_site.rs"
= ["test-fixtures"]
[[]]
# Research-only: scores codediff against the Alikhanifard & Tsantalis AST node-mapping oracle
# (see research/external/README.md). Needs `csv`, hence the gate; touches nothing in the product.
= "benchmark_astdiff_oracle"
= "src/bin/benchmark_astdiff_oracle.rs"
= ["test-fixtures"]
[[]]
# Not a diff tool: a stand-in that answers predictably (nothing changed / everything changed /
# a per-line hash / a crash) so `benchmark_other`'s harness can be scored against arithmetic
# instead of against a real tool whose correct answer nobody knows. Speaks difftastic's JSON, so
# `DIFFT_BIN` points at it and a real adapter is what runs. See tests/benchmark_other_e2e.rs.
= "fake_diff_tool"
= "src/bin/fake_diff_tool.rs"
# No fixture dependency of its own - gated like ascii_visualizer's entry, reusing `test-fixtures`
# as the general "checkout-only dev tool" gate so it stays out of the published crate.
= ["test-fixtures"]
[[]]
= "diff_inventory"
= "src/bin/diff_inventory.rs"
= ["test-fixtures"]
[[]]
= "analyze_human_mappings"
= "src/bin/analyze_human_mappings.rs"
= ["test-fixtures"]
[[]]
= "apted_only_worker"
= "src/bin/apted_only_worker.rs"
# Single-pair worker, spawned as a subprocess by apted_only_benchmark (stats-gated, below) -
# doesn't itself need git2/csv, but gated the same way as ascii_visualizer's own bin entry: reuse
# test-fixtures as the general "checkout-only dev tool" gate rather than inventing a feature for
# one binary with no fixture/stats dependency of its own.
= ["test-fixtures"]
[[]]
= "apted_only_benchmark"
= "src/bin/apted_only_benchmark.rs"
# Needs git2 (repo/blob access, same as benchmark_diff_pairs) and csv - both gated by `stats`.
= ["stats"]
[[]]
= "commit_stats"
= "src/bin/commit_stats.rs"
= ["stats"]
[[]]
= "materialize_test_diffs"
= "src/bin/materialize_test_diffs.rs"
= ["stats"]
[[]]
= "benchmark_diff_pairs"
= "src/bin/benchmark_diff_pairs.rs"
= ["stats"]
[[]]
= "file_stats"
= "src/bin/file_stats.rs"
= ["stats"]
[[]]
= "reclassify_tips"
= "src/bin/reclassify_tips.rs"
= ["stats"]
[[]]
= "sample_test_diffs"
= "src/bin/sample_test_diffs.rs"
= ["stats"]
[[]]
= "sample_code_pairs"
= "src/bin/sample_code_pairs.rs"
= ["stats"]
[[]]
= "diff_code_benchmark"
= false
# Loads its inputs through `codediff::test::helper`, which is gated the same way (see `test-fixtures`
# above), so a bare `cargo bench` skips this target instead of failing to build it.
= ["test-fixtures"]
[]
= "0.5"
= ["html_reports"]