I'm trying to set up Git for staging my website so that I can git pull
to get the current version to work on locally and then git push
to push the changes to the remote server. I've got it set up so that it works the way I want it to, but after I push, I have to manually run git checkout -f
or git reset --hard HEAD
on the remote server.
I've tried putting these into a shell script as the post-receive hook on the server, but it just doesn't seem to have any effect. I know that the script is running because I'm seeing "Changes pushed to server" after I push. Here's the post-receive hook:
#!/bin/sh
git reset --hard HEAD
echo "Changes pushed to server."
The answer to your question is here: http://toroid.org/ams/git-website-howto
In short, what you want to do is add a "detached work tree" to the bare repository. Normally you think of your work tree as containing the .git
directory. Bare repositories do not have a work tree by definition, but you can create one as long as it's in a different directory than the bare repo.
The post-receive hook is just a simple git checkout -f
to replicate the repository's HEAD
into the work directory. Apache uses that as its document root, and you're all set. Any time you push to the bare repository, Apache will immediately start serving it.
I generally use this to automatically push to a staging server to see if the "real" environment will puke on my changes. Deploying to the live server is a completely different story. :-)
Update March 2015
As I mentioned in "What is this Git warning message when pushing changes to a remote repository?", you actually can push directly to a non-bare repo now (Git 2.3.0+, February 2015) with:
git config receive.denyCurrentBranch updateInstead
Update the working tree accordingly, but refuse to do so if there are any uncommitted changes.
That would allow you to avoid any post-receive hook.
(Original answer: Oct 2010)
The GitFAQ recommends for non-bare repo this post-update hook:
(it might give you more clue as to what is actually going on in the hook execution. Note this is a post-update hook, not a post-receive)
#!/bin/sh
#
# This hook does two things:
#
# 1. update the "info" files that allow the list of references to be
# queries over dumb transports such as http
#
# 2. if this repository looks like it is a non-bare repository, and
# the checked-out branch is pushed to, then update the working copy.
# This makes "push" function somewhat similarly to darcs and bzr.
#
# To enable this hook, make this file executable by "chmod +x post-update".
git-update-server-info
is_bare=$(git-config --get --bool core.bare)
if [ -z "$is_bare" ]
then
# for compatibility's sake, guess
git_dir_full=$(cd $GIT_DIR; pwd)
case $git_dir_full in */.git) is_bare=false;; *) is_bare=true;; esac
fi
update_wc() {
ref=$1
echo "Push to checked out branch $ref" >&2
if [ ! -f $GIT_DIR/logs/HEAD ]
then
echo "E:push to non-bare repository requires a HEAD reflog" >&2
exit 1
fi
if (cd $GIT_WORK_TREE; git-diff-files -q --exit-code >/dev/null)
then
wc_dirty=0
else
echo "W:unstaged changes found in working copy" >&2
wc_dirty=1
desc="working copy"
fi
if git diff-index --cached HEAD@{1} >/dev/null
then
index_dirty=0
else
echo "W:uncommitted, staged changes found" >&2
index_dirty=1
if [ -n "$desc" ]
then
desc="$desc and index"
else
desc="index"
fi
fi
if [ "$wc_dirty" -ne 0 -o "$index_dirty" -ne 0 ]
then
new=$(git rev-parse HEAD)
echo "W:stashing dirty $desc - see git-stash(1)" >&2
( trap 'echo trapped $$; git symbolic-ref HEAD "'"$ref"'"' 2 3 13 15 ERR EXIT
git-update-ref --no-deref HEAD HEAD@{1}
cd $GIT_WORK_TREE
git stash save "dirty $desc before update to $new";
git-symbolic-ref HEAD "$ref"
)
fi
# eye candy - show the WC updates :)
echo "Updating working copy" >&2
(cd $GIT_WORK_TREE
git-diff-index -R --name-status HEAD >&2
git-reset --hard HEAD)
}
if [ "$is_bare" = "false" ]
then
active_branch=`git-symbolic-ref HEAD`
export GIT_DIR=$(cd $GIT_DIR; pwd)
GIT_WORK_TREE=${GIT_WORK_TREE-..}
for ref
do
if [ "$ref" = "$active_branch" ]
then
update_wc $ref
fi
done
fi
For this to work, you would still need to specifically allow pushing changes to the current branch by using either one of these configuration settings:
git config receive.denyCurrentBranch ignore
or
git config receive.denyCurrentBranch warn
I had the exact same issue. In a reply to this link: http://toroid.org/ams/git-website-howto - The following command has done it:
sudo chmod +x hooks/post-receive
We missed a sudo
permission first configured the stuff.
Fixed version of VonC's script, works for me (absolutely no guarantees).
#!/bin/sh
#
# This hook does two things:
#
# 1. update the "info" files that allow the list of references to be
# queries over dumb transports such as http
#
# 2. if this repository looks like it is a non-bare repository, and
# the checked-out branch is pushed to, then update the working copy.
# This makes "push" function somewhat similarly to darcs and bzr.
#
# To enable this hook, make this file executable by "chmod +x post-update".
set -e
git update-server-info
is_bare=$(git config --get --bool core.bare)
if [ -z "${is_bare}" ]
then
# for compatibility's sake, guess
git_dir_full=$(cd $GIT_DIR; pwd)
case $git_dir_full in */.git) is_bare=false;; *) is_bare=true;; esac
fi
update_wc() {
ref=$1
echo "Push to checked out branch $ref" >&2
if [ ! -f ${GIT_DIR}/logs/HEAD ]
then
echo "E:push to non-bare repository requires a HEAD reflog" >&2
exit 1
fi
if (cd ${GIT_WORK_TREE}; git diff-files -q --exit-code >/dev/null)
then
wc_dirty=0
else
echo "W:unstaged changes found in working copy" >&2
wc_dirty=1
desc="working copy"
fi
if git diff-index --cached HEAD@{1} >/dev/null
then
index_dirty=0
else
echo "W:uncommitted, staged changes found" >&2
index_dirty=1
if [ -n "$desc" ]
then
desc="$desc and index"
else
desc="index"
fi
fi
if [ "$wc_dirty" -ne 0 -o "$index_dirty" -ne 0 ]
then
new=$(git rev-parse HEAD)
echo "W:stashing dirty $desc - see git-stash(1)" >&2
( trap 'echo trapped $$; git symbolic-ref HEAD "'"$ref"'"' 2 3 13 15 ERR EXIT
git update-ref --no-deref HEAD HEAD@{1}
cd ${GIT_WORK_TREE}
git stash save "dirty $desc before update to $new";
git symbolic-ref HEAD "$ref"
)
fi
# eye candy - show the WC updates :)
echo "Updating working copy" >&2
(cd ${GIT_WORK_TREE}
git diff-index -R --name-status HEAD >&2
git reset --hard HEAD
# need to touch some files or restart the application? do that here:
# touch *.wsgi
)
}
if [ x"${is_bare}" = x"false" ]
then
active_branch=$(git symbolic-ref HEAD)
export GIT_DIR=$(cd ${GIT_DIR}; pwd)
GIT_WORK_TREE="${GIT_DIR}/.."
for ref in $(cat)
do
if [ x"$ref" = x"${active_branch}" ]
then
update_wc $ref
fi
done
fi
Simple script for setting this git deployment:
Preparing post-receive hook:
echo '#!/bin/sh' > .git/hooks/post-receive
echo 'git checkout -f' >> .git/hooks/post-receive
echo 'git reset --hard' >> .git/hooks/post-receive
chmod +x .git/hooks/post-receive
Allowing push into this repository, though it is not bare:
git config receive.denycurrentbranch false
I'm just guessing, but this may be some permission issue (full path needed? cd
?). Check what is really happening in the log files.
However publishing the files via git is always just one tasks of the publishing process. You usually need to copy some files, delete other, setup, update permissions, generate docs etc.
For a complex solution a build script might be better than any git hook. Tools which can handle those tasks very well:
(I realize this is not the answer you are expecting, but it's too long to post as a comment)
来源:https://stackoverflow.com/questions/3838727/git-post-receive-hook-for-website-staging