A bit of background... I use several different machines, including
three laptops and two desktops, and often have long running projects
that I need to work on no matter where I am. Most of us handle this
situation with Dropbox, but I wanted finer grain control over
versioning and also didn't want my files stored on a third-party
server.
The solution I now use is the script below (I've added extra comments
for readability). There's a line in my crontab on each machine to run
this hourly, at which point it pushes and pulls changes from one of my
machines that is externally accessible and acts as the server. As with
any homebrew solution, there are several caveats that you need to be
aware of:
- You can easily generate merge conflicts by switching machines
without manually committing and pushing on one and then pulling on the
other.
- Moving directories containing gitignored files can cause these
files to become unexpectedly tracked.
- If you shutdown without manually committing and pushing, your last
changes may not get propagated until you restart that machine. You can
trigger the script on shutdown, but I just remember this and commit
and push manually.
- The script relies on GNU tools and will only function correctly on
Linux. This is just because I'm lazy. With a few tweaks it could run
on any *nix.
There are some other issues, and this solution is far from perfect,
but I like the degree of control and flexibility it gives me over my
work. Let me know if you have any questions or suggestions.
----
#!/bin/bash
# Check command line arguments. It is advisable to run a copy of this script
# that DOES NOT exist in the directory you are trying to sync (and pass the
# directory as an argument). While it may seem logical to keep this script in
# the directory you are trying to sync this means that changes to this script
# will be propagated WHILE THIS SCRIPT IS EXECUTING. Changing a script while it
# is running is a quick way to shoot yourself in the foot.
if [ $# -gt 1 ]; then
echo "Usage: $0 [ repo_path ]" >&2
exit 1
fi
# Commit message:
COMMIT_MESSAGE="Auto-commit from ${USER}@`uname --nodename`."
# Get the absolute path to this script. Will be handy later on.
MYSELF=`readlink -f $0`
# Move into the directory we're syncing.
if [ -n "$1" ]; then
cd "$1" &>/dev/null || {
echo "Failed to change to $1." >&2 ;
exit 1
}
else
cd `dirname $0`
fi
# Add all outstanding changes.
git add --all . >/dev/null
if [ $? -ne 0 ]; then
echo "Adding new files failed" >&2
exit 1
fi
# Commit outstanding changes.
git commit -m "${COMMIT_MESSAGE}" >/dev/null
# We should probably attempt to detect commit failure here, but git returns the
# same error code for failure as for "nothing to commit."
# Check if we have a network. This avoids the script spamming you with
# irrelevant failure messages when you don't have an internet connection.
ping -c 1 -w 1 www.google.com &>/dev/null
if [ $? -ne 0 ]; then
# No network.
exit 0
fi
# Pull in new changes. Note --rebase and --ff-only. This forces only trivially
# rebasable changes to be pulled in. If any kind of complicated rebase is
# required, you DO NOT want this script resolving it automatically.
git pull --rebase --ff-only origin master &>/dev/null
if [ $? -ne 0 ]; then
# If the pull failed, we may have been asked to resolve a conflict.
git rebase --abort
echo "Rebase pull failed. Merge probably required." >&2
exit 4
fi
# Check we are consistent with the copy in the repo. This is handy to detect
# when you have updated the copy of this script in the sync directory, but have
# not propagated these changes to the version of the script that you are
# executing. Ideally you would want these to stay in sync automatically, but
# executing the copy in the sync directory or a symlink to this file will go
# badly for you for the aforementioned reasons.
diff `basename ${MYSELF}` ${MYSELF} &>/dev/null || {
echo "Warning: `basename ${MYSELF}` differs from repository copy." >&2 ;
}
# Push any changes we just committed.
git push origin master &>/dev/null
if [ $? -ne 0 ]; then
echo "Pushing to remote failed. Merge probably required." >&2
exit 5
fi