This is the last part of a 3 part series:
- Introduction, Logseq and Termux on mobile
- Improving Logseq and Org-roam interoperability
- Automating synchronisation (this part)
This post will cover automating synchronisation between the 2 mediums, to the point where you will almost forget it is there. It’s not perfect (merge conflicts sometime arise) but for the most part, it works flawlessly.
Automatic synchronisation in Emacs
There’s many ways to automatically synchronise Git repositories on your desktop. I wrote an Emacs package for it which has the added benefit to let me execute Emacs hooks post merge. Skip this part if you already have a solution that you are satisfied with.
Pros of my package:
- Self-contained in Emacs, no need to setup external daemons
- Simple configuration with
.dir-locals.el
- Works with
magit
- Provides a hook to run tasks after each merge
- Works asynchronously, except when merging to avoid Emacs stepping on its own toes
- Commits and pushes as necessary (when files are saved)
Cons (unsurprisingly):
- Only works within Emacs and while it is running
- Starts synchronisation only when Emacs visits a file within the repository
You can grab it at https://github.com/sbougerel/autosync-magit.
With straight.el
and use-package.el
, your configuration could look like:
(use-package autosync-magit
:straight (:host github
:repo "sbougerel/autosync-magit"
:files ("*.el")))
Once you have this package installed, head to your Notes/
directory, and create a .dir-locals.el
file with the following content:
((nil . ((autosync-magit-commit-message . "Update from desktop")
(autosync-magit-pull-timer . 600)
(eval add-hook 'autosync-magit-after-merge-hook #'logseq-org-roam nil t)
(mode . autosync-magit)
(mode . auto-revert))))
- The first line specifies the commit message used every time a commit is created.
- The second line specifies the background pull timer: the interval between pulls done in the background, here set to 10 minutes.
In addition to the background pull timer, by default
autosync-magit
pulls files when they are visited. The package is smart enough to throttle pulls when Emacs visits files in the repository in quick succession (e.g. duringlogseq-org-roam
calls). You can change the throttle value but you should never set it to0
: sincelogseq-org-roam
causes files to be visited in quick succession, it callsautosync-magit
often with each calls potentially callinglogseq-org-roam
again due to a merge. - The third line tells Emacs to run
logseq-org-roam
once merge is complete. This ensures that all files edited with Logseq are now converted to Org-roam automatically. - The fourth line activates the minor mode for files in this directory. None of the above works without this.
- The last line sets up
auto-revert
on files that have been modified, either due to merge or post-processing withlogseq-org-roam
.
The first time you visit a file after the creation of the .dir-locals.el
, Emacs will ask you whether you should trust these settings? Never trust anything you read or copy from Internet! With the warning out of the way, proceed to accept the settings permanently 😇.
With this done, the workflow on your desktop changes from:
git pull
- Invoke
logseq-org-roam
in Emacs - Edit notes
git commit -a -m "Update from desktop"; git push
To:
- Edit notes (and Save)
Didn’t I say it’s almost magic? I’ve tested it for a while now, but if you face any problem don’t hesitate to raise an issue or a PR. Now let’s keep the magic going with the mobile setup.
Automatic synchronisation on mobile with Tasker
This section assumes you’ve already gone through part 1, meaning you already have Logseq and Termux on your mobile. Similar to part 1, this section only applies to Android; there could be iOS alternatives but you’ll have to adapt the following.
The first thing is to add a couple of scripts on your Termux home. There’s nothing Termux-specific about these scripts, so you can test them on your desktop first.
Copy the content below in file named ~/pull-notes
:
#!/usr/bin/bash
set -euo pipefail
cd ~/Notes
git fetch &>/dev/null
if [ "$(git rev-parse @{0})" == "$(git rev-parse @{u})" ]; then
echo Nothing to do
elif git merge-base --is-ancestor @{0} @{u}; then
git merge &>/dev/null
echo Pulled
elif git merge-base --is-ancestor @{u} @{0}; then
echo Nothing to do
else
git merge &>/dev/null
echo Conflict
fi
Similarly, copy the content below in a file ~/push-notes
:
#!/usr/bin/bash
set -euo pipefail
cd ~/Notes
git add -A &>/dev/null
git commit -m "Update from Android" &>/dev/null || true
if [ "$(git rev-parse @{0})" != "$(git rev-parse @{u})" ]; then
git push &>/dev/null
echo Pushed
else
echo Nothing to do
fi
You can test these 2 files after you’ve made them executable:
chmod a+x pull_notes
chmod a+x push_notes
The roles of these 2 files is to print a simple one-line statement about the results of git synchronisation operations.
The application Tasker (installed next) calls these 2 scripts to perform the git operations.
First move the scripts to a specific location so that Tasker can call them with a path relative to HOME
:
mkdir -p ~/.termux/tasker
mv push-notes pull-notes ~/.termux/tasker
Now grab Tasker from the Play Store and install the companion termux-tasker app from Github or F-droid. Tasker is not free but at less that USD$3 it’s a small price to pay for all the time it will save you (If you know of a free alternative that is on par with the functionalities presented below, I’d love to hear about it).
The first time you open Tasker, it will invite you to watch tutorials, etc. Tasker has a ton of useful features outside of this project, which I’ll let you discover on your own.
Before I describe this Tasker project, let’s go through some definitions of Tasker’s entities:
- Profiles describe events or situations which (in general) are used as a trigger for tasks
- Tasks describe operations to perform, and they are made up of a set of actions
- Scenes represents UI elements that can displays or used for input during task execution
- Vars allow tasks to store information across execution or even share information across tasks
This Tasker project consists of setting up 2 tasks and 2 profiles.
- Task LogseqPull sets a throttle, calls the script
~/push-notes
created above and displays its result in a toaster. - Task LogseqPush calls the script
~/pull-notes
created above and displays its result in a toaster. - Profile Logseq triggers LogseqPull when we launch Logseq on our phone and triggers LogseqPush when we exit it.
- Finally, profile Every 30m triggers LogseqPull every 30 minutes.
- The task LogseqPull will also create the variable Launch_logseq to throttle calls to
~/pull-notes
, you don’t need to do anything about it.
Setting it all up is rather cumbersome, so I provided the project as a gist which you can review, download, and import into Tasker. The XML is rather opaque but hopefully the above information helps with the review before you trust me with another download. You’re welcome to build or modify your own version, of course.
To import it into Tasker, use “Import Project” from the 🏠 button. The result should look similar to:
As soon as you enable the different profiles in the project, you should start seeing it in action. Navigate to the task to verify that they execute properly.
At this point, the workflow on your mobile changes from:
git pull
on Termux- Edit notes
git commit -a -m "Update from phone"; git push
To:
- Edit notes
How it’s going
I’ve been using and refining this setup for months, I’m really happy with it and I hope you’ll find it works great for you too. It takes bit of time to setup, but it works almost flawlessly in my case. And in case there’s an issue, I’ve got git to resolve it.
There are some left over quirks which can be addressed through habits.
For example: don’t leave trailing *
at then of a page in Logseq.
Logseq does so when you press enter in edit mode, and sometimes it messes with Org-element parsing.
A good habit is to press the page title or journal date to exit the input mode instead of pressing Enter
.
That’s it for now. I’ll continue to improve on logseq-org-roam in the future. It’s already pretty fast, but I know it still a bit rough under the edges, the code is a little messy, and there’s more features I’m planning to add for tagging and cards (spaced repetitions). I hope you found this useful. Feel free to contribute!