- Go 99.4%
- Makefile 0.6%
| cmd/mass | ||
| internal | ||
| .gitignore | ||
| Design.md | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
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/differrows areleftPath\trightPathso 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