A tree command substitute that respects .gitignore, .dockerignore, .npmignore, .eslintignore, .prettierignore, .ignore — and any other ignore-file format you wire in — through one unified pipeline.
Built on the same ignore crate that powers ripgrep and fd, so traversal is fast and the ignore semantics are correct.
$ bush
.
├── Cargo.toml
├── src
│ ├── color.rs
│ ├── config.rs
│ ├── exit.rs
│ ├── filter.rs
│ ├── format.rs
│ ├── format_meta.rs
│ ├── main.rs
│ ├── tree.rs
│ └── walker.rs
└── tests
└── cli.rs
2 directories, 11 files
git clone <this-repo> bush && cd bush
cargo build --release
ln -s "$PWD/target/release/bush" ~/.local/bin/bush # symlink (auto-updates on rebuild)
# or:
cp target/release/bush ~/.local/bin/bush # copy (frozen at this build)Verify: bush --version → bush 0.2.0
bush [OPTIONS] [PATH]
-I, --ignore-file <NAME> Add an ignore filename to honor (repeatable)
--no-ignore Disable all ignore-file processing
-H, --hidden Include dotfiles and dot-directories
-L, --max-depth <N> Limit traversal depth
-o, --output <FILE> Write to FILE instead of stdout
--stdout Force stdout (overrides any output= in config)
-d, --dirs-only Show only directories
--follow-symlinks Traverse into symlinked directories
--color <auto|always|never>
Color output (auto follows TTY)
-s, --show-sizes Show file sizes in [bracketed] form
-D, --show-mtime Show modification time (YYYY-MM-DD HH:MM, UTC)
-p, --show-permissions Show file mode (unix; e.g. [rwxr-xr-x])
-l, --show-symlink-target Print "name -> target" for symlinks
--sort <name|size|mtime|none>
Sort key (default name; mtime is newest-first)
-r, --reverse Reverse the sort order
--include <GLOB> Only show files matching this glob (repeatable)
--exclude <GLOB> Exclude paths matching this glob (repeatable)
--noreport Suppress the "N directories, M files" footer
--format <tree|json|html|xml>
Output format (default tree)
--config <FILE> Use a specific config file (skips discovery)
--no-config Skip .bush config discovery; use defaults + CLI
-h, --help Print help
-V, --version Print version
bush # current directory, defaults
bush src/ -L 2 -d # 2 levels, directories only
bush -H --no-ignore # show absolutely everything
bush -s -D -p # size + mtime + permissions
bush --sort size -r # largest files first
bush --include "*.rs" # only Rust files (parent dirs auto-pruned)
bush --exclude target # hide target/ entirely
bush --format json | jq # structured output for scripting
bush --color=always | less -R # force color through pipes
bush -o STRUCTURE.txt # write to filebush reads JSON config from several locations, merging them in a defined precedence. All keys are optional; unknown keys produce a clear error so typos surface immediately.
.bush (or .bush.json) example:
{
"ignore_files": [".gitignore", ".ignore", ".dockerignore"],
"include_hidden": false,
"max_depth": 3,
"output": "STRUCTURE.txt",
"follow_symlinks": false,
"directories_only": false,
"use_ignore": true,
"color": "auto",
"show_sizes": false,
"show_mtime": false,
"show_permissions": false,
"show_symlink_target": false,
"sort": "name",
"reverse": false,
"include": [],
"exclude": [],
"no_report": false,
"format": "tree"
}CLI flags
> local .bush.json or .bush (walked up from target dir)
> $XDG_CONFIG_HOME/bush/config.json
> ~/.config/bush/config.json
> ~/.bush (legacy global)
> built-in defaults
--config <FILE>bypasses every discovery layer; CLI flags still layer on top.BUSH_CONFIG=<path>env var acts like--configbut loses to an explicit--configflag.--no-configskips all discovery; built-in defaults still apply.- Local lookup walks up from the target directory and stops at
$HOMEso~/.bushis treated as global, not local. .bush.jsonis preferred over.bushwhen both exist in the same directory.
If no config is found, bush honors:
.gitignore.ignore(the ripgrep/fd convention).dockerignore.npmignore.eslintignore.prettierignore
| Type / extension | Style |
|---|---|
| Directories | bold blue |
| Symlinks | cyan |
Executables (mode & 0o111) |
bold green |
.zip .tar .gz .bz2 .xz .7z .rar .zst |
red |
.jpg .png .gif .webp .svg .bmp .tif |
magenta |
.mp3 .wav .flac .ogg .m4a |
cyan |
.mp4 .mkv .avi .mov .webm |
magenta |
.rs .go .py .js .ts .c .cpp .java .rb .swift … |
yellow |
.json .yaml .toml .ini .conf .env |
bold yellow |
| Anything else | default |
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime error (invalid JSON in config, nonexistent path, unknown config key, glob compile error, file write error, …) |
| 2 | CLI parse error (handled by clap) |
| 130 | Interrupted by SIGINT (Ctrl-C) |
cargo test # ~275 tests across unit + integration
cargo clippy --all-targets -- -D warnings
cargo fmt -- --checkMIT — see LICENSE.