Git Post-Receive Hook for Website Staging

2019-01-01 15:15发布

问题:

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.\"

回答1:

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. :-)



回答2:

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


回答3:

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.



回答4:

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


回答5:

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


回答6:

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:

  • phing (or ant)
  • hudson + git plugin

(I realize this is not the answer you are expecting, but it\'s too long to post as a comment)