Metadata-Version: 2.4
Name: yd-cli
Version: 0.14
Summary: CLI tool to synchronize directories using rsync.
Author: Christian Heinze
License-Expression: MIT
License-File: LICENSES/MIT.txt
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Archiving
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Dist: msgspec>=0.21
Requires-Dist: rich>=15
Requires-Dist: typer>=0.27
Requires-Python: >=3.14
Project-URL: Repository, https://codeberg.org/christianheinze/yd-cli
Description-Content-Type: text/markdown

# Directory synchronization tool `yd`

Build and execute `rsync` commands from *TOML* configuration files.

## (Un)Install

To install, run

```bash
uv tool install yd-cli
```

Then run

```bash
yd --install-completion
```

to install auto-completion in your shell.

In `bash`, the completion code is stored in `~/.bash_completions/yd.sh` and that file is sourced from `~/.bashrc`.
Remove both and call `uv tool uninstall yd-cli` to remove this tool.
The below mentioned configuration files need to be removed separately.

## Create a config

Create a new configuration called `photos` (using a positive number of `.` as name is not allowed) with:

```bash
yd edit --new photos
```

This command

- creates `~/.config/yd/photos.toml` (or under `$XDG_CONFIG_HOME/yd` if set; relative paths are resolved from your home directory), and
- opens it with the executable selected via `EDITOR`, or `nvim` when `EDITOR` is unset. The selected editor must be available on `PATH`.

Reopen an existing configuration with:

```bash
yd edit photos
```

## Configuration format

### Example

```toml
# Phone backup
src_home = "/home/alice"
target_home = "/mnt/backup"
backup = "deleted/%Y-%m-%d"
exclude = [".venv/", "__pycache__/"]

[[commands]]
src = "Documents"
exclude = ["*.log"]

[[commands]]
src = "Pictures"
target = "pics-%Y-%m-%d"
delete_extra = "never"
```

Leading comment lines directly at the top of the file are treated as the configuration description and are shown by `yd ls`.

### Top-level options

| Key | Meaning |
| --- | --- |
| `src_home` | Base directory for all `src` paths. Relative paths are resolved from your home directory. |
| `target_home` | Base directory for all target paths. Relative paths are resolved from your home directory. |
| `mtp_target` | Use in-place syncing for MTP targets. |
| `backup` | Backup directory for replaced or deleted files. Relative paths are resolved from `target_home`. `strftime` placeholders are supported. |
| `exclude` | Exclude patterns applied to every command. |

`mtp_target` matters because `rsync` normally copies to a temporary file and renames it afterward, but that is not possible when syncing via *MTP*.

### Command options

| Key | Meaning |
| --- | --- |
| `src` | Relative source directory below `src_home`. |
| `target` | Relative target directory below `target_home`; defaults to `src`. `strftime` placeholders `%Y`, `%m`, and `%d` are supported. Each may appear at most once; no other use of `%` is allowed. A newly created dated target is removed if it contains only directories after synchronization. |
| `delete_extra` | `"when_safe"` deletes files in the target that do not exist in the source when no target overlaps; `"never"` retains them. Defaults to `"when_safe"`. |
| `exclude` | Extra exclude patterns for this command only. |

### Notes

- `src` and `target` must stay within `src_home` and `target_home` after path resolution.
- Literal targets are retained when they contain only empty directories. Dated targets serve as snapshots: if a dated target did not exist before synchronization and contains neither files nor symlinks afterward, `yd` removes its empty directory tree.
- `src_home` or `target_home` may be omitted from the configuration, but every missing value must then be supplied when running (via CLI parameters).
- Exactly one of `--src-home -` and `--target-home -` may read from standard input in a single run.
- If `backup` is omitted, `yd` creates a timestamped backup directory automatically unless `--no-backup` is specified.
- If a configured source directory exists but is empty, `yd` reports that no synchronization was performed for that command. E.g., forgetting to mount an external drive does not delete all corresponding copies on hard-drive.
- If a valid date placeholder is included in the `target`, then the `target_home` is scanned for matching directories (possibly) created by this rule before today.
  The 20 most recent matches are included as additional comparison destinations in the synchronization: if a file in `src` is included in one of those reference directories, then it will not be copied to `target`.

## Run a config

Run a saved configuration by name:

```bash
yd run photos
```

### Options

| Option | Meaning |
| --- | --- |
| `--dry-run` | Show what would happen without changing files. |
| `--no-backup` | Disable backup handling for this run. |
| `--keep-newer` | Skip updates when the target file is newer. |
| `--rename-speedup` | Enable `rsync` options tuned for rename-heavy targets. This may require more disk space on the target. |
| `--src-home PATH` | Override `src_home` from the config. Use `-` to read the value from standard input. |
| `--target-home PATH` | Override `target_home` from the config. Use `-` to read the value from standard input. |

### Synchronization semantics

- Synchronization does not cross mounted filesystems, and mount-point directories are omitted from the target.
- Symlinks are copied as symlinks; symlinks pointing outside the individual source directories are ignored.
- Permissions, modification times, access times, and hard links are preserved.
- File owner or group are not preserved.

## Environment variables

| Name | Description |
| --- | --- |
| `XDG_CONFIG_HOME` | Base directory for configurations. `yd` stores them in `yd` below this path. Defaults to `.config` below the home directory; relative values are resolved from the home directory. |
| `EDITOR` | Editor used by `yd edit`. Defaults to `nvim`. The selected executable must be available on `PATH`. |
| `YD_NPROC` | Maximum number of synchronizations that may run concurrently. Must be a positive integer. Defaults to one less than the available CPU count, with a minimum of one. |
| `YD_RSYNC` | Executable name or path used for `rsync`. Defaults to `rsync`; it must be discoverable on `PATH` when a name is supplied. |
| `YD_ECHO` | Executable name or path used to print messages. Defaults to `echo`; if it cannot be found, `yd` uses its built-in alternative. |
| `YD_LOGLEVEL` | Numeric [Python logging level](https://docs.python.org/3/library/logging.html#logging-levels), such as `10` for debug, `20` for info, or `40` for errors. When set, `yd` appends *JSON* Lines records to `.yd.jsonl` in the current working directory. Records contain `time`, `level`, `logger`, and `message` fields, plus exception `type`, `message`, and `traceback` when applicable. |

Logging is effectively disabled by default. Enable it when diagnosing a failed or interrupted run.

## List configs

List available configurations with:

```bash
yd ls
```

This shows the configuration name together with the optional leading-comment description.

## Why `yd`?

- Has one character from `synchronize` and one from `directory`.
- Easy to type with both *QWERTZ* and *QWERTY* keyboards.
- Name was still available on *PyPI*.
