It is certainly possible to do this, e.g. (untested):
git fetch &&
ahead=$(git rev-list --count master..origin/master) &&
case "$ahead" in
0) ;; # run normally
*) echo "I seem to be out of date"
git merge --ff-only || { echo "update failed, quitting"; exit 1; }
exec <path-to-script>;;
esac
# ... normal part of script here
But this is also almost certainly the wrong approach. Instead of doing that, schedule a job—a script—that consists of:
git fetch && git merge --ff-only && exec <path-to-script>
This script can live in the same repository. It's a separate script whose job is to update in place—which, if there's nothing to do, is a no-op (it says "Already up to date." and then exits 0 = success)—and then run the other script, whether it's updated or not. This provides a clean separation of purpose: one script updates; one script runs; there's no weird mix of self-update-and-oops-now-I-have-to-quit-because-maybe-my-code-is-different.
Note that adding --quiet
to the git merge --ff-only
suppresses the "Already up to date." message, which may be helpful if your version of cron emails you the output when there is output. (If your version of cron doesn't do this, it probably should be upgraded to one that does.) So you probably really want:
git fetch && git merge --ff-only --quiet && exec <path-to-script>
The fetch followed by merge is what git pull
does by default, but git pull
is a program meant to be run by a human. Git divides its various programs into so-called porcelain and plumbing, and the porcelain commands are the ones meant for humans, while the plumbing ones are meant for writing scripts. Git's division here is quite imperfect: some commands are both plumbing and porcelain, and there are some varieties that are missing (e.g., git log
is porcelain, but there are no plumbing commands for some of what it does)—but to the extent that you can, it's usually wise to stick to this pattern.