on
Italy
- Get link
- X
- Other Apps
git version control system, in particular,
has seen wide adoption in recent years due to its decentralized
architecture and the speed at which it can make and transfer changes
between parties.git suite of tools offers many
well-implemented features, one of the most useful characteristics is its
flexibility. Through the use of a "hooks" system, git allows developers
and administrators to extend functionality by specifying scripts that
git will call based on different events and actions.git installed on your server. If you are following along on Ubuntu 14.04, you can check out our guide on how to install git on Ubuntu 14.04 here.hooks directory within the git repository to see if there is an associated script to run.| Hook Name | Invoked By | Description | Parameters (Number and Description) |
|---|---|---|---|
| applypatch-msg | git am |
Can edit the commit message file and is often used to verify or actively format a patch's message to a project's standards. A non-zero exit status aborts the commit. | (1) name of the file containing the proposed commit message |
| pre-applypatch | git am |
This is actually called after the patch is applied, but before the changes are committed. Exiting with a non-zero status will leave the changes in an uncommitted state. Can be used to check the state of the tree before actually committing the changes. | (none) |
| post-applypatch | git am |
This hook is run after the patch is applied and committed. Because of this, it cannot abort the process, and is mainly used for creating notifications. | (none) |
| pre-commit | git commit |
This hook is called before obtaining the proposed commit message. Exiting with anything other than zero will abort the commit. It is used to check the commit itself (rather than the message). | (none) |
| prepare-commit-msg | git commit |
Called after receiving the default commit message, just prior to firing up the commit message editor. A non-zero exit aborts the commit. This is used to edit the message in a way that cannot be suppressed. | (1 to 3) Name of the file with the commit message, the source of the commit message (message, template, merge, squash, or commit), and the commit SHA-1 (when operating on an existing commit). |
| commit-msg | git commit |
Can be used to adjust the message after it has been edited in order to ensure conformity to a standard or to reject based on any criteria. It can abort the commit if it exits with a non-zero value. | (1) The file that holds the proposed message. |
| post-commit | git commit |
Called after the actual commit is made. Because of this, it cannot disrupt the commit. It is mainly used to allow notifications. | (none) |
| pre-rebase | git rebase |
Called when rebasing a branch. Mainly used to halt the rebase if it is not desirable. | (1 or 2) The upstream from where it was forked, the branch being rebased (not set when rebasing current) |
| post-checkout |
git checkout and git clone
|
Run when a checkout is called after updating the worktree or after git clone. It is mainly used to verify conditions, display differences, and configure the environment if necessary. |
(3) Ref of the previous HEAD, ref of the new HEAD, flag indicating whether it was a branch checkout (1) or a file checkout (0) |
| post-merge |
git merge or git pull
|
Called after a merge. Because of this, it cannot abort a merge. Can be used to save or apply permissions or other kinds of data that git does not handle. | (1) Flag indicating whether the merge was a squash. |
| pre-push | git push |
Called prior to a push to a remote. In addition to the parameters,
additional information, separated by a space is passed in through stdin
in the form of " |
(2) Name of the destination remote, location of the destination remote |
| pre-receive |
git-receive-pack on the remote repo |
This is called on the remote repo just before updating the pushed
refs. A non-zero status will abort the process. Although it receives no
parameters, it is passed a string through stdin in the form of
" |
(none) |
| update |
git-receive-pack on the remote repo |
This is run on the remote repo once for each ref being pushed instead of once for each push. A non-zero status will abort the process. This can be used to make sure all commits are only fast-forward, for instance. | (3) The name of the ref being updated, the old object name, the new object name |
| post-receive |
git-receive-pack on the remote repo |
This is run on the remote when pushing after the all refs have been
updated. It does not take parameters, but receives info through stdin in
the form of " |
(none) |
| post-update |
git-receive-pack on the remote repo |
This is run only once after all of the refs have been pushed. It is similar to the post-receive hook in that regard, but does not receive the old or new values. It is used mostly to implement notifications for the pushed refs. | (?) A parameter for each of the pushed refs containing its name |
| pre-auto-gc | git gc --auto |
Is used to do some checks before automatically cleaning repos. | (none) |
| post-rewrite |
git commit --amend, git-rebase
|
This is called when git commands are rewriting already committed
data. In addition to the parameters, it receives strings in stdin in the
form of " |
(1) Name of the command that invoked it (amend or rebase) |
proj.mkdir ~/proj
cd ~/proj
git init
Initialized empty Git repository in /home/demo/proj/.git/
Now, we are in the empty working directory of a git-controlled
directory. Before we do anything else, let's jump into the repository
that is stored in the hidden file called .git within this directory:cd .git
ls -F
branches/ config description HEAD hooks/ info/ objects/ refs/
We can see a number of files and directories. The one we're interested in is the hooks directory:cd hooks
ls -l
total 40
-rwxrwxr-x 1 demo demo 452 Aug 8 16:50 applypatch-msg.sample
-rwxrwxr-x 1 demo demo 896 Aug 8 16:50 commit-msg.sample
-rwxrwxr-x 1 demo demo 189 Aug 8 16:50 post-update.sample
-rwxrwxr-x 1 demo demo 398 Aug 8 16:50 pre-applypatch.sample
-rwxrwxr-x 1 demo demo 1642 Aug 8 16:50 pre-commit.sample
-rwxrwxr-x 1 demo demo 1239 Aug 8 16:50 prepare-commit-msg.sample
-rwxrwxr-x 1 demo demo 1352 Aug 8 16:50 pre-push.sample
-rwxrwxr-x 1 demo demo 4898 Aug 8 16:50 pre-rebase.sample
-rwxrwxr-x 1 demo demo 3611 Aug 8 16:50 update.sample
We can see a few things here. First, we can see that each of these
files are marked executable. Since these scripts are just called by
name, they must be executable and their first line must be a shebang magic number reference to call the correct script interpreter. Most commonly, these are scripting languages like bash, perl, python, etc..sample.
That is because git simply looks at the filename when trying to find
the hook files to execute. Deviating from the name of the script git is
looking for basically disables the script. In order to enable any of the
scripts in this directory, we would have to remove the .sample suffix.cd ../..
post-commit hook to show
you how to deploy to a local web server whenever a commit is made. This
is not the hook you would use for a production environment, but it lets
us demonstrate some important, barely-documented items that you should
know about when using hooks.sudo apt-get update
sudo apt-get install apache2
In order for our script to modify the web root at /var/www/html
(this is the document root on Ubuntu 14.04. Modify as needed), we need
to have write permission. Let's give our normal user ownership of this
directory. You can do this by typing:sudo chown -R `whoami`:`id -gn` /var/www/html
Now, in our project directory, let's create an index.html file:cd ~/proj
nano index.html
Inside, we can add a little bit of HTML just to demonstrate the idea. It doesn't have to be complicated:
Here is a title!
Please deploy me!
Add the new file to tell git to track the file:git add .
Now, before you commit, we are going to set up our post-commit hook for the repository. Create this file within the .git/hooks directory for the project:vim .git/hooks/post-commit
Before we go over what to put in this file, we need to learn a bit about how git sets up the environment when running hooks.post-commit hook.#!/bin/bash
echo Running $BASH_SOURCE
set | egrep GIT
echo PWD is $PWD
The information on his site is from 2011 working with git version
1.7.1, so there have been a few changes. At the time of this writing in
August of 2014, the current version of git in Ubuntu 14.04 is 1.9.1./home/demo/test_hooks and the bare remote (where necessary) was /home/demo/origin/test_hooks.git:applypatch-msg, pre-applypatch, post-applypatch
GIT_AUTHOR_DATE='Mon, 11 Aug 2014 11:25:16 -0400'GIT_AUTHOR_EMAIL=demo@example.comGIT_AUTHOR_NAME='Demo User'GIT_INTERNAL_GETTEXT_SH_SCHEME=gnuGIT_REFLOG_ACTION=am/home/demo/test_hooks
pre-commit, prepare-commit-msg, commit-msg, post-commit
GIT_AUTHOR_DATE='@1407774159 -0400'GIT_AUTHOR_EMAIL=demo@example.comGIT_AUTHOR_NAME='Demo User'GIT_DIR=.gitGIT_EDITOR=:GIT_INDEX_FILE=.git/indexGIT_PREFIX=/home/demo/test_hooks
pre-rebase
GIT_INTERNAL_GETTEXT_SH_SCHEME=gnuGIT_REFLOG_ACTION=rebase/home/demo/test_hooks
post-checkout
GIT_DIR=.gitGIT_PREFIX=/home/demo/test_hooks
post-merge
GITHEAD_4b407c...GIT_DIR=.gitGIT_INTERNAL_GETTEXT_SH_SCHEME=gnuGIT_PREFIX=GIT_REFLOG_ACTION='pull other master'/home/demo/test_hooks
pre-push
GIT_PREFIX=/home/demo/test_hooks
pre-receive, update, post-receive, post-update
GIT_DIR=./home/demo/origin/test_hooks.git
pre-auto-gc
post-rewrite
GIT_AUTHOR_DATE='@1407773551 -0400'GIT_AUTHOR_EMAIL=demo@example.comGIT_AUTHOR_NAME='Demo User'GIT_DIR=.gitGIT_PREFIX=/home/demo/test_hooks
post-commit hook), we can begin our script.#!/bin/bash
After that, we are just going to use git itself to unpack the newest
version of the repository after the commit, into our web directory. To
do this, we should set our working directory to Apache's document root.
We should also set our git directory to the repo.#!/bin/bash
git --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f
At this point, we are almost done. However, we need to look extra
close at the environmental variables that are set each time the post-commit hook is called. In particular, the GIT_INDEX_FILE is set to .git/index./var/www/html.
Since the git index does not exist at this location, the script will
fail if we leave it as-is. To avoid this situation, we can manually unset the variable, which will cause git to search in relation to the repo directory, as it usually does. We need to add this above the checkout line:#!/bin/bash
unset GIT_INDEX_FILE
git --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f
These types of conflicts are why git hook issues are sometimes
difficult to diagnose. You must be aware of how git has constructed the
environment it is working in.chmod +x .git/hooks/post-commit
Now, we are finally ready to commit the changes we made in our git
repo. Ensure that you are back in the correct directory and then commit
the changes:cd ~/proj
git commit -m "here we go..."
Now, if you visit your server's domain name or IP address in your browser, you should see the index.html file you created:http://server_domain_or_IP
As you can see, our most recent changes have been automatically
pushed to the document root of our web server upon commit. We can make
some additional changes to show that it works on each commit:echo "Here is a change.
" >> index.html
git add .
git commit -m "First change"
When you refresh your browser, you should immediately see the new changes that you applied:sudo apt-get update
sudo apt-get install apache2
Again, we should give ownership of the document root to the user we are operating as:sudo chown -R `whoami`:`id -gn` /var/www/html
We need to remember to install git on this machine as well:sudo apt-get install git
Now, we can create a directory within our user's home directory to
hold the repository. We can then move into that directory and initialize
a bare repository. A bare repository does not have a working directory
and is better for servers that you will not be working with much
directly:mkdir ~/proj
cd ~/proj
git init --bare
Since this is a bare repository, there is no working directory and all of the files that are located in .git in a conventional setup are in the main directory itself.post-receive hook, which is run on the server receiving a git push. Open this file in your editor:nano hooks/post-receive
Again, we need to start off by identifying the type of script we are
writing. After that, we can type out the same checkout command that we
used in our post-commit file, modified to use the paths on this machine:#!/bin/bash
git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f
Since this is a bare repository, the --git-dir should point to the top-level directory of that repo. The rest is fairly similar.test-feature branch to this server, we do not want that to be deployed. We want to make sure that we are only going to be deploying the master branch.post-receive hook, you may have noticed in the
table earlier that git passes the old revision's commit hash, the new
revision's commit hash, and the reference that is being pushed as
standard input to the script. We can use this to check whether the ref
is the master branch or not.while loop to surround the git command:#!/bin/bash
while read oldrev newrev ref
do
git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f
done
So now, we will have three variables set based on what is being pushed. For a master branch push, the ref object will contain something that looks like refs/heads/master. We can check to see if the ref the server is receiving has this format by using an if construct:#!/bin/bash
while read oldrev newrev ref
do
if [[ $ref =~ .*/master$ ]];
then
git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f
fi
done
For server-side hooks, git can actually pass messages back to the
client. Anything sent to standard out will be redirected to the client.
This gives us an opportunity to explicitly notify the user about what
decision has been made.else block to notify the user when a non-master branch was successfully received, even though the action won't trigger a deploy:#!/bin/bash
while read oldrev newrev ref
do
if [[ $ref =~ .*/master$ ]];
then
echo "Master ref received. Deploying master branch to production..."
git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f
else
echo "Ref $ref successfully received. Doing nothing: only the master branch may be deployed on this server."
fi
done
When you are finished, save and close the file.chmod +x hooks/post-receive
Now, we can set up access to this remote server on our client.cd ~/proj
Inside, add the remote server as a remote called production.
You will need to know the username that you used on your production
server, as well as its IP address or domain name. You will also need to
know the location of the bare repository you set up in relation to the
user's home directory.git remote add production demo@server_domain_or_IP:proj
Let's push our current master branch to our production server:git push production master
If you do not have SSH keys configured, you may have to enter the
password of your production server user. You should see something that
looks like this:Counting objects: 8, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 473 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
remote: Master ref received. Deploying master branch...
To demo@107.170.14.32:proj
009183f..f1b9027 master -> master
As you can see, the text from our post-receive hook is
in the output of the command. If we visit our production server's domain
name or IP address in our web browser, we should see the current
version of our project:test_feature and check the new branch out by typing:git checkout -b test_feature
test_feature branch. Let's make a change that we might want to move to production. We will commit it to this branch:echo "
New Feature Here
" >> index.html
git add .
git commit -m "Trying out new feature"
At this point, if you go to your development machine's IP address or domain name, you should see your changes displayed:test_feature branch to our remote production server:git push production test_feature
You should see the other message from our post-receive hook in the output:Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 301 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Ref refs/heads/test_feature successfully received. Doing nothing: only the master branch may be deployed on this server
To demo@107.170.14.32:proj
83e9dc4..5617b50 test_feature -> test_feature
master branch and merge in our test_feature branch on our development machine:git checkout master
git merge test_feature
Now, you have merged the new feature into the master branch. Pushing to the production server will deploy our changes:git push production master
If we check out our production server's domain name or IP address, we will see our changes:
Comments
Post a Comment