I wrote this code today to verify setup branch pushes on Branchable. When I was writing it I was just down in the trenches coding until it worked, but it's rather surprising that what it does to git does work.
The following code runs in git's update
hook. The fast path of the hook
(written in C) notices that the user is committing a change to the setup
branch, and hands the incoming git ref off to the setup verifier.
# doing a shared clone makes the setupref, which has
# not landed on any branch, be available for checkout
shell("git", "clone", "--quiet", "--shared",
"--no-checkout", repository($hostname), $tmpcheckout);
chdir($tmpcheckout) || error "chdir $tmpcheckout: $!";
shell("git", "checkout", "--quiet", $setupref, "-b", "setup");
I got lucky here, since I initially passed --shared
only to avoid
the overhead of a clone of the site's entire git repository (which can
be quite large, since Branchable doesn't have any real limits on site size).
Without the --shared
, the clone wouldn't see the incoming ref at all.
In the setup branch is an ikiwiki.setup
file, and we only want to allow
safe changes to be committed to it. Ikiwiki has metadata about which
configurations are safe. Checking that and various other amusing scenarios
(what if someone makes ikiwiki.setup
a symlink etc) takes a hundred lines
of fairly hairy code, but that doesn't matter here. Eventually it decides
the setup file is ok as-is, or it's already died with an error message.
# Check out setup file in toplevel. This is slightly tricky
# as the commit has not landed in the bare git repo yet --
# but it is available in the tmpcheckout.
shell("git", "pull", "-q", $tmpcheckout, "setup");
# Refresh or rebuild site to reflect setup changes.
print STDERR "Updating site to reflect setup changes...\n";
shell("ikiwiki", "-setup", "ikiwiki.setup", "-v",
($rebuild_needed ? ("-rebuild") : ("-refresh", "-wrappers"))
);
When this code runs there are three repositories, each with a different view of the setup branch. The main bare repository is waiting for the hook to succeed before it updates the ref to point to what was pushed. The temporary clone has what was pushed already checked out. And the site's home directory still has the old version of the setup branch checked out. Possibly even a version that has diverged from what's in the bare repository.
It's rather odd that the update hook goes and causes that latter repository to be updated, before the change has finished landing in the bare repository. But it does work; it ensures that if there is some kind of bizzare merge problem the user doing the push sees it, and I probably won't regret it.
The result certianly is nice -- edit ikiwiki.setup
file locally,
commit and push it, and ikiwiki automatically reconfigures itself
and even rebuilds your whole site if you've changed something significant.