Sreedev Kodichath cc7144afbc [ADD] resolver: bulk duplicate resolution via --keep/--force
The tool could only act on duplicates through the default listing or the
manual per-group interactive prompt, with no way to resolve groups in bulk
by a rule. This blocked any scripted or automated use, and bulk resolution
is the top item in the README's proposed operations.

Add a resolver module that reduces each duplicate group to a single kept
file. --keep <newest|oldest|first|last|shortest|shallowest> selects the
keeper: newest/oldest by mtime, first/last and shortest/shallowest by
path, each resolved to a unique keeper through total-order path tiebreaks
so the outcome is deterministic and order-independent. Selection is a pure,
independently tested function; deletion is a separate step.

Deletion is guarded: --keep alone previews (KEEP/DELETE lines and a
would-free summary) and removes nothing, so a mistaken invocation is
harmless. --force performs the deletion, continues past individual
failures, reports freed space, and exits non-zero if any file could not be
removed. clap enforces that --keep excludes --interactive and that --force
requires --keep, so misuse fails before any file is touched.

Also fixes the interactive table's column width, which sized on path
component count instead of path string length so the columns never aligned.
2026-07-26 19:27:49 +02:00
2025-07-19 13:41:52 +00:00
2025-07-19 11:33:52 +00:00
2023-01-13 01:09:31 -05:00
2022-12-24 19:18:17 -05:00
2025-07-13 19:03:14 -04:00

Deduplicator

Find, Sort, Filter & Delete duplicate files

Usage

find,filter and delete duplicate files

Usage: deduplicator [OPTIONS] [scan_dir_path]

Arguments:
  [scan_dir_path]  Run Deduplicator on dir different from pwd (e.g., ~/Pictures )

Options:
  -T, --exclude-types <EXCLUDE_TYPES>  Exclude Filetypes [default = none]
  -t, --types <TYPES>                  Filetypes to deduplicate [default = all]
  -i, --interactive                    Delete files interactively
  -m, --min-size <MIN_SIZE>            Minimum filesize of duplicates to scan (e.g., 100B/1K/2M/3G/4T) [default: 1b]
  -D, --max-depth <MAX_DEPTH>          Max Depth to scan while looking for duplicates
  -d, --min-depth <MIN_DEPTH>          Min Depth to scan while looking for duplicates
  -f, --follow-links                   Follow links while scanning directories
  -s, --strict                         Guarantees that two files are duplicate (performs a full hash)
  -p, --progress                       Show Progress spinners & metrics
  -h, --help                           Print help
  -V, --version                        Print version

Examples

# Scan for duplicates recursively from the current dir, only look for png, jpg & pdf file types & interactively delete files
deduplicator -t pdf,jpg,png -i

# Scan for duplicates recursively from current dir, exclude png and jpg file types
deduplicator -T jpg,png

# Scan for duplicates recursively from the ~/Pictures dir, only look for png, jpeg, jpg & pdf file types & interactively delete files
deduplicator ~/Pictures/ -t png,jpeg,jpg,pdf -i

# Scan for duplicates in the ~/Pictures without recursing into subdirectories
deduplicator ~/Pictures --max-depth 0

# look for duplicates in the ~/.config directory while also recursing into symbolic link paths
deduplicator ~/.config --follow-links

# scan for duplicates that are greater than 100mb in the ~/Media directory
deduplicator ~/Media --min-size 100mb

Demo

demo

Installation

Currently, you can only install deduplicator using cargo package manager.

Cargo

GxHash relies on aes hardware acceleration, so please set RUSTFLAGS to "-C target-feature=+aes" or "-C target-cpu=native" before installing.

install from crates.io (stable)

$ RUSTFLAGS="-C target-cpu=native" cargo install deduplicator

# or

$ RUSTFLAGS="-C target-feature=+aes,+sse2" cargo install deduplicator

install from git (nightly)

$ RUSTFLAGS="-C target-cpu=native" cargo install deduplicator --git https://github.com/sreedevk/deduplicator

# or

$ RUSTFLAGS="-C target-feature=+aes,+sse2" cargo install --git https://github.com/sreedevk/deduplicator

Manual Installation

  • Download the right pre-compiled binary archive for your platform from github release page.
  • Decompress it using tar -zxvf <archive>.tar.gz
  • Move it to a directory included in $PATH.
    • ideally /usr/local/bin/.

Performance

Deduplicator uses size comparison and GxHash to quickly check a large number of files to find duplicates. its also heavily parallelized. The default behavior of deduplicator is to only hash the first page (4K) of the file. This is to ensure that performance is the default priority. You can modify this behavior by using the --strict flag which will hash the whole file and ensure that 2 files are indeed duplicates. I'll add benchmarks in future versions.

Benchmarks

I've used hyperfine to run deduplicator on files generated by the rake file at rakelib/benchmark.rake. The Benchmarking accuracy can further be improved by isolating runs inside restricted docker containers. I'll include that in the future. For now, here's the hyperfine output on my i7-12800H laptop with 32G of RAM.

Fewer Large Files

# hyperfine -N --warmup 80 './target/release/deduplicator bench_artifacts'
Benchmark 1: ./target/release/deduplicator bench_artifacts
  Time (mean ± σ):       2.2 ms ±   0.4 ms    [User: 2.2 ms, System: 4.4 ms]
  Range (min … max):     1.3 ms …   7.1 ms    1522 runs

