1 #+TITLE: Synchronizing org files with Unison
3 #+EMAIL: alan.schmitt@polytechnique.org
4 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
5 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
6 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
10 #+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
14 This describes how to synchronize org files using the [[http://www.cis.upenn.edu/~bcpierce/unison/][Unison file synchronizer]],
15 as well as how to configure it to use an external tool to merge conflicting
20 You should have Unison up and running. Binaries can be found [[http://www.cis.upenn.edu/~bcpierce/unison/download.html][here]] and the
21 documentation is [[http://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html][here]].
25 Unison is a file synchronizer, thus it may be used to synchronize org files. To
26 configure Unison, one uses a /profile/ which states where the things to
27 synchronize are as well as some options. Assuming I want to synchronize the
28 files in ~/Users/schmitta/dir1~ and ~/Users/schmitta/dir2~, the profile would
32 root = /Users/schmitta/dir1
33 root = /Users/schmitta/dir2
36 In most cases Unison will be used with a remote machine. The local machine is
37 called the /client/ and the remote one the /server/. For such remote
38 synchronization, the ~unison~ binary must be installed in the server as
39 well. The simplest way to connect to the machine is using ssh. One should check
40 that unison can be found there by doing ~ssh user@remote unison -version~. If
41 ~unison~ cannot be found in the path, one may set the ~servercmd~ option as
42 indicated in the next example.
44 (Please see the [[http://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html#roots][manual section on roots]] for further details.)
47 root = /Users/schmitta/dir1
48 root = ssh://user@remote/relative/path/to/dir2
50 servercmd = /usr/bin/unison
55 As Unison works on the level of files, it will trigger a /conflict/ if both
56 files have changed since the last synchronization. In that case one can only
57 choose which file to keep, which is not satisfactory. Unison offers the
58 possibility to use external tools to merge the files. There is an [[http://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html#merge][extensive
59 manual section]] regarding this, we'll just describe how to use emacs and ediff to
62 For better merging, we will ask unison to keep the last synchronized version of
63 every org file on the client; this way we can use ediff with ancestor. These
64 ~currentbackup~ files may live alongside the synchronized files (with names of
65 the form ~.bak.version.name~, which is configurable) or in a central location.
67 Here is the modified configuration file.
70 root = /Users/schmitta/dir1
71 root = ssh://user@remote/relative/path/to/dir2
73 servercmd = /usr/bin/unison
75 backupcurrent = Name *.org
76 backuplocation = local
79 merge = Name *.org -> emacsclient -c --eval '(ediff-merge-files-with-ancestor "CURRENT1" "CURRENT2" "CURRENTARCH" nil "NEW")'
83 The ~backupcurrent~ option tells unison to keep a backup of the last
84 synchronized version of every file with an ~org~ extension. The location of the
85 backup should be local (alongside the file). Finally, no other backup should be
88 Next follows the merge command. For every org file in conflict, use the command
89 that launches a new emacs frame calling the ediff with ancestor function. The
90 ~CURRENT1~, ~CURRENT2~, and ~CURRENTARCH~ strings are replaced with the file
91 from the first root, the file from the second root, and the last synchronized
92 version. The ~NEW~ file is where Unison expects the file to be saved (which will
93 be done by the ediff session).
95 Thus, when an org file has been modified on both hosts, an ediff session will be
96 launched in a new frame. Closing the frame will make Unison commit the merge (it
97 waits until the command has finished).
99 If one does not want to use backups, it's possible to use the simpler ediff
100 (without ancestor) command as follows.
103 root = /Users/schmitta/dir1
104 root = ssh://user@remote/relative/path/to/dir2
106 servercmd = /usr/bin/unison
108 merge = Name *.org -> emacsclient -c --eval '(ediff-merge-files "CURRENT1" "CURRENT2" nil "NEW")'