Synchronizing org files with Unison
Introduction.
This describes how to synchronize org files using the Unison file synchronizer, as well as how to configure it to use an external tool to merge conflicting edits.
Prerequisites.
Synchronization.
Unison is a file synchronizer, thus it may be used to synchronize org files. To
configure Unison, one uses a profile which states where the things to
synchronize are as well as some options. Assuming I want to synchronize the
files in /Users/schmitta/dir1
and /Users/schmitta/dir2
, the profile would
look like this
root = /Users/schmitta/dir1 root = /Users/schmitta/dir2
In most cases Unison will be used with a remote machine. The local machine is
called the client and the remote one the server. For such remote
synchronization, the unison
binary must be installed in the server as
well. The simplest way to connect to the machine is using ssh. One should check
that unison can be found there by doing ssh user@remote unison -version
. If
unison
cannot be found in the path, one may set the servercmd
option as
indicated in the next example.
(Please see the manual section on roots for further details.)
root = /Users/schmitta/dir1 root = ssh://user@remote/relative/path/to/dir2 servercmd = /usr/bin/unison
Merging.
As Unison works on the level of files, it will trigger a conflict if both files have changed since the last synchronization. In that case one can only choose which file to keep, which is not satisfactory. Unison offers the possibility to use external tools to merge the files. There is an extensive manual section regarding this, we'll just describe how to use emacs and ediff to do it.
For better merging, we will ask unison to keep the last synchronized version of
every org file on the client; this way we can use ediff with ancestor. These
currentbackup
files may live alongside the synchronized files (with names of
the form .bak.version.name
, which is configurable) or in a central location.
Here is the modified configuration file.
root = /Users/schmitta/dir1 root = ssh://user@remote/relative/path/to/dir2 servercmd = /usr/bin/unison backupcurrent = Name *.org backuplocation = local maxbackups = 0 merge = Name *.org -> emacsclient -c --eval '(ediff-merge-files-with-ancestor "CURRENT1" "CURRENT2" "CURRENTARCH" nil "NEW")'
The backupcurrent
option tells unison to keep a backup of the last
synchronized version of every file with an org
extension. The location of the
backup should be local (alongside the file). Finally, no other backup should be
created.
Next follows the merge command. For every org file in conflict, use the command
that launches a new emacs frame calling the ediff with ancestor function. The
CURRENT1
, CURRENT2
, and CURRENTARCH
strings are replaced with the file
from the first root, the file from the second root, and the last synchronized
version. The NEW
file is where Unison expects the file to be saved (which will
be done by the ediff session).
Thus, when an org file has been modified on both hosts, an ediff session will be launched in a new frame. Closing the frame will make Unison commit the merge (it waits until the command has finished).
If one does not want to use backups, it's possible to use the simpler ediff (without ancestor) command as follows.
root = /Users/schmitta/dir1 root = ssh://user@remote/relative/path/to/dir2 servercmd = /usr/bin/unison merge = Name *.org -> emacsclient -c --eval '(ediff-merge-files "CURRENT1" "CURRENT2" nil "NEW")'