remote_mirror is a minimal Neovim plugin for editing remote files through a local mirror backed by rsync over ssh.
- Mirror root under:
stdpath('cache') .. '/remote-mirror/<target_id>/rootfs' - Session state under:
stdpath('data') .. '/remote-mirror' - Pseudo-root mapping:
- Remote
/home/u/p/a.lua - Local
<rootfs>/home/u/p/a.lua
- Remote
- Sync entire project on
:RemoteMirrorConnectvia rsync - Open a remote file via
:RemoteMirrorEdit(after project sync it should be local already; fallback pull still exists) - Auto-push on
BufWritePostwith debounce and serialized rsync execution - Optional URI input:
rssh://host//abs/path
Place this repository in your runtimepath (or install with your plugin manager).
Then call setup:
require("remote_mirror").setup({
debounce_ms = 400,
-- By default we DO NOT expose legacy aliases (:RemoteConnect/:RemoteEdit/...)
-- enable_legacy_commands = true,
-- Ignore rules for project sync (rsync exclude patterns)
-- These required ignores are ALWAYS applied:
-- .git/ .root/ .opencode/ .sisyphus/
-- `ignore` adds more patterns on top (union).
-- ignore = { "build/" },
-- use_gitignore = true,
-- Pull behavior
-- pull_mode = "full", -- "full" | "lazy"
-- lazy_pull_mode = "dir", -- "dir" | "file" (used when pull_mode="lazy")
-- Lazy mode UX
-- When enabled (default), remote_mirror will build a placeholder tree on connect
-- so file explorers can show the full project structure without pulling contents.
-- lazy_index = true,
-- Bidirectional sync
-- When true, pushes do not overwrite newer remote files.
-- bidirectional = true,
-- Optional periodic pull (off by default)
-- auto_pull = false,
-- auto_pull_interval_ms = 5000,
-- Skip transferring files larger than this (human readable)
-- max_file_size = "50m", -- e.g. "2k", "1m", "10g", "off"
-- Rsync tuning
-- rsync_compress = false, -- adds: -z
-- rsync_bwlimit = nil, -- adds: --bwlimit=<rate> (number or string)
-- rsync_resume = false, -- adds: --partial/--partial-dir/--delay-updates (pull+push)
-- Progress UI for long-running pull operations.
-- When enabled, remote_mirror streams rsync stderr and uses `--info=progress2`.
-- A small spinner is shown until the first progress update arrives.
-- progress_ui = true,
-- ssh_args defaults to key-based auth and a short timeout:
-- { "-o", "BatchMode=yes", "-o", "ConnectTimeout=5" }
-- ssh_args = { "-o", "BatchMode=yes", "-o", "ConnectTimeout=10" },
})All of the options above can also be configured via vim.g variables set before setup().
Globals have lower precedence than setup({ ... }) options.
let g:RMC_IgnoreFiles = ["build/"]
let g:RMC_UseGitignore = 1
let g:RMC_MaxFileSize = "50m" " case-insensitive, supports k/m/g/t
let g:RMC_PullMode = "lazy" " full|lazy
let g:RMC_LazyPullMode = "dir" " dir|file
let g:RMC_LazyIndex = 1 " build placeholder tree on connect (lazy mode)
let g:RMC_Bidirectional = 1
let g:RMC_AutoPull = 0
let g:RMC_AutoPullIntervalMs = 5000
let g:RMC_RsyncCompress = 0
let g:RMC_RsyncBwlimit = "5m" " or a number (KB/s)
let g:RMC_RsyncResume = 0
let g:RMC_ProgressUI = 1:RemoteMirrorConnect {host} {remote_root} [target_id](remote_root should be a directory; if you pass a file path, it will connect to its parent dir and open that file):RemoteMirrorEdit {/abs/path | rel/path | rssh://host//abs/path}:RemoteMirrorStatus:RemoteMirrorExec {remote shell command}:RemoteMirrorPull(pull entire project):RemoteMirrorPullDir {/abs/dir|rel/dir}(pull a directory subtree):RemoteMirrorIndex(index remote tree; creates placeholder files/dirs in local mirror):RemoteMirrorBrowse(browse remote files; uses telescope.nvim if available):RemoteMirrorDiff [/abs/path|rel/path](diff local mirror vs remote content)
Legacy aliases (:RemoteConnect, :RemoteEdit, :RemoteStatus, :RemoteExec) are kept for backwards compatibility but disabled by default.
:RemoteMirrorConnect devbox /home/u/project
:RemoteMirrorEdit lua/main.lua
" Or: :RemoteMirrorEdit /home/u/project/lua/main.luaAfter saving the buffer, the file is pushed back to devbox automatically.
:RemoteMirrorConnectvalidates connectivity by runningsshand checking thatrsync,tar, andremote_rootexist on the remote.- The default
ssh_argsusesBatchMode=yes(no interactive password prompts). Set up SSH keys or overridessh_args. rsyncis invoked with argument vectors (no local shell) and--protect-args.- For push, remote parent directories are created using:
--rsync-path=mkdir -p '<escaped-dir>' && rsync- When
rsync_resume=true, remote partial dirs are also created:--rsync-path=mkdir -p '<escaped-dir>' '<escaped-dir>/.rsync-partial' && rsync
- Remote paths with NUL or newline are rejected.