0.5.0
-
Expand at most one related topic in focused flag help, with explicit selections checked at
compile time. Full HTML/Markdown references retain clickable related pointers, man pages name
their targets, and console long help stays compact; none expands navigation. -
Add copyable “See also” help commands across focused option, expression, and topic pages.
Related controls follow the registry relationships, and HTML/Markdown links use stable targets.
Full/long help keeps one copy of each detailed entry and topic without expanding navigation. -
Add contextual comparison help and related-control lists. Provide comprehensive
--help=regex
separately from--help=regextype, whose alphabetical value list is followed by the structured
grammar reference and a link to--help=regex. -
Add
--compare=summaryas shorthand for status comparison with per-path output disabled and
a comparison summary enabled.--compare-select=noneor an empty value suppresses records;
summaries always count all comparison outcomes. -
Add
--summary=comparewith status counts, percentages, and a total; bare--summary
selects it during tree comparison, including empty comparisons.
0.4.0
-
Use
--block-policy-categories=LISTto select separate archive, temp, or output block controls
per INI file; unlisted categories use ordinary file controls. -
Fail before actions on invalid configuration globals and undefined selected or composed configs.
-
Reject command-line-only options and non-regular configuration targets; document directive scopes
in individual help entries and apply INI pager settings to listing output. -
Fix the user config location at the effective OS account home under
.config/xff/config;
environment variables cannot redirect trusted configuration. -
Reject missing explicit
--xffrcfiles and unreadable existing configuration, including
dangling symlinks and requested skips. Missing automatic system/user files remain normal. -
Evaluate
.xffrcadmission controls in requested selector order, including composed configs. -
Make
--no-system-configand--no-user-configexclude named sections while retaining existing
globals by default.--no-configcombines those exclusions and disables.xffrcautoloading. -
Add config-only
--require-system-globals/--no-require-system-globalsand
--require-user-globals/--no-require-user-globalspairs. Each pair appears once per permitted
file, in unsectioned globals; the system decision wins for user globals. Optional globals are
excluded only when a skip is requested. Missing automatic files remain normal. -
Add config-selectable
--allow-xffrc/--no-allow-xffrccontrols for explicitly named config
files, subject to the system policy gate and unavailable to the file being admitted. -
Document every config-only allow/deny control individually in
--help=config, including its
scope, permitted location, precedence, and multiplicity. -
Warn about overriding the same setting more than once within one logical config section while
preserving last-value-wins behavior. Deliberately accumulating settings, expression primaries,
separate sections and tiers, and all command-line repetitions remain unaffected. -
Apply named configuration and explicitly loaded config files at each command-line
selector's exact position instead of flattening them ahead of all CLI options. -
Defer regex matcher binding until the final configuration has selected grammar
and case, avoiding premature or duplicate compilation. -
Add
--re2and--pcreas direct regex-engine selectors, plus BSD/macOS
find-compatible-Eusing the configured extended grammar. -
Add the always-available
--regextype=EREengine for native POSIX extended
regular expressions, with platform locale semantics and explicit NUL handling. -
Add distinct root-spanning traversal orders:
--sort=rootssorts only the search operands,
while--sort=globalsorts the operands and walks each tree in deterministic depth-first order.
Clarify the ordering, directory read-ahead, result buffering, post-order, and score-ranking
contracts throughout the generated reference and parallel-walk design. -
Honor the selected path encoding in tree-comparison status records, including non-UTF-8 path
bytes, instead of emitting raw relative paths independently of the output configuration. -
Apply every matching system-policy safety-class denial to mixed config lines, so a destructive
primary cannot be hidden from an@destructiverule by a sensitive primary on the same line. -
Clarify concurrency and matching controls, including worker defaults and
-exectiming,
filesystem-native versus explicit case folding, block-size units, and regex grammar scope.
0.3.7
- Publish an immutable, versioned documentation website for every release, with stable links to
the native standalone HTML reference, release notes, source documents, and coverage. Historical
snapshots remain available while the site root follows the latest stable release. - Restructure the generated reference with linked Markdown contents, clearer option groupings, a
command-grammar overview, and dedicated output and tree-comparison guides. Console and man-page
rendering remain compact, while HTML retains its single native navigation block. - Make
--no-configabsolute: it now consults no system, user, or explicitly named config file,
matching the expectation set by its name regardless of other config-option ordering. - Clarify config-selector stacking,
--explainoutput, and-H/-L/-Psymlink traversal
boundaries in the generated reference. - Test the complete suite with the same Clang, size optimization, ThinLTO, and linker settings as
the published binaries, eliminating duplicate release builds while smoke-testing the final
stripped artifacts. - Upgrade PCRE2 to 10.48, including its security fixes, Unicode 17 data, matching corrections, and
memory-use improvements. - Replace the legacy Bazel setup action and implicit cache with the current opt-in setup while
retaining XFF's explicitly partitioned and size-bounded CI caches.
0.3.6
- Make
--comparerun two ordinary walks in parallel and compare their expression matches by
relative path. It no longer silently enables Git ignores, skips VCS metadata, forces tree sorting,
rejects expressions and file roots, or overrides-P/-H/-Lsymlink traversal. - Make release installation instructions explicit for both supported platforms and both binaries,
verifying each download before installation and generating its matching manual page. A generated
man page now uses the binary's invocation name, includingxff_fullor anxffsymlink. Downloads
use a unique temporary directory and clean it on exit instead of polluting the caller's directory. - Publish
xffandxff_fullsymmetrically: each has a directly downloadable, fully stripped
executable and a matching.tar.zstcontaining that executable and its separate debug file.
0.3.5
- Shrink the installed Linux release binaries with LLD string-tail and branch optimization, safe
identical-code folding, and packed relative relocations. The measured AArch64 binaries are 12.9%
smaller forxffand 7.3% smaller forxff_full, without changing macOS or non-release builds.
0.3.4
- Restore the four directly downloadable platform binaries and additionally publish each platform's
debug bundle as a level-19 Zstandard-compressed.tar.zstarchive; do not publish.tar.gz
duplicates. - Publish an attested
SHA256SUMSmanifest and include binary, checksum-verification, and generated
man-page installation instructions in every release.
0.3.3
- Build release binaries with hermetic Clang,
-Oz, and ThinLTO; strip the published executables
and include their matching debug files in one archive per platform. - Test the exact release binaries in the default Linux and macOS CI jobs, and use the same staging
path for pull-request candidates and tagged releases.
0.3.2
-
Check in formatter-clean C++ mirrors of embedded license bodies and verify each one against its
authoritative license text with Bazel, giving every platform and build configuration the same
source path while retaining deterministic regeneration checks. -
Add
--help=full:html, a standalone semantic HTML5 rendering of the complete generated reference
with responsive styling, dark-mode support, stable anchors, and no scripts or external assets.
--help=full:markdowngenerates the Markdown form;longaliasesfull, and--manremains the
conventional shortcut for--help=full:roff. New releases publish paired HTML and Markdown
references generated from the same binary.
0.3.1
- Publish GitHub build-provenance attestations for every release binary, and link each release to
its retained production coverage report and versionedXFF.mdreference on MBO Works Pages. - Derive release coverage instrumentation from the extras registry so every enabled extension is
measured automatically rather than depending on a manually maintained filter.
0.3.0
-
Transfer the repository to
mboworks/xff, rename the root Bazel module to
mboworks_xff, move coverage publishing to MBO Works Pages, and adopt the
canonical MBO Works copyright identity. Historical tags and GitHub releases
remain unchanged. -
Migrate MBO Works dependencies and repository references, including
mboworks_mboandmboworks_bashtest. -
Keep post-release CHANGELOG updates as ordinary human-reviewed pull requests;
the release helper no longer arms them for automatic merging. -
Add a non-publishing
--dry-runmode to the release helper so its clean-main,
version, tag, and archive checks can be verified before creating a signed tag. -
Add
--compare[=status|diff] LEFT RIGHTtree comparison. It applies each repository's
.gitignorerules independently, skips VCS metadata, and compares text and binary files byte for
byte. Status output reportsleft-only,right-only,identical, anddifferentpaths selected
by--compare-select; diff output produces a unified tree patch. Unlike the one-sided-diff
expression action, it inventories both roots and therefore finds right-only paths. -
Add
-similar[:WIDTH[:PCT%]] TARGET, an exact reference-file near-duplicate matcher using unique
contiguous word shingles and Jaccard overlap. It defaults to five-word shingles and an 80%
threshold, treats punctuation and whitespace as word boundaries, folds ASCII case, and skips
binary content. Whole-tree clustering remains a separate deferred reduction. -
--help=stylesnow documents xff's command-line grammar: double-dash whole-run flags versus
single-dash expression primaries, where global hoisting stops, and why the deliberate-n/-p
pair does not establish automatic one-letter aliases for other primaries. -
Fzf compatibility tests now pin exact normalized scores for documented compound queries and
nearby boundary/gap variations, rather than merely checking that successful scores lie in range.
End-to-end-topand--max-resultstests likewise pin complete ordered output. -
-fuzzy:fzfnow follows fzf's operator precedence for compound inverse, quote,
anchor, and OR expressions, and its anchors ignore whitespace at the corresponding
candidate edge. Tests carry fzf's documented compound query and upstream parser cases. -
Add
--max-results=N, an aggregate ceiling for the implicit result listing across expression
branches and independent-first/-topfilters. It does not stop the walk or truncate
reductions; explicit expression actions retain their positional semantics. -
Add exact expression-level
-top Nfuzzy result selection. Each use keeps its own stable best-N
set; actions before it run while traversing and actions after it run only for the selected entries.
Mixed fuzzy models or thresholds are rejected rather than silently imposing an arbitrary order. -
-n PATTERNand-p PATTERNare concise aliases for-name PATTERNand-path PATTERN.
They resolve through the expression registry to the canonical primaries, so their matching,
style, cost, and generated documentation are identical. -
Size inputs now spell their scale explicitly across
-size,-blocks,--block-size, and
--buffer:B/kB/MB/.../EBare decimal SI, while
KiB/MiB/.../EiBare binary IEC. Find's historicalc/w/band
k/M/G/.../Eforms remain accepted with their original meanings. Multiplication overflow
is rejected, and--help=sizedocuments the complete shared vocabulary. -
-fuzzy[:MODEL[:PCT%]] PATTERN(and the-ifuzzy/ path variants) selects
fzf, plainsequence,levenshtein(edit), or character-bigramshingles
matching and optionally requires a normalized match quality. Thefzfmodel
supports fzf's extended-search terms: space AND,|OR, exact/prefix/suffix
operators, inverse terms, and escaped spaces.
{fuzzy}and--sort=scoreuse the same 0..100 score, which
composes across an expression: AND keeps the weakest required match and OR the
best successful alternative. Scores from different patterns are therefore
comparable at the same quality threshold and predicate order does not choose the ranking accidentally.
--sort=scorerejects mixed models or thresholds rather than silently choosing between absolute similarity and
distance above each matcher-specific threshold. -
Add the removable
@xff_asarreader for Electron application archives. It exposes packed files,
external.asar.unpackedmembers, directories, and links through the ordinary archive VFS; validates
string offsets, bounds, and SHA-256 whole-file/block integrity metadata; and never invents metadata
records as files. Container readers now compose through a deterministic multi-reader registry, so
ASAR remains independent of libarchive and can coexist with every existing archive format. -
--pack-option=@FILE.jsonloads the existing writer-option vocabulary from a JSON object. String,
integer, and boolean values map to the same validation and backend translation as inline
NAME=VALUEoptions; file and inline forms compose in command-line order, so the last value for a
name still wins. -
Add the removable
@xff_brotliextra. It extends@xff_archivewith streaming.tar.br/.tbr
packing and traversal and raw.brsingle-file traversal, without letting libarchive absorb the
feature accidentally. New archives use the self-identifying RFC 9841 framing format by default;
--pack-option=framing=rawproduces legacy RFC 7932 streams, and the reader accepts both. -
Extend the archive extra's native filter coverage to lzip/LZMA (
.tar.lz,.tar.lzma,.tlz),
LZ4 (.tar.lz4), and Unix compress (.tar.Z,.taZ) for both name-gated traversal and packing.
Their single-file forms are recognized too. These codecs were already compiled into libarchive;
the public suffix and writer tables now expose them consistently, and shorthand formats accept
the samelevel/threadsoptions as their long forms. Remove the internal.brclaim because
libarchive has no Brotli filter. Read filters now use an explicit standalone allowlist, and a
committed Brotli-compressed tar proves the base extra cannot silently absorb a future libarchive
Brotli implementation that belongs behind the separate Brotli extra. -
Native phar
.phar/stub.phpis explicitly part of the ordinary member model, including fields
and statistics. A stored member at that reserved path wins over the synthetic stub, preserving
one entry per path; help also distinguishes archive-write permission from an operation that uses it. -
--archive/-zdives for real in a build with the archive extra
(--//xff:xff_archive): a container is visited as the file it is and then descends like
a directory, so its members are ordinary entries atcontainer!memberpaths that
-name,-type,-sizeand friends match unchanged.--archive=roots(the xff-family
default) dives only an archive named as a search root;--archive=all(-z+, or bare
--archive) also dives archives met mid-walk, at the position a directory of that name
would take under every--sort.-prune,-quitand-maxdepthapply to members as
they do everywhere else. Without the extra, asking for diving remains a hard error. -
Under
--archive=all, a file the walk meets is opened only if its NAME looks like a
container (.tar,.tgz,.zip,.jar,.phar, ...), so walking a source tree no longer
offers every.ccand every binary to the reader.--archive-anydrops the gate, for an
archive whose name says nothing. A file named on the command line is always opened. -
--archive-separator/--archive-prefixnow reach the walk, so printed member paths
round-trip through the flags that produced them. -
Everything that reads an entry works on members:
-content,-icontent,-rxc,
-grep,-hash,-hasheq,{hash}and{lines}read a member's bytes out of its
container, so a member's digest equals the digest of the same bytes on disk. -
--archive-depth=Nbounds how many CONTAINERS deep diving goes (default 1, so an
archive inside an archive stays a plain member). A nested container has no path of its
own, so its bytes are read out of its parent and mounted from memory;-grepand the
rest then work at any depth. The cap is its own knob rather than part of-maxdepth,
because nesting is where a decompression bomb lives. -
A plain text file is no longer mistaken for an archive. libarchive's "every format"
set includesmtree, a magic-less text format, soxff notes.txtcould report a
bogus member; the reader now enables its formats explicitly and leavesmtreeout. -
Native PHP phar archives dive: the mount path tries libarchive and then the phar reader,
so a.pharlists its members and content predicates search them - including members
compressed with deflate or bzip2, which the reader inflates itself (a phar compresses the
member, not the container, so no libarchive format or filter applies). A member whose bytes
do not match its declared uncompressed size is reported as corrupt rather than truncated.
A native phar also exposes its executable PHP stub as.phar/stub.php, matching the real member
used by tar- and zip-based phars. A stored member at that path wins without duplication; the
synthetic stub is readable and searchable but cannot be deleted because the native format
requires it. -
A write action on an archive member is refused instead of quietly doing nothing:
-delete,
-exec,-execdir,-okand-okdirreport that the member is read-only (exit 2, naming
the path) or skip it under--skip-unsupported. Previously-deleteexited 0 having deleted
nothing and-exechanded the child acontainer!memberpath it could not open. The
container itself is a real file, so actions on it still work. -
A compressed single file dives:
notes.txt.gzpresents one member namednotes.txt(the
namegzip -drestores) with its uncompressed size, and content predicates read it. A text
file merely named.gzis not claimed, and.tar.gzis still read as the archive it is. -
A whole-file-compressed phar (
.phar.gz,.phar.bz2) shows its members: the container is
decompressed first and then offered to the readers again, so what is inside decides. -
--//xff:xff_allturns on every composable extra at once. Each extra links when its own
flag or that one is set, so--config=xff_docs(which the committed reference is generated
from, and which must document the full surface) is a single line instead of one per extra
plus a comment asking whoever adds the next extra to remember. -
MemorySanitizer is a hard CI gate on Linux. The instrumented C++ standard library it
needs now comes from the toolchain itself (toolchains_llvm's--features=msanplus a
prebuilt instrumented-libc++ overlay) instead of a cmake/ninja build of the LLVM
runtimes in CI, so the cell is a plainbazel testand needs no suppression file. -
The hermetic LLVM toolchain moved to 22.1.8, matching that instrumented libc++.
0.2.0
Sharded-file awareness, hash verification, and a help system that is generated end to
end from one source of truth. The complete, always-current reference is
XFF.md.
A sharded set (data-000-of-003, data-001-of-003, ...) is one logical file, and xff
can now treat it that way instead of listing every shard.
-
--shards[=auto|of|dotnum|underscore,...]collapses each set to a single entry.
auto(the bare flag) recognizes every built-in scheme:<stem>-<i>-of-<n>,
7-Zip-style<stem>.<NNN>volumes, and<stem>_<NNN>. -
--shards-show=first|wildcard|countpicks how a collapsed set is displayed - the
representative shard's path, the masked name (arc.???), or the masked name plus the
shard count. Incomplete sets are annotated, so a missing shard is visible rather than
silently ignored. -
--shards-dedup=first|mtime|errordecides what happens when two files claim the same
index: keep the lexicographically first, keep the newest, or fail. -
--shard-pattern=REGEXregisters a custom scheme for layouts the built-ins miss. -
{shard}renders the number of shards in the set, and the size / statistics fields
aggregate across the whole set, so--summaryand--histogramcount logical files. -
-hasheq EXPECTEDis true when the file's digest equals EXPECTED, a{field}template
rendered per entry.-hasheq {def.SUMS}checks a manifest value and
! -hasheq {def.SUMS}selects drift or corruption.-hasheq:ALGO[/ENCODING]shares the
-hashspec grammar (sha256 / hex by default); hex comparison folds case. -
--summary=hashgroups matches by digest, so identical files collapse into one bucket
and the count column reads as a duplicate report. -
--time-zone-suffix=auto|always|nevercontrols whether a time preset renders its
trailing zone offset. Formats whose zone is part of their identity (zulu,
zulu-dense,asn1z) always keep theirZ. -
ASN.1
GeneralizedTimepresets:asn1(also spelledgeneralizedtime) is
YYYYMMDDHHMMSSin local time with an optional offset, andasn1zis the UTC form
with a mandatoryZ. -
--help,--help=TOPIC,--manand--markdownare now rendered from a single help
document built out of the flag and expression registries, so they cannot drift from
each other or from the implementation. -
New topics
--help=environment(every variable xff reads) and--help=help(how the
help system itself is organized), alongside the existing topic set. -
Plain help is colorized, word-wrapped with correct indentation, and respects
--width
as well as the terminal size. -
Valued flags document their values as an aligned table instead of an unreadable inline
synopsis. -
--pager[=auto|always|never](with--no-pager) pages the long documentation surfaces
and never the file listing;--manis formatted through a roff formatter first, so it
reads likeman xff. -
--summaryno longer prints a size column when nothing size-worthy was aggregated: a
count-only summary now reports just the count. -
A topic's flags are no longer listed twice in the full reference.
-
Sanitizer and lint coverage grew: clang-tidy runs as a hard CI gate over the whole
tree, and MemorySanitizer joins AddressSanitizer and ThreadSanitizer on Linux (built
against an instrumented libc++). -
Environment access is centralized behind one read-once, mutex-guarded cache, and flag
values share a single option-value parser.
0.1.0
First release of xff (eXtended File Find): a find(1)-compatible file finder
with modern extensions. This is the basic tool - the core is implemented,
tested, and verified in CI on Linux and macOS.
Everything find does works the same way. On top of that, xff adds content and
type matching, structured output, per-run summaries and histograms, file
hashing, diffing, and safe deletes, under the find / xff / xfd / rg flavors. The
complete, always-current list of what is supported is the generated reference in
XFF.md; the roadmap and open design questions are in TODO.md.
Notable features not yet built (see TODO.md):
- archive diving (
--archive) - sharded-file handling
- hash verification (files can be hashed; checking a tree against a manifest is
not done yet)