tools

various tools I have been using throughout the years
Log | Files | Refs | README | LICENSE

README.md (4027B)


      1 # dam (diff-and-merge)
      2 
      3 Syncs line-oriented text files between a local directory and a remote host
      4 over rsync, with a plan/apply workflow: `--plan` (the default) previews the
      5 changes, `--pull` applies them locally, `--push` applies them to the remote.
      6 
      7 **Merge strategy:** when a file exists on both sides, the result is the
      8 de-duplicated **union** of its local and remote lines: the local lines in
      9 their original order, with the lines only the remote has appended. When a
     10 file exists on only one side it is propagated verbatim. The merged result is
     11 the same in every mode; the mode only chooses which side gets written.
     12 
     13 Order is never changed — pushing a reordered file (say, a playlist)
     14 propagates the local order rather than fighting it. `dam` is intended for
     15 list-like data (allow-lists, hosts files, playlists, word lists). Blank lines
     16 and duplicate lines are **not** preserved by the merge.
     17 
     18 `--local` and `--remote` may be **directories or single files**. When
     19 `--local` is an existing directory, `dam` syncs the whole tree (recursing into
     20 subdirectories); otherwise it treats both paths as a single file. To mirror a
     21 directory into a new local location, create that directory first.
     22 
     23 > **dam never deletes.** Because the merge is a union, a line removed on one
     24 > side reappears from the other. Removing data is a manual edit on both sides
     25 > (or a one-shot `rsync`), not something `dam` does.
     26 
     27 Local files are written atomically (staged in a temp file and renamed), so an
     28 interrupted run can't leave a half-written file. rsync runs with
     29 `ssh -o BatchMode=yes` and an I/O `--timeout` so it fails fast in automation
     30 rather than blocking on a prompt.
     31 
     32 ## Building
     33 
     34 ```sh
     35 cargo build --release
     36 ```
     37 
     38 ## Usage
     39 
     40 ```sh
     41 dam --host <host> --local <dir> --remote <dir> [--plan | --pull | --push] [--sudo | --doas] [--exit-code] [--timeout <secs>]
     42 ```
     43 
     44 ### Modes
     45 
     46 `--plan`, `--pull`, and `--push` are mutually exclusive; `--plan` is the default.
     47 
     48 | Flag | Description |
     49 |------|-------------|
     50 | `--plan` | Show the changes each direction would make. Writes nothing. (default) |
     51 | `--pull` | Apply the merge to the local directory (remote → local) |
     52 | `--push` | Apply the merge to the remote host (local → remote) |
     53 
     54 ### Options
     55 
     56 | Flag | Description |
     57 |------|-------------|
     58 | `--host` | SSH host alias (from `~/.ssh/config`) to sync with |
     59 | `--local` | Local directory containing the text files |
     60 | `--remote` | Remote directory to sync from/to |
     61 | `--sudo` | Elevate the **remote** rsync with `sudo` (via `--rsync-path`). Mutually exclusive with `--doas` |
     62 | `--doas` | Elevate the **remote** rsync with `doas` (via `--rsync-path`). Mutually exclusive with `--sudo` |
     63 | `--exit-code` | Exit 1 if `--plan` finds pending changes. Useful in CI |
     64 | `--timeout` | rsync I/O timeout in seconds (default: 30; 0 disables) |
     65 
     66 Run `dam --help` for full details.
     67 
     68 `--sudo`/`--doas` elevate only the rsync process on the remote host (e.g. when
     69 the remote path is root-owned) by passing `--rsync-path="sudo rsync"`. The
     70 local rsync always runs unprivileged, so your own SSH config and keys are used.
     71 
     72 ### Exit status
     73 
     74 | Code | Meaning |
     75 |------|---------|
     76 | `0` | Success; nothing pending (or changes were applied with `--pull`/`--push`) |
     77 | `1` | `--exit-code` was set with `--plan` and pending changes were found |
     78 | `2` | One or more files failed to process |
     79 
     80 Progress and the summary go to **stderr**; the plan diff goes to **stdout**,
     81 so `dam --plan … > changes.diff` captures only the differences.
     82 
     83 ### Examples
     84 
     85 ```sh
     86 # See what would change on either side
     87 dam --host myserver --local ~/lists --remote /srv/lists
     88 
     89 # Bring the local directory up to date
     90 dam --host myserver --local ~/lists --remote /srv/lists --pull
     91 
     92 # Bring the remote up to date
     93 dam --host myserver --local ~/lists --remote /srv/lists --push
     94 
     95 # Sync a single file
     96 dam --host myserver --local ~/hosts.allow --remote /etc/hosts.allow --pull
     97 
     98 # Fail if the two sides have drifted
     99 dam --host myserver --local ~/lists --remote /srv/lists --plan --exit-code
    100 ```