htc
Here / There Compare — interactively sync a single file between local and a remote host over SSH.
htc opens a side-by-side diff in your terminal. For each hunk you pick the local side, the remote side, or hand-merge in your $EDITOR. When you commit, both sides are atomically rewritten to match, with backups stashed in ~/.local/share/htc/backups/.
Install
go install github.com/orutherfurd/htc@latest
Or from source:
git clone https://github.com/orutherfurd/htc
cd htc
just install # → ~/.local/bin/htc
Usage
htc <local-path> <[user@]host[:remote-path]>
Examples:
htc ~/.config/nvim/init.lua dev
htc ~/.zshrc me@server:.zshrc
htc ./notes.md server:projects/notes.md
If the remote path is omitted (htc ~/.zshrc dev), htc mirrors the local path relative to $HOME on the remote.
If the file exists on only one side, you'll be prompted to copy it across.
Key bindings
While in the TUI:
| key |
action |
j / k |
next / previous hunk |
l |
pick the local side |
r |
pick the remote side |
e |
edit the current hunk in $EDITOR |
E |
edit the whole file in $EDITOR |
u |
unset the current hunk |
c |
commit (writes both sides atomically) |
q |
abort, no changes written |
Editor integration
htc resolves your editor via $VISUAL → $EDITOR → nvim → vim → vi.
For vim and nvim, hunks open in 3-way diff mode (-d local merged remote); the cursor lands in the merged buffer. Use :diffg 1 to pull from local, :diffg 3 to pull from remote, then :wqa.
For other editors, the file opens with git-style conflict markers (<<<<<<< local / ======= / >>>>>>> remote); strip the markers, save, and the result is used as the merged content.
Safety
- Both writes are atomic (temp file + rename locally; mktemp + mv on the remote).
- Pre-merge contents of both sides are stashed in
~/.local/share/htc/backups/<timestamp>-{local,remote}-<basename> at mode 0600. The directory grows unbounded — clean it up periodically.
- Hosts beginning with
- are rejected (CVE-2017-1000117 class).
- Control bytes in displayed content are stripped to prevent ANSI smuggling from the remote.
Built with
License
MIT — see LICENSE.