An open API service indexing awesome lists of open source software.

https://github.com/ilyasturki/dircmp

Terminal TUI for comparing two directories side by side
https://github.com/ilyasturki/dircmp

cli diff directory-compare directory-diff file-comparison ink terminal tui typescript vim-keybindings

Last synced: about 2 months ago
JSON representation

Terminal TUI for comparing two directories side by side

Awesome Lists containing this project

README

          

# dircmp

Terminal TUI for comparing two directories side by side.

![dircmp screenshot](assets/screenshot.png)

[![npm version](https://img.shields.io/npm/v/@ilyasturki/dircmp)](https://www.npmjs.com/package/@ilyasturki/dircmp)
[![CI](https://github.com/ilyasturki/dircmp/actions/workflows/ci.yml/badge.svg)](https://github.com/ilyasturki/dircmp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/github/license/ilyasturki/dircmp)](LICENSE)
[![AUR version](https://img.shields.io/aur/version/dircmp)](https://aur.archlinux.org/packages/dircmp)
[![platform](https://img.shields.io/badge/platform-linux%20%7C%20macOS%20%7C%20windows-blue)](https://github.com/ilyasturki/dircmp/releases)

## Features

- **Side-by-side tree view** with color-coded diff status
- **Built-in unified diff viewer** with line-level added/removed counts
- **Copy, delete, and sync** entries between directories
- **Gitignore-style ignore patterns** (global and per-directory-pair)
- **Remote directory support** via rclone (SFTP, S3, GCS)
- **Manual directory pairing** for renamed directories
- **Content-based comparison** via SHA-256 hashing
- **Sortable entries** by name, size, date modified, or status
- **Configurable** preferences, keybindings, and external diff command

## Installation

### npx (no install)

```sh
npx @ilyasturki/dircmp
```

### npm

```sh
npm install -g @ilyasturki/dircmp
```

### Homebrew

```sh
brew install ilyasturki/dircmp/dircmp
```

### Arch Linux (AUR)

```sh
yay -S dircmp
```

### Nix

```sh
nix run github:ilyasturki/dircmp --
```

Or add to your flake inputs and install the package:

```nix
{
inputs.dircmp.url = "github:ilyasturki/dircmp";
}
```

Then add it to your installed packages:

```nix
environment.systemPackages = [
inputs.dircmp.packages.${pkgs.system}.default
];
```

### Build from source

```sh
git clone https://github.com/ilyasturki/dircmp.git
cd dircmp
bun install
bun run build
./dircmp
```

## Usage

```sh
dircmp
dircmp # direct file-to-file diff
```

### CLI subcommands

**`diff`** — print differences to stdout:

```sh
dircmp diff
dircmp diff --format json
dircmp diff --only modified
dircmp diff --stat
```

Formats: `tree` (default), `flat`, `json`. Filters: `modified`, `left-only`, `right-only`.

**`check`** — silent comparison for scripts and CI:

```sh
dircmp check # exits 0 if identical, 1 if different
dircmp check --stat # print summary before exiting
```

### Remote directories

Requires [rclone](https://rclone.org). Supports SFTP, S3, GCS, and named rclone remotes:

```sh
dircmp ./local-dir sftp://user@host/path
dircmp ./local-dir s3://bucket/prefix
dircmp ./local-dir gcs://bucket/prefix
dircmp ./local-dir myremote:path
```

### Flags

| Flag | Description |
| -------------------- | ---------------------------------------- |
| `--no-ignore` | Don't apply ignore patterns |
| `--ignore ` | Add a custom ignore pattern (repeatable) |
| `--follow-symlinks` | Follow symbolic links as their targets |
| `--help`, `-h` | Show help |
| `--version`, `-v` | Show version |

## Keybindings

All keybindings are customizable via `~/.config/dircmp/keybindings.json` or the in-app editor (`K`).

### Navigation

| Key | Action |
| --------- | ------------------- |
| `j` / `↓` | Move cursor down |
| `k` / `↑` | Move cursor up |
| `G` | Jump to last entry |
| `gg` | Jump to first entry |
| `Ctrl+d` | Half page down |
| `Ctrl+u` | Half page up |
| `Ctrl+f` | Full page down |
| `Ctrl+b` | Full page up |
| `Ctrl+e` | Scroll view down |
| `Ctrl+y` | Scroll view up |
| `Tab`/`%` | Switch panel focus |
| `H` | Focus left panel |
| `L` | Focus right panel |

### Tree

| Key | Action |
| --------- | ---------------------------------- |
| `l` / `→` | Expand directory or enter file |
| `h` / `←` | Collapse directory or go to parent |
| `Enter` | Open unified diff view |
| `zR` | Expand all directories |
| `zM` | Collapse all directories |
| `]c` | Jump to next difference |
| `[c` | Jump to previous difference |

### Actions

| Key | Action |
| ------- | ------------------------------- |
| `>` | Copy entry to right |
| `<` | Copy entry to left |
| `Space` | Copy focused side across |
| `d` | Delete selected entry |
| `y` | Yank file path to clipboard |
| `e` | Open focused entry in `$EDITOR` |
| `r` | Refresh comparison |
| `u` | Undo last action |
| `U` | Redo last undone action |
| `S` | Swap panels |
| `s` | Open sort options |
| `m` | Mark/pair renamed directory |
| `M` | Unpair directory |

### Filtering & Config

| Key | Action |
| ---- | ------------------------- |
| `/` | Filter entries by name |
| `f` | Open filter menu |
| `i` | Quick-add entry to ignore |
| `I` | Manage ignore patterns |
| `zi` | Toggle ignore filtering |
| `,` | Open preferences |
| `.` | Open actions menu |
| `K` | Open keybindings editor |
| `?` | Show all keybindings |
| `q` | Quit |

## Configuration

### Preferences

Stored in `$XDG_CONFIG_HOME/dircmp/config.json` (defaults to `~/.config/dircmp/config.json`):

```json
{
"dateLocale": "en-US",
"showHints": true,
"compareDates": true,
"compareContents": true,
"nerdFont": true,
"dirsFirst": true,
"diffCommand": "nvim -d"
}
```

| Option | Description |
| ----------------- | ------------------------------------------------ |
| `dateLocale` | Locale for date formatting |
| `showHints` | Show keyboard hints in the status bar |
| `compareDates` | Include modification dates in file comparison |
| `compareContents` | Hash file contents (SHA-256) to detect changes |
| `nerdFont` | Use Nerd Font icons (falls back to ASCII) |
| `dirsFirst` | List directories before files |
| `diffCommand` | External diff command (e.g., `nvim -d`, `delta`) |

### Ignore patterns

Patterns use gitignore syntax and are stored under `$XDG_DATA_HOME/dircmp/` (defaults to `~/.local/share/dircmp/`):

- **Global:** `ignore`
- **Per directory pair:** `pairs/.ignore`

Default ignored: `.git`, `node_modules`, `.DS_Store`.

## Manual directory pairing

When a directory has been renamed on one side, it shows up as two unmatched entries (one left-only, one right-only) instead of being compared together. Manual pairing lets you tell dircmp that two differently-named directories are logically the same, so their contents are diffed against each other.

1. Navigate to the directory on one side and press `m` — a magenta `[m]` indicator appears next to the name.
2. Switch to the other panel (`Tab`) and navigate to the corresponding renamed directory.
3. Press `m` again — the pairing is created and the two directories are compared as one entry.

Both directories must share the same parent directory. To cancel a pending mark before pairing, press `m` on the marked directory, `M`, or `Escape`. To remove an existing pairing, press `M` on the paired entry or restart the app.

## Color coding

| Color | Meaning |
| ------ | -------------------------- |
| Yellow | Modified (content differs) |
| Green | Only exists on one side |
| Red | Missing from this side |
| Dim | Identical |

## License

[MIT](LICENSE)