In production, I maintain two sites - beta and release. Each points to a different directory via a soft link (e.g.)
beta_public_html -> /home/scott/myapp/
I use a post-receive
hook like this to publish my website, because Git does not touch the working directory when doing a push. The remote repository is a non-bare repository, i.e. it has a working directory.
if [ -n $GIT_DIR ]; then
# Current dir is "<reporoot>/.git/", but we need to run reset at "<reporoot>/".
# Clearing GIT_DIR is needed, or reset will fail with "fatal: Not a git repository: '.'"
unset GIT_DIR
cd ..
fi
git reset --hard
(BTW, note that I can't seem to push to "myapp-beta.git" - that fails, I have to push to the directory name. I am worried that this is part of the problem, but I don't know what I did wrong here.)
When creating a bare Git repository (git init --bare
) which does not have a working directory, it is a convention to name the directory "something.git". When having a non-bare repository, the repository is actually in the ".git" subdirectory, so the full path is "something/.git". It seems that in either case you can leave out the ".git" part and Git will detect it automatically.
I'm not really opposed to the other solutions, but I think there's a less hack'ish "Git way" to do this. Here's what I would do:
The solution is to push to single repository, which would employ update
or post-receive
hook.
There are a few separate possibilities to create two checkouts, which can be used in hook (on server). Going from most lightweight:
If you don't need for those two checked out directories (checked out versions) to actually be git repositories, you can simply use git-archive to export two snapshots (two branches)
git archive --format=tar --prefix=public_html/ master | (cd /var/www/html && tar xf -)
git archive --format=tar --prefix=beta_public_html/ devel | (cd /var/www/html && tar xf -)
where 'master' and 'devel' are names of branches that you wanted to have checked out. Note that --format=tar
is not strictly speaking needed, as tar format is default for "git archive". You might also want to remove old contents ("rm -rf public_html/"
before "tar xf -
" in first line, joined with "&&
", and similarly for the second line).
Alternate way of exporting files would be to use low-level commands "git read-tree
" (which writes tree to index) and "git checkout-index
" (which copies files from index to working area).
In this solution the repository you push into can (and should) be bare, i.e. without working directory itself.
Another solution would be for the repository you push into to have two working directories, which can be created using git-new-workdir script from contrib/workdir
. Each of those working areas would have be a checkout of appropriate branch of this repository.
Then update
or post-receive
hook would unset GIT_DIR
, go to those working areas (public_html
and beta_public_html
), and do "git reset --hard
" there.
In this solution "checkouts" would have some git-related metainfo in them (in hidden .git
directory).
Yet another solution would be to have two (additional) slave repositories. Pushing into main (master) repository would then (via hook) either push into those two slave repositories, where their hooks would do "git reset --hard
" or equivalent, or go to those two slave repositories and do a "git pull
" from master there.
Those two slave repositories would be non-bare, and can be [directly in] public_html
and beta_public_html
. In this solution "checkouts" would be full-fledged git repositories itself.
You can improve this setup by having those slave repositories to have "alternates" (alternate object database) to point to master repository (i.e. be cloned with "git clone --shared ...
"); without this object database in slaves starts hardlinked to master. This of course is possible only if master and slaves are on the same filesystem.
Note this solution allows for master repository to be on different host than slave repositories. (although I guess this is flexibility you don't need).
Finally you can instead of current setup deploy gitweb or some other git web interface (see InterfacesFrontendsAndTools and Gitweb wiki pages for a partial list), so that your users can browse different versions and different branches of your repository at their leisure.
In gitweb (and I guess also in other git web interface) thanks to path_info URL support you can view files in browser, and follow links correctly (if they are local), see e.g. git.html from 'html' branch of git.git repository at repo.or.cz.
P.S. "git push
" does not update working directory in remote repository by default, because if somebody is working in the non-bare repository you push into, such sideways push can be very unexpected and lead to loss of work.
The article Git push is worse than worsless has an interesting discussion about a similar issue.
One of its solution and conclusion involves:
So in your case,
In short: one step: git push
. No more link to manage (since the directory no longer represent a branch in Git, unlike SVN)
Regarding the hook part, a post-receive hook in the bare repo could be all what you need
See Git Tip: Auto update working tree via post-receive hook
$ cd bare
$ chmod +x .git/hooks/post-receive
with a post-receive hook like
#!/bin/sh
cd ../../beta
env -i git reset --hard
cd ../../release
env -i git reset --hard
Note:
the
post-receive
hook starts out with theGIT_DIR
environment variable set to therepo/.git
folder, so no matter what path you 'cd
' into it will always try to run any following git commands there.
Fixing this is simply a matter of unsetting theGIT_DIR
.
'env -i' does just that: it ignores the inherited environment completely and uses only the supplied variables and values.