Search code examples
synchronizationunison

Resolve conflicts in favour of one root for all but a few dirs


I'm trying to use Unison to synchronize files between my host machine on Windows and my guest VM on Fedora Linux.

I want all conflicts to be managed automatically:

  • all conflicts must be resolved in favour of the host (Windows) by default
  • a few selected dirs must be resolved in favour of the guest (Fedora)

Here is my default.prf file:

root = c:\www
root = socket://192.168.40.100:9999//home/ben/www

# only synchronize these dirs

path = site1.com
path = site2.com

# do not synchronize PHPStorm's config
ignore = Name .idea

# resolve conflicts in favour of the host by default
prefer = c:\www

# resolve directories managed by CLI commands on the guest in favour of the guest

preferpartial = BelowPath site1.com/node_modules -> socket://192.168.40.100:9999//home/ben/www
preferpartial = BelowPath site1.com/public/build -> socket://192.168.40.100:9999//home/ben/www
preferpartial = BelowPath site1.com/var          -> socket://192.168.40.100:9999//home/ben/www
preferpartial = BelowPath site1.com/vendor       -> socket://192.168.40.100:9999//home/ben/www

preferpartial = BelowPath site2.com/node_modules -> socket://192.168.40.100:9999//home/ben/www
preferpartial = BelowPath site2.com/vendor       -> socket://192.168.40.100:9999//home/ben/www

# automatically accept default (nonconflicting) actions
auto = true

# don't keep backup copies
backups = false

# batch mode: ask no questions at all
batch = true

# synchronize continuously (watch for changes)
repeat = watch

The above config doesn't work at all: when I create conflicting files in the host and the guest, inside and outside preferpartial directories, the conflicts are alternatively either all resolved in favour of the guest, or all in favour of the host.

(I've setup a test script where I create conflicting files with "HOST" or "GUEST" contents, start Unison, then cat the files once Unison has finished syncing).

What am I doing wrong?


Solution

  • I figured it out, after more reading through the docs, and extensive trial-and-error.

    My config above is OK.

    My issue is that I expected Unison to start from a fresh, blank state whenever I ran the unison command, and detect my files as conflicts every time. This is not the case: Unison keeps an internal archive of synced files, which can be misleading when performing tests like I was doing.

    So even though I was overwriting the files on both sides with conflicting values every time, I was always writing the same contents ("HOST" or "GUEST") to these files. Upon startup, Unison was comparing the contents of the files to its archives, only to find out that even though modification times had been updated, the contents of some of the files had not changed: when I wrote "HOST" or "GUEST" to the files, some of them already had this value.

    So in this case, Unison only detected a change in one direction, and as such would propagate it to the other side, instead of running the conflict resolution algorithm.

    I guess this makes sense and would be the expected behaviour under normal circumstances.

    That being said, I was able to successfully validate my config above in two independent ways:

    • Method 1: ignore existing archive files on startup by setting ignorearchives=true; this works, but startup syncing takes 5x more time; alternatively, removing the archives under the .unison directory on both sides would work as well
    • Method 2: when performing my tests, ensure that I write unique contents to the files, to force Unison to detect them as conflicts; to do this, I replaced:
      echo "HOST" > site1.com/conflict1
      
      with:
      echo "HOST @ $(date)" > site1.com/conflict
      

    In both cases, Unison now considers the files as conflicting, and applies my prefer rules as expected.