The HTML source for my (i.e., this) web site lives in a
Git repository on my local workstation.
This page describes how I set things up so that I can make changes live
by running just "git push web".
The one-line summary:
push
into a
remote
repository that has a detached work tree, and a
post-receive hook
that runs "git checkout -f".
It doesn't really matter how the local repository is set up, but for the sake of argument, let's suppose you're starting one from scratch.
$ mkdir website && cd website $ git init Initialized empty Git repository in /home/ams/website/.git/ $ echo 'Hello, world!' > index.html $ git add index.html $ git commit -q -m "The humble beginnings of my web site."
Anyway, however you got there, you have a repository whose contents you want to turn into a web site.
I assume that the web site will live on a server to which you have ssh
access, and that things are set up so that you can ssh to it without
having to type a password (i.e., that your public key is in
~/.ssh/authorized_keys and you are running
ssh-agent locally).
On the server, we create a new repository to mirror the local one.
$ mkdir website.git && cd website.git $ git init --bare Initialized empty Git repository in /home/ams/website.git/
Although this mirror starts out "bare" (i.e., with no work tree of its
own), we add a detached work tree that corresponds to the web server's
DocumentRoot (this directory must exist; Git will not
create it for you.)
$ mkdir /var/www/www.example.org $ git config core.worktree /var/www/www.example.org $ git config core.bare false $ git config receive.denycurrentbranch ignore
Then we define (and enable) a post-receive hook.
$ cat > hooks/post-receive #!/bin/sh git checkout -f $ chmod +x hooks/post-receive
Back on the workstation, we define a name for the remote mirror, and
then mirror to it, creating a new "master" branch there.
$ git remote add web ssh://server.example.org/home/ams/website.git $ git push web +master:refs/heads/master
On the server, /var/www/www.example.org should now contain
a copy of your files, independent of any .git metadata.
Nothing could be simpler. In the local repository, just run
$ git push web
This will transfer any new commits to the remote repository, where the
post-receive hook will immediately update the
DocumentRoot for you.
(This is more convenient than defining your workstation as a remote on
the server, and running "git pull" by hand or from a cron
job, and it doesn't require your workstation to be accessible by ssh.)
A few more things bear mentioning.
First, the work tree (/var/www/www.example.org above) does
not need to correspond exactly to your DocumentRoot. Your
repository may represent only a subdirectory of it, or even contain it
as a subdirectory.
In the work tree, you will need to set the environment variable
GIT_DIR to the path to website.git before you
can run any git commands (e.g. "git status").
Setting receive.denycurrentbranch to "ignore"
on the server eliminates a warning issued by recent versions of git when
you push an update to a checked-out branch on the server. (Thanks to
Miklos Vajna for pointing this out.)
Second, you can push to more than one remote repository by adding more
URLs under the [remote "web"] section in your
.git/config.
[remote "web"]
url = ssh://server.example.org/home/ams/website.git
url = ssh://other.example.org/home/foo/website.git
There are also other hooks. See
githooks(5)
for details. For example, you could use pre-receive to
accept or deny a push based on the results of an
HTML validator. Or you could do
more work in the post-receive hook (such as send email to
co-maintainers; see contrib/hooks/post-receive-email).
I wrote this after reading Daniel Miessler's piece on
Using
Git to maintain your website. His setup is quite straightforward:
push to a bare repository on the server, then pull the changes into a
second clone that is used as the DocumentRoot. (He later
set up a post-update hook on the bare repository to pull
into the live clone). Same effect, different implementation.
Note: some people have reported that this strategy doesn't work under
git 1.5.4.3 (because the git checkout -f fails). I know
it does work with 1.6.x. I haven't investigated further.
Questions and suggestions are welcome.
Abhijit Menon-Sen <ams@toroid.org>