A tool that compares 2 directory hierarchies and shows the differences; useful for managing files on a NAS.
  • Go 99.4%
  • Makefile 0.6%
Find a file
2026-08-18 02:33:42 +02:00
cmd/mass add option -maxdepth 2026-08-18 02:33:42 +02:00
internal add option -maxdepth 2026-08-18 02:33:42 +02:00
.gitignore create the mass tool via opencode 2026-08-17 23:51:26 +02:00
Design.md add option -maxdepth 2026-08-18 02:33:42 +02:00
go.mod create the mass tool via opencode 2026-08-17 23:51:26 +02:00
go.sum create the mass tool via opencode 2026-08-17 23:51:26 +02:00
Makefile create the mass tool via opencode 2026-08-17 23:51:26 +02:00
README.md add option -maxdepth 2026-08-18 02:33:42 +02:00

mass

Side-by-side directory tree differ for the terminal. Point it at two paths and see, at a glance, what exists where — and what is missing.

Built for one job in particular: reorganizing directory/file hierarchies and making sure the new layout didn't drop anything from the old.

Usage

mass [flags] <left> <right>     # interactive TUI
mass -dump [flags] <left> <right>  # print a list instead

The TUI renders two columns — one per root — with a header naming each path. Items present in both trees are dimmed, items only on the left are red, items only on the right are green, same-named items whose sizes differ are yellow, and the missing side is left blank.

Flags

Flag Default Description
-dir-only false Only compare directories
-glob * File name glob filter (filepath.Match semantics, e.g. *.go)
-flat false Flatten each tree before comparing (keyed by -lastseg)
-lastseg 1 Flat-mode key: number of trailing path segments (1 = basename); implies -flat
-compare-size false Treat same-named files of different byte size as not identical
-maxdepth 0 Limit the walk to N levels below the root (0 = unlimited)
-no-hidden false Skip dotfiles
-ignore Ignore entries with this basename (repeatable)
-follow-symlinks false Follow symlinks (with cycle detection)
-dump false Print a list instead of launching the TUI
-scope prompt Dump scope: left, right, both, differ, all
-format prompt Dump format: paths, names
-o stdout Dump output file

-dump with no -scope/-format asks interactively. Give both flags to make it scriptable:

mass -dump -scope=left -format=paths old new | xargs -r rm

Flat mode

The -flat flag flattens each tree to a multiset and compares them. The comparison key is the last -lastseg path segments — 1 (the default) means basename, 2 means parent/basename, and so on. Passing -lastseg implies -flat, since a segment key only makes sense once the trees are flattened. So with -lastseg=1, old/a/b/x.txt matches new/c/x.txt; with -lastseg=2, pkg/a/x.txt and lib/a/x.txt match but cmd/b/x.txt does not.

Duplicates are not collapsed: three x.txt on the left and two on the right produce two matched pairs plus one left-only row, so the counts stay visibly distinct. Matching is deterministic (lexical order within each key).

Size comparison

-compare-size captures each file's byte size and refuses to match same-named files whose sizes differ, surfacing them as differ rows (yellow) instead of false both matches. In flat mode, equal sizes are matched first and the leftover same-key pairs become differ rows. This catches reorgs where a file kept its name but not its content.

Dump output

Exit code is 0 when the two trees are identical, 1 when there are differences, 2 on error.

  • -scope=left / right / both / differ — one path per line.
  • -scope=all — grouped under ## only in left: / ## only in right: / ## differ: / ## in both:.
  • -format=paths — relative paths (in flat mode, both/differ rows are leftPath\trightPath so you can trace where each file moved).
  • -format=names — basenames only.

TUI keys

Key Action
/, j/k move cursor
PgUp/PgDn scroll a page
g/G top / bottom
enter descend into the selected directory (both sides)
backspace go back up
d toggle dir-only
f set file glob
t toggle tree/flat mode
r rescan
/ filter visible rows
D dump current diff to a file
? help
q quit

Build & test

go build -o mass ./cmd/mass
go test ./...

Layout

cmd/mass          entry point, flag parsing, dump/TUI dispatch
internal/scan     recursive walk + filters (dir-only, glob, hidden, symlinks)
internal/diff     tree/flat alignment into side-by-side rows
internal/ui       bubbletea model, styles, dump serialization, select prompt