--- title: isf related: - "[pm-user-repository](pm-user-repository.md)" - "[cesga-ft3-skill](cesga-ft3-skill.md)" categories: - Tools and applications --- **isf** (interactive shared folder) keeps a local folder and a remote one the same, both ways, over ssh. Change a file on either side and it shows up on the other a moment later. ``` $ isf ./proj server:proj isf: ./proj ⇄ server:proj ↑ src/main.c ↓ notes.md isf: in sync: 1 sent, 1 received isf: watching for changes, Ctrl-C to stop ↑ src/main.c ↓ todo deleted ``` ## Requirements - Linux on both machines (isf uses inotify to see changes). - ssh access to the remote, with SFTP enabled (the OpenSSH default). - The same isf on both machines: the local one starts a copy on the remote and stops if it isn't the same version. - `curl` or `wget`, only for `isf --update`. ## Installation The [releases](https://github.com/hugoocoto/isf/releases) have isf for x86_64 and aarch64, as a static binary and as an AppImage. `nightly` is built from every push to `main`. ``` $ curl -Lo ~/.local/bin/isf https://github.com/hugoocoto/isf/releases/download/nightly/isf-x86_64 $ chmod +x ~/.local/bin/isf ``` Or build it: ``` $ git clone --recurse-submodules https://github.com/hugoocoto/isf $ cd isf $ make && cp isf ~/.local/bin/ ``` Then put the same binary on the remote, for example with `scp isf server:.local/bin/isf`. If it isn't in the `PATH` of commands run over ssh, tell isf once and it remembers: ``` $ isf ./proj server:proj -I .local/bin/isf ``` It is also available through the [pm user repository](pm-user-repository.md). `isf --update` replaces isf with the newest release; run it on both machines. ## Usage ``` isf [folder...] [[user@]host:folder] [options] ``` The first time, give the local folder and where it goes, like `scp`: ``` $ isf ./proj server:proj # ./proj with ~/proj on server $ isf ./proj server:/srv/proj # an absolute path on the remote $ isf ./proj me@server:proj -p 2222 # another user and port $ isf a b server:backups/ # backups/a and backups/b ``` A remote folder ending in `/` means "inside it". After the first run isf remembers where each folder syncs to, so `isf ./proj`, or just `isf` inside the folder, is enough. It runs until Ctrl-C; with `--once` it syncs what is different now and exits, for scripts and cron jobs. ### Options | Option | Description | |---|---| | `-p`, `--port PORT` | ssh port | | `-I`, `--isf PATH` | where isf is on the remote | | `-j`, `--jobs N` | transfer up to N files at once (default 4) | | `-n`, `--dry-run` | show what syncing would do and exit | | `--once` | sync what is different now and exit | | `-q`, `--quiet` | don't list each file | | `-v`, `--verbose` | show every file system event | | `--reset` | forget previous syncs: sync like the first time | | `--check-update`, `--update` | check for, or install, a newer release | ### Output ``` ↑ path sent to the remote (↓: received from it) ↑ path deleted removed there because it was removed here ↑ path mode only the permissions changed ↑ old → new renamed on both sides ! path changed on both sides: kept the newest, the other one is path.isf-conflict ``` ## How changes are decided - If something changed on one side only, that side wins. - If it changed on both, the newest modification time wins, and the other version is kept as `FILE.isf-conflict` on both sides. - If one side deleted it and the other edited it, the edit wins. - A file kept open while it's written (a log) is synced once the writes stop for a couple of seconds. > [!NOTE] > "Newest" compares the clocks of the two machines, so keep them in time > with NTP. isf warns at start if they disagree. isf syncs files, folders and symlinks (as symlinks) with their permissions and modification times. It does not sync owners, groups or hard links. ## Ignoring files Put patterns in a `.isfignore` file at the top of the synced folder, with `.gitignore`-like syntax: ``` # any name that matches, in any directory *.log # a trailing / matches only directories build/ # a leading / is relative to the folder /secret.txt ``` Editor temp files (`*.swp`, `*~`, `4913`, `.#*`) are always ignored. `.isfignore` is synced too, so both sides use the same patterns. ## Safety - Files are written next to their place and renamed in, so a half-written file is never seen. - If a previously synced folder is empty on one side at start, isf stops instead of deleting everything on the other side. - If the connection drops, isf reconnects and syncs what changed meanwhile. ## Limitations - A remote edit made while isf wasn't running, that kept the size and landed in the same second as the last sync, can be missed. - On network filesystems (NFS, Lustre, SMB) changes made by other machines raise no events; isf polls them every 5 seconds. - Linux limits inotify watches per user; very large trees can run out. isf tells you how to raise the limit. - IPv6 addresses need a host alias in `~/.ssh/config`. ## See also - [Source code](https://github.com/hugoocoto/isf) - [Releases](https://github.com/hugoocoto/isf/releases)