Skip to main content

Mirror dirty files between matching Windows and WSL Git repositories.

Project description

wdsync

wdsync is a cross-environment (WSL <-> Windows) Python CLI for mirroring the dirty working tree between matching WSL and Windows Git repositories without requiring a commit, push, or pull cycle.

It is built for the workflow where:

  • your primary editing happens to be on Windows for whatever reason (Flutter, VS Studio, .NET development, etc.)
  • your backend, scripts, containers, or Linux tooling run in WSL either for prod or testing parity
  • you want the WSL repo to mirror the current Windows dirty-set (staged and unstaged changes) on demand

The key design choice is that wdsync reads each source repo with the git implementation native to that repo (git on WSL, git.exe on Windows), so the sync set matches what that environment considers dirty rather than what the other side infers through translated paths.

What It Does

From inside a git repo, wdsync will:

  • auto-detect the repo identity via remote URL or root commit SHA
  • initialize local wdsync state with wdsync init
  • discover and link the peer repo via RPC with wdsync connect
  • establish the connection on both repos from a single connect call
  • remove only the saved peer link with wdsync disconnect
  • remove local wdsync state entirely with wdsync deinit
  • fetch (pull from peer into the local repo) with wdsync fetch
  • send (push from the local repo to the peer) with wdsync send
  • show a unified status of both repos with wdsync status

Note: It includes tracked unstaged files, tracked staged files, and untracked files, including nested files in untracked directories. Deleted files detected in the source are propagated to the destination. If a deletion fails due to permissions, wdsync prompts to retry with sudo. Files with local changes in the destination are never deleted. It does not yet do patch-apply checks, staging/index mirroring, or global config.

Prerequisites

Get git available in WSL and Windows (git.exe), plus rsync, wslpath, Python 3.11+, and either uv or pip; the source repo must be reachable as /mnt/<drive>/....

wdsync does not install rsync for you. It must already be available:

  • WSL/Linux: install it system-wide, for example sudo apt install rsync
  • Windows: wdsync uses wsl.exe --exec rsync, so install rsync inside the target WSL distro and verify with wsl.exe --exec rsync --version

Install

uv tool install wdsync
# or Alternative install path with `pip` after python venv:
python -m pip install wdsync

Per-Project Config

Running wdsync init inside a git repo creates:

  • A .wdsync marker file at the repo root (added to .git/info/exclude)
  • A .git/wdsync/config.json with the repo's identity (remote URL + root commit SHA)

No manual path configuration needed. The identity is used to match repos during wdsync connect.

Quick Start

# On each side (WSL and Windows), inside the repo:
wdsync init

# From either side, discover and link the peer:
wdsync connect

# After one successful connect, both repos are configured.
# Daily use from either side:
wdsync status        # see both repos' dirty files, conflicts, risk
wdsync fetch         # pull from peer into local repo
wdsync send          # push from local repo to peer

If you want to contribute or work on the codebase itself, use:

Additional references:

Project Layout

wdsync/
├── docs/                        # Design documents and roadmap
├── scripts/                     # Repo-local shell wrapper examples
│   ├── bash/
│   └── fish/
├── src/wdsync/                  # Published package source
│   ├── core/                    # Foundation: models, config, runner, exceptions, logging
│   ├── git/                     # Git state readers and porcelain parsing
│   ├── sync/                    # Sync pipeline: planner, engine, deleter, manifest, conflicts
│   ├── rpc/                     # RPC client, peer discovery, connect flow, request handlers
│   ├── output/                  # Human-readable and JSON formatters
│   ├── cli/                     # Typer CLI command definitions
│   └── shell/                   # Shell completion and helper installation
└── tests/                       # Mirrors src/ structure with unit and integration tests

Sync Rules

Scenario Behavior
File modified in source only Copied to destination
File modified in destination only Not touched (destination changes are preserved)
File modified on both sides Conflict — skipped unless --force is used
File deleted in source (tracked) Deleted from destination
File deleted in source (untracked, previously synced) Deleted from destination via pair-owned manifest tracking
File deleted then restored in source Restored in destination via git restore
Deleted file has local changes in destination Skipped to avoid data loss
Permission denied on deletion (WSL path) Prompts for sudo retry
Permission denied on deletion (Windows path) Skipped — sudo has no authority over NTFS ACLs
Path traversal attempt (../../etc/passwd) Blocked
Empty parent directory after deletion Pruned automatically
Source staged file synced to destination Copied as content only — not staged in destination
Untracked directory in source Expanded to leaf file paths and synced

Commands

Command JSON Output Purpose
wdsync init No Create .wdsync and .git/wdsync/config.json for this repo.
wdsync connect No Discover peer via RPC, verify identity, and save the link on both repos.
wdsync disconnect No Remove the saved peer from config.
wdsync deinit No Remove local wdsync state and restore the repo to a pre-init state.
wdsync fetch Yes, with wdsync fetch --json Pull peer dirty tree into the local repo. Use --force for conflicts.
wdsync send Yes, with wdsync send --json Push local dirty tree to the peer. Use --force for conflicts.
wdsync status Yes, with wdsync status --json Unified view of both repos, conflicts, and risk.
wdsync shell install No Install optional shell helpers and completions.

Git Status Labels Passed

| [unstaged] [ M] | [staged] [M ] | [both] [MM] | [new] [??] | [added] [A ] | [added+mod] [AM] | [renamed] [R ] | [copied] [C ] | [deleted] [ D] | fallback [changed] |

Shell Integration

The CLI itself is shell-agnostic once installed. Optional shell helpers can be installed with:

wdsync shell install

Override shell detection when needed:

wdsync shell install --shell fish
wdsync shell install --shell bash
wdsync shell install --shell zsh

What shell install does:

  • auto-detects fish, bash, or zsh
  • installs completion assets
  • installs a wdsync-init helper wrapper
  • installs fish function delegates when using fish

The scripts/ directory contains thin repo-local wrapper examples that delegate to the installed CLI or fall back to uv run.

  • scripts/bash/ contains executable shell wrappers
  • scripts/fish/ contains fish functions
  • the bash wrappers also work fine as standalone executable helpers from zsh

Important Behavior Notes

Each side reads its own dirty state using its native git (git on WSL, git.exe on Windows). connect is bilateral: one successful wdsync connect stores peer configuration on both repos, and either repo can then run status, fetch, or send. Repo inspection and peer-side delete/restore run natively through RPC, while file transfer still uses rsync over translated peer paths. Conflicts (files dirty on both sides) are detected and blocked by default — use --force to override. State lives under .git/wdsync/, with a repo-root .wdsync marker for visibility. disconnect removes only the peer link; deinit removes local wdsync state entirely. See the Sync Rules table above for full details.

Limitations

wdsync currently depends on WSL/Windows path interop: Windows repos must be reachable from WSL as /mnt/<drive>/..., and WSL repos must be reachable from Windows through \\wsl.localhost\... path translation. connect can be initiated from either side. File transfer still depends on translated paths via rsync, but repo inspection and peer-side delete/restore are RPC-native. wdsync does not support empty repos (at least one commit is required for identity resolution). It does not yet run patch-apply checks or support post-sync hooks.

See ARCHITECTURE.md for design decisions and IN_DEVELOPMENT.md for the roadmap.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wdsync-0.6.0.tar.gz (98.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wdsync-0.6.0-py3-none-any.whl (62.8 kB view details)

Uploaded Python 3

File details

Details for the file wdsync-0.6.0.tar.gz.

File metadata

  • Download URL: wdsync-0.6.0.tar.gz
  • Upload date:
  • Size: 98.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for wdsync-0.6.0.tar.gz
Algorithm Hash digest
SHA256 8b232471a0e66ce15a3b9fa6f1f352c5a859a81974ec470dfda7ebbf624bf153
MD5 c6636c7a748f384806c3b45f151dfecb
BLAKE2b-256 1f56dde8826a931169d8c8157ad610205e4ae309250e3372262e7b5789cc8bb1

See more details on using hashes here.

File details

Details for the file wdsync-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: wdsync-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 62.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for wdsync-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 67bd7db23d6eefac7d610773f8845cac07ef76c8683f8fcbd3669cb8c1822f1a
MD5 5ec6d08dce2045aeacc75dbdfdb70d8c
BLAKE2b-256 a5d393a0167e3027afadf9c3004fef15594e12c1eb399bfa8fa5174c23c875c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page