Automated Syncing with Git
Syncing folders with Git Sync and Systemd
I use Git quite a lot. Pretty much all of my personal projects end up in a Git repository, and are pushed to BitBucket. This doesn’t just mean software projects, but also things like my notebook (I’ve been learning org-mode in Emacs recently), and the XML generated by Gnucash that I use to keep track of my finances.
In a software dev project, Git is typically part of my process. I’ll have something I want to do in mind. I’ll make some changes. I’ll test. Then I’ll commit with a meaningful commit message.
Writing a note in my notebook doesn’t have such a disciplined structure, and I don’t care to write a commit message. I just want it to be committed and sent to BitBucket so I can pull it elsewhere. Really, I want Dropbox-style syncing, just with the version history and merging of Git.
As with many quick automations, the first step is to write a script. I started writing this script myself, after all it sounded like it would just be a case of pull, commit, push, but soon realized that it was more complicated than I thought. What if a merge conflict happens? Then the step where you add everything will break things. What if you’re in the middle of doing something manually, like resolving a merge conflict, and the script runs? Instead of writing something to handle these cases, I found that someone else already had here. In terms of setup on my Git repo, I needed to set some config flags using the following two commands.
git config --bool branch.master.sync true git config --bool branch.master.syncNewFiles true
syncNewFiles flag will let it add new files. It’s worth making
sure that your
.gitignore file covers anything that you don’t want
After that, I still wrapped my command in a shell script, to make it apply to multiple repos. When I get around to setting this up on my Windows machine, the Windows-friendly version of this script will also include running something like MinGW, since Git Sync is a Bash script.
#!/bin/sh cd ~/doc/gnucash ~/auto/git-sync cd ~/doc/notebook ~/auto/git-sync
Put it on a Schedule
How you schedule the script to run will depend on your system. On Linux, you’re likely to have Cron available. Windows has the Task Scheduler. Anything that lets you run your shell script at regular intervals will work.
I am running Arch Linux, which uses Systemd to manage these sorts of things internally, so I decided to give writing my own Systemd unit a go.
The first thing that you need is a service file telling Systemd how to run your script. This includes some useful settings like a description and a “Niceness”. Nicer programs have less priority when it comes to allocating resources. I would rather my script takes a bit longer and doesn’t slow down the system at all for me (not that the script is actually doing anything intense), so I made it super nice.
#Saved in ~/auto/autosync-repos.service [Unit] Description=Automatically does git push and pull on a number of repos [Service] Type=simple ExecStart=/home/justin/auto/autosync-repos.sh Nice=19 [Install] WantedBy=autosync-repos.target
The next thing that Systemd needs is a timer, which is the trigger that launches the service at regular intervals. For testing, you can launch the service without the timer, or have it triggered by other events. I want my script to run every 15 minutes, starting soon after the machine boots up.
If you don’t specify which service to run, the timer will find a service with the same name as itself, just a different extension.
#Saved in ~/auto/autosync-repos.timer [Unit] Description=Automatically does git push and pull on a number of repos [Timer] OnBootSec=1min OnUnitActiveSec=15min [Install] WantedBy=timers.target
The last step is to register these config files with Systemd. These commands are run from the terminal. It will create a symlink to your config files in the folders that Systemd is checking, so you can keep your custom Systemd units arranged on your drive however you want.
systemctl --user enable /home/justin/auto/autosync-repos.service systemctl --user enable /home/justin/auto/autosync-repos.timer
I found that the path to your service files needed to be absolute.
--user flag installs it in the Systemd config that applies to
your current logged in user, not everyone that shares your
computer. It also means that it runs as your user, so your script can
use your ssh keys and it knows where your home directory is.
But Does it Work?
If you want to test that your script works with the service config, you can run it with
systemctl --user start autosync-repos.service
and then, after waiting a few seconds, run
systemctl --user status autosync-repos.service
to see its output. You can also use that status command to make sure that the timer has been running it.
What am I Missing?
The big thing that I’m missing is error reporting. The Git Sync script is set up that it won’t do much harm if a merge conflict comes up or my network is dead, but it will fail quietly. The Systemd docs suggest that you can set it up to email you when things go wrong. This could work, but I haven’t played with it yet. If I ever add that, it will be the topic of a future post.
Update - 7 Nov 2017
I’ve finally gotten around to having this script tell me when things go wrong. Read more here.