dust 'bench_artifacts'

 54M   ┌── file_0_fwds.bin   │████                                                               │   2%
122M   ├── file_1_fwds.bin   │████████                                                           │   5%
390M   ├── file_0_fwdcbss.bin│██████████████████████████                                         │  15%
390M   ├── file_0_fwscas.bin │██████████████████████████                                         │  15%
390M   ├── file_0_fwss.bin   │██████████████████████████                                         │  15%
390M   ├── file_1_fwdcbss.bin│██████████████████████████                                         │  15%
390M   ├── file_1_fwscas.bin │██████████████████████████                                         │  15%
390M   ├── file_1_fwss.bin   │██████████████████████████                                         │  15%
2.5G ┌─┴ bench_artifacts     │██████████████████████████████████████████████████████████████████ │ 100%

Many Small Files

# hyperfine --warmup 20 './target/release/deduplicator bench_artifacts'
Benchmark 1: ./target/release/deduplicator bench_artifacts
  Time (mean ± σ):      40.1 ms ±   2.3 ms    [User: 251.0 ms, System: 277.3 ms]
  Range (min … max):    35.0 ms …  45.9 ms    72 runs

dust 'bench_artifacts'
3.9M   ┌── file_992_fwscas.bin │█                                                                │   0%
3.9M   ├── file_992_fwss.bin   │█                                                                │   0%
3.9M   ├── file_993_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_993_fwscas.bin │█                                                                │   0%
3.9M   ├── file_993_fwss.bin   │█                                                                │   0%
3.9M   ├── file_994_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_994_fwscas.bin │█                                                                │   0%
3.9M   ├── file_994_fwss.bin   │█                                                                │   0%
3.9M   ├── file_995_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_995_fwscas.bin │█                                                                │   0%
3.9M   ├── file_995_fwss.bin   │█                                                                │   0%
3.9M   ├── file_996_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_996_fwscas.bin │█                                                                │   0%
3.9M   ├── file_996_fwss.bin   │█                                                                │   0%
3.9M   ├── file_997_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_997_fwscas.bin │█                                                                │   0%
3.9M   ├── file_997_fwss.bin   │█                                                                │   0%
3.9M   ├── file_998_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_998_fwscas.bin │█                                                                │   0%
3.9M   ├── file_998_fwss.bin   │█                                                                │   0%
3.9M   ├── file_999_fwdcbss.bin│█                                                                │   0%
3.9M   ├── file_999_fwscas.bin │█                                                                │   0%
3.9M   ├── file_999_fwss.bin   │█                                                                │   0%
3.9M   ├── file_99_fwdcbss.bin │█                                                                │   0%
3.9M   ├── file_99_fwscas.bin  │█                                                                │   0%
3.9M   ├── file_99_fwss.bin    │█                                                                │   0%
3.9M   ├── file_9_fwdcbss.bin  │█                                                                │   0%
3.9M   ├── file_9_fwscas.bin   │█                                                                │   0%
3.9M   ├── file_9_fwss.bin     │█                                                                │   0%
 11G ┌─┴ bench_artifacts       │████████████████████████████████████████████████████████████████ │ 100%

proposed

  • parallelization

    • scanning + processing sw + processing hw + formatting + printing
  • user supplied cache file path for faster re-runs

  • hardlinks / symlinks support

  • max file path size should use the last set of duplicates

  • add more unit tests

    • test against different filesystems
    • test against different file name encodings
  • restore json output (was removed in 0.3 due to quality issues)

  • fix memory leak on very large filesystems

    • maybe use a bloom filter
    • reduce FileInfo size
  • tui

  • change the default hashing method to include the first & last page of a file (8K)

  • provide option to localize duplicate detection to arbitrary levels relative to current directory

  • localize file meta store locks to sub path levels to avoid global lock contention from multiple threads.

  • bulk operations

    • --keep-latest
    • --keep-oldest
    • --keep-last-modified
    • --keep-first-modified
  • fix: partial hash collision - a file full of null bytes ("\0") and an empty file. This is a known trade off in gxhash.

    • include initial pages and final pages of the file
    • append the offset between the last initial page hashed and the first final page hashed in the content passed to the hasher.
  • potential optimizations

    • lookup the memory efficiency gains if instead of directly inserting into a hashmap, deduplicator looksup the file in a bloom filter. this way, the duplicate store hashmap does not require to be locked for every single file. The bloom filter can be stored in an atomically updateable type to improve performance as well.

v0.3.2

  • fix: single file groups are printed to screen
  • fix: --exclude-types and --types flag behave identically.

v0.3.1

  • parallelization
    • (scanning + processing sw + processing hw) & formatting & printing
  • remove formatting step and write directly to stdout
  • simplify output to improve performance
  • increase the number of pages hashed in partial hashing
  • updated dependencies
  • fix: full file hash collision between a file full of null bytes ("\0") and an empty file. This is a known trade off in gxhash.
    • appending the file size at the end of content before hashing.
  • created an automated release pipeline

v0.3.0

  • parallelization
    • (scanning) + (processing sw & processing hw & formatting & printing)
  • reduce cloning values on the heap
  • add a partial hashing mode (--strict)
  • add unit tests
  • add silent mode
  • update documentation
  • remove color output
  • progress bar improvements
    • use progress bar groups
  • remove broken json rendering
  • add benchmarks
Languages
Rust 92.2%
Ruby 7.8%