Skip to content

Feature request: add option to replace remote symlinks with regular files ONLY when the local file has actual changes #555

@Zorzi23

Description

@Zorzi23

Feature request: add option to replace remote symlinks with regular files ONLY when the local file has actual changes


📝 Description

Problem Statement

When synchronizing files with Mutagen, if the remote (beta) endpoint contains a symbolic link at a path where the local (alpha) endpoint has a regular file, Mutagen currently cannot synchronize that file.

Depending on the --symlink-mode setting:

Mode Behavior with remote symlink
portable (default) Synchronization halts if a non-portable symlink is detected
ignore Symlink is ignored (file is not synced)
posix-raw Propagates raw symlink (does not convert to file)

None of these modes removes the remote symlink and replaces it with the regular file from the local endpoint.

Use Case

In CI/CD pipelines for creating review environments, a common optimization is to create a working directory using symlinks pointing to a base branch (e.g., main) to save disk space. For example:

cp -rns /path/to/main/* /path/to/review/branch/

This creates absolute symlinks in the review directory pointing back to main.

However, during development, the developer needs to edit files in this review environment. The desired behavior is:

  • When a file is actually modified locally (content change) and synced via Mutagen
  • If the remote path is currently a symlink, Mutagen should:
    1. Remove the symlink on the remote side
    2. Create a regular file on the remote side with the modified content from the local side
  • If the local file has not been modified (no content change), the remote symlink should remain untouched

Why this matters

Replacing symlinks unconditionally would be destructive and inefficient:

  • Unnecessary disk usage: Replacing every symlink with a regular file would defeat the space-saving purpose of using symlinks in the first place
  • Unnecessary network transfers: Syncing files that haven't changed wastes bandwidth
  • Unexpected behavior: Developers would lose the symlink structure for files they never intended to modify

The replacement should be strictly tied to local file modifications.

Proposed Solution

Add a new --symlink-mode option, for example:

--symlink-mode=replace-on-modify

Or a more specific flag:

--replace-remote-symlinks-on-modify

Behavior:

  1. Mutagen detects that the remote (beta) path is a symbolic link
  2. Mutagen checks if the local (alpha) file has actual content changes (not just metadata)
  3. If changed:
    • Remove the symbolic link on the remote side
    • Create a regular file on the remote side with the changed content
  4. If not changed:
    • Leave the remote symlink untouched
    • No network transfer occurs

In one-way-replica mode (local → remote), this would work as follows:

Scenario Remote is symlink? Local has changes? Action
1 Yes No ⏭️ Nothing (preserve symlink)
2 Yes Yes 🔄 Remove symlink, create file
3 No No ⏭️ Nothing
4 No Yes 📝 Normal file sync

Alternative Considered

A workaround exists: manually removing symlinks on the remote side before syncing:

ssh user@host "find /remote/path -type l -delete"
mutagen sync flush session-name

However, this requires:

  • Manual intervention or additional scripting
  • An extra SSH connection
  • Cannot be done per-file based on whether the file actually changed
  • Removes ALL symlinks, defeating the space-saving purpose

Additional Context

  • Mutagen version: v0.18.1 - windows-amd64
  • OS:
    • Alpha: Windows 10
    • Beta: Linux Debian
  • Sync mode: one-way-replica

Related documentation: https://mutagen.io/documentation/synchronization/symbolic-links/


🏷️ Labels Suggestion

  • enhancement
  • feature-request
  • symlinks
  • performance

📋 Example Scenario

Initial state on remote (symlinks pointing to main branch):

/review/branch/
├── index.php -> ../main/index.php      (symlink)
├── config.php -> ../main/config.php    (symlink)
├── style.css -> ../main/style.css      (symlink)
└── .env -> ../main/.env                (symlink)

Developer modifies only index.php locally:

  • index.php: CHANGED → should become a regular file on remote
  • config.php: unchanged → remains symlink
  • style.css: unchanged → remains symlink
  • .env: unchanged → remains symlink

Expected remote state after sync:

/review/branch/
├── index.php          (regular file - REPLACED)
├── config.php -> ../main/config.php    (symlink - preserved)
├── style.css -> ../main/style.css      (symlink - preserved)
└── .env -> ../main/.env                (symlink - preserved)

🔗 Related Discussion

This behavior is similar to how rsync handles symlinks with --copy-links and --safe-links, but with the crucial difference that replacement should be conditional on file modification.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions