syncrclone

repository·master·Indexed 41 days ago

https://github.com/jwink3101/syncrclone

A robust, configurable, bi-directional synchronization tool for rclone remotes. It features automatic conflict resolution, rename tracking, and built-in backups for modifying or deleting operations. It supports file comparison via modification time (mtime), size, or hash, and allows for dynamic configuration using Python scripts.

Tokens
8.5K
Snippets
24
Records
44
Agent score
51%

What's inside syncrclone

  1. Understand syncrclone interruption safety and atomicity

    master

    syncrclone is not atomic. If the process is interrupted, it can be left in an incomplete state. While generally safe from data loss, you should be aware of the following consequences:

    StageConsequence of Interruption
    Pre-Sync (Delete/Backup/Move)May result in extra backup copies or files appearing moved on both sides.
    TransfersFiles not yet transferred will be identified for transfer on the next run. Files already transferred will be fine.
    Post-Sync (File Listing)If not rerun, files deleted after the interruption might be restored during the next sync because syncrclone won't know they were deleted.
    Empty Dir CleanupSome empty directories may remain and require manual cleanup (e.g., using rclone rmdirs).

    Recommendation: Always maintain backups and consider running a backup before and after a sync if data integrity is critical.

  2. Manage previous state and data storage in syncrclone

    master

    A critical aspect of bi-directional synchronization is how the 'previous state' is stored. syncrclone offers more flexibility than bisync regarding where this data lives:

    • Default Behavior: The state is stored inside each remote, named based on a unique identifier for the pair. This allows you to sync two remotes (e.g., OneDrive to Google Drive) from any machine, as the state travels with the remotes.
    • Alternative Storage: You can configure syncrclone to store the state on a different rclone remote entirely.

    Note on bisync comparison: bisync stores state in a local cache on the machine. This means if you are syncing two cloud remotes, you must use the same machine for every sync operation to maintain state consistency.

  3. Understand the syncrclone synchronization algorithm

    master

    Unlike bisync, which compares the current state to a past state to generate changes, syncrclone uses a different approach to change detection and conflict resolution:

    1. Current State Comparison: syncrclone first compares the current state of each machine/remote.
    2. Conflict Resolution: It then uses the previous (past) state to resolve conflicts and deduce the necessary changes.

    Key benefits of this approach:

    • Implicit First Sync: Because the algorithm handles the absence of previous state gracefully, there is no need for an explicit --first-sync flag. If no previous state exists, syncrclone performs a union of the two sides (no deletes), making it safer for initial setups.
    • Robustness: It is more robust to issues where the previous state might be missing or corrupted.
    • Filter Flexibility: You can safely change filters (except for --include-if-present) without needing to rerun a special 'first-sync' mode.
  4. Configure Move Tracking for Renames

    master

    Move tracking allows syncrclone to detect when a file has been renamed rather than deleted and recreated. This is only active if the renames{A/B} attribute is set in your configuration.

    Supported Attributes

    You can choose which attributes to use for tracking. The actual comparison performed depends on your selection:

    Configured AttributeActual Comparison Performed
    sizesize
    mtimesize, mtime
    hashsize, hash

    How it works

    A rename is detected on side A if:

    1. A file is marked as new on side A.
    2. The renamesA attribute matches a file in the prev list that was marked as old (deleted) on side A.
    3. The file named old on side B is marked for deletion and matches the --compare attribute.

    If these conditions are met, the file is moved on the target side instead of being transferred as a new file.

    | Attribute | Actually compared |
    |-----------|-------------------|
    | size      | size              |
    | mtime     | size,mtime        |
    | hash      | size,hash         |
  5. Configure multiple repository topologies

    master

    syncrclone performs pair-wise synchronization. You can define any pair-wise topology (such as a star topology) to sync multiple remotes.

    Requirement: Each pair in your configuration must have a unique name to avoid conflicts.

  6. Understand path resolution in syncrclone

    master

    The sync operation, including the execution of your Python configuration code, is always performed relative to the directory containing the configuration file.

    If you run: $ syncrclone /full/path/to/sync/config.py

    It is functionally identical to:

    $ cd /full/path/to/sync/
    $ syncrclone config.py

    This is critical when using local mode, as rclone will be evaluated within the .syncrclone directory located inside the config's directory.

  7. Understand the Bi-Direction Sync Algorithm

    master

    syncrclone uses a bi-directional synchronization algorithm that compares two remotes (A and B) against their previous states to determine which files are new, modified, deleted, or moved.

    Comparison Logic

    The algorithm first identifies identical files (where A == B) and removes them from both current and previous lists to simplify the remaining comparison. For the remaining files, it evaluates:

    • Missing Files: If a file exists on one side but not the other, it checks the previous list to distinguish between a new file and a deleted file.
    • Modified Files/Conflicts: If files differ, the algorithm checks their previous states. If only one side shows a modification relative to the previous state, it performs a backup and sync. If both sides show modifications, a conflict is identified.

    Conflict Resolution

    Resolution behavior is determined by your configuration. Available strategies include:

    • A or B: Force one side to win.
    • tag: Renaming both files.
    • older / newer: Based on mtime (modification time).
    • older_tag / newer_tag: Based on mtime.

    Note: If the comparison attribute is hash or size, older means smaller and newer means larger.

  8. Compare files using Size or Hash instead of ModTime

    master

    By default, syncrclone uses Modification Time (mtime) to decide if files need to be synced. For more robust comparisons, especially on remotes that do not support reliable ModTimes, you can configure comparison by size or hash.

    • Conflict Resolution: Even if you use size or hash for comparison, you can still use ModTime-based logic (like newer, older, or newer_tag) for conflict resolution. However, do not use these if your remote does not reliably support ModTimes.
    • Optimizing Hash Calculation: If your remote (like SFTP or local) requires recalculating hashes, you can use reuse_hashes(A/B) in your configuration to only recalculate them when necessary.
  9. Use Local Mode for local repositories

    master

    syncrclone provides a convenience mode for local directories.

    • Detection: In local mode, syncrclone searches upwards from your current directory for a .syncrclone/config.py file.
    • Setup: When creating a new config with --new in local mode, it is placed in .syncrclone/config.py and remoteA is automatically populated as ../.
    • Execution: To trigger local mode, simply run syncrclone while inside the target directory or a subdirectory of it. It will automatically find the configuration.

    Example of running in local mode:

    $ cd /path/to/local/files
    $ syncrclone
  10. Set dynamic remote directories and unique names

    master

    If you use the same configuration file across multiple machines, you can use Python to define dynamic paths and unique names. This prevents different machines from overwriting each other's data.

    Using Hostname for unique names

    You can use the subprocess module to fetch the hostname:

    import subprocess
    hostname = subprocess.check_output(['hostname']).decode().strip()
    name = hostname
    
    if hostname == 'machine1':
        remoteA = '/path/to/machine1/documents'
    elif hostname == 'machine2':
        remoteA = '/different/path/to/documents'
    else:
        raise ValueError(f"Unrecognized host {hostname}")
    
    remoteB = 'remoteB:path/to/remote'

    Using Environment Variables

    You can define a name in your shell profile (e.g., .bashrc) and read it in the config:

    In .bashrc:

    export SYNCRCLONE_NAME="my_computer_name"

    In config.py:

    import os
    name = os.environ['SYNCRCLONE_NAME']
    import subprocess
    hostname = subprocess.check_output(['hostname']).decode().strip()
    if hostname == 'machine1':
        remoteA = '/path/to/machine1/documents'
    elif hostname == 'machine2':
        remoteA = '/different/path/to/documents'
    else:
        raise ValueError(f"Unrecognized host {hostname}")
    
    remoteB = 'remoteB:path/to/remote'
    
    name = hostname
  11. Configure attribute settings for robust syncing

    master

    Attribute settings (like compare, conflict_mode, and renames) depend on the capabilities of your remotes.

    If both remotes support hashes, use this for the most reliable sync and clear conflict resolution:

    compare = 'hash'
    conflict_mode = 'tag'
    renamesA = 'hash'
    renamesB = 'hash'

    If hashes are not easily available, use modification time:

    compare = 'mtime'
    conflict_mode = 'newer_tag'

    Handling Remotes without Hash support (e.g., SFTP)

    If one side (e.g., B) doesn't support hashes, use reuse_hashes to save resources:

    compare = 'mtime'
    conflict_mode = 'newer_tag'
    renamesA = 'hash'
    renamesB = 'mtime'

    Worst-case: No ModTime or Hash support

    If neither remote supports ModTime or hashes, use size-based comparison. Note that rename tracking will not work reliably here:

    compare = 'size'
    conflict_mode = 'tag'
    renamesA = None
    renamesB = None
    compare = 'hash'
    conflict_mode = 'tag'
    renamesA = 'hash'
    renamesB = 'hash"
  12. Configure rclone filters using filter_flags

    master

    Syncrclone applies rclone's filtering directly via the filter_flags list. For the most robust results, use --filter rather than --include or --exclude.

    Filters are tested sequentially. Once a file matches an include or exclude rule, processing stops for that file. To exclude specific files while keeping others, place the inclusion rule first.

    Example: Exclude all .exc files except .keep.exc

    filter_flags = [
        '--filter', '+ *.keep.exc',
        '--filter', '- *.exc'
    ]

    Suggested Exclusions

    Commonly excluded patterns:

    • .DocumentRevisions-V100/**
    • .DS_Store
    • .fseventsd/**
    • .Spotlight-V100/**
    • .TemporaryItems/**
    • .Trashes/**
    filter_flags = ['--filter','+ *.keep.exc',
                    '--filter','- *.exc']