Version control, which is one of the core requirements of modern software development. With it, software projects can safely track code changes and perform multiple operations such as backtracking, integrity checking, and collaborative development. In various version control software, git
is one of the most popular software in recent years, its de-centralized architecture and the speed of source change Exchange is favored by many developers.
This article will introduce the basic ideas and usage of git hooks and demonstrate how to automate tasks in your environment. The operating system used in this article is Ubuntu 14.04 Server Edition, theoretically any system that can run git can do it in the same way.
Second, you should be able to perform basic git operations. If you think you're not familiar with git, take a look at this git getting Started tutorial.
Please continue reading after the above conditions have been reached.
The concept of Git hooks is quite simple, and it is designed to be implemented for a single requirement. In the development of a shared project (or multi-person co-development project), team members need to ensure that their coding style is unified, that the deployment is uniform, and so on (often this scenario is covered by git users), which creates a lot of repetitive work.
Git Hooks is event-based (event-based). When you execute a specific git instruction, the software checks for the corresponding script from the directory under the GIT repository hooks
and executes it if there is one.
Some scripts are executed before the action is executed, and this "antecedent script" can be used to implement the functions of code specification unification, integrity checking, environment building, etc. Some scripts are executed after the event, and this "down script" can be used to implement such functions as code deployment, error correction of permissions (Git has a bit of a lack of functionality in this regard).
In general, git hooks can implement policy enforcement, ensure consistency, environment control, deployment task handling, and many other functions.
The above classification helps us to create a holistic concept of hooks to understand what kind of events it can use. Of course, to be able to actually use it, but also need to do hands-on, debugging.
Some hooks can accept parameters. In other words, when git invokes the hook's script, we can pass some data to the script. The list of available hooks is as follows:
Here are a few scenarios to illustrate how git hooks are used.
We are now in this git-controlled directory and there is nothing in the directory. Before adding anything, let's go into .git
this hidden directory:
Here you can see some files and directories. We are interested in hooks
this directory:
There are already some things in this. The first thing you can see is that every file in the directory is marked as "executable." Scripts are called by file names, so they must be executable, and the first line of their content must have a shebang magic number (#!) referenced to the correct script parser. Common scripting languages include bash, Perl, Python, and so on.
About the environment variables for git hooksSome environment variables are involved when the hook is called. To get our script to work, we need to change post-commit
the environment variables that git has changed when it calls the hook.
This is a particular point to note when writing git hooks. GIT sets different environment variables when it calls different hooks. In other words, different hooks can cause git to pull information from different environments.
As a result, your scripting environment becomes out of control, and you may not even realize which variables are automatically changed. Unfortunately, these changed variables are not fully documented in the GIT documentation.
Fortunately, Mark Longair found a test method to check the environment variables that were changed when each hook was called. This test method only requires you to paste the following lines of code into your git hook script:
#!/bin/bashecho Running $BASH_SOURCEset | egrep GITecho PWD is $PWD
He wrote this article in 2011, when the git version was in 1.7.1. I wrote this article in the August 2014, the git version is 1.9.1, the operating system is Ubuntu 14.04, it should be said there are some changes. In summary, here are my test results:
In the following tests, the local project directory is /home/demo/test_hooks
, and the remote path is /home/demo/origin/test_hooks.git
.
-
Hooks : applypatch-msg
, pre-applypatch
, post-applypatch
- Environment Variables :
-
git_author_date= ' Mon, 11:25:16-0400 '
-
[email protected]
-
Git_author_name= ' Demo User '
-
Git_internal_gettext_sh_scheme=gnu
-
Git_reflog_action=am
-
working directory :/home/demo/test_hooks
Hooks:,, pre-commit
prepare-commit-msg
commit-msg
,post-commit
- Environment Variables :
Git_author_date= ' @1407774159-0400 '
[Email protected]
Git_author_name= ' Demo User '
Git_dir=.git
Git_editor=:
Git_index_file=.git/index
git_prefix=
working directory :/home/demo/test_hooks
Hooks:pre-rebase
Hooks:post-checkout
Hooks:post-merge
- Environment Variables :
Githead_4b407c ...
Git_dir=.git
Git_internal_gettext_sh_scheme=gnu
git_prefix=
Git_reflog_action= ' pull Other master '
working directory :/home/demo/test_hooks
Hooks:pre-push
Hooks:,, pre-receive
update
post-receive
,post-update
Hooks:pre-auto-gc
- This is hard to test, so information is missing
Hooks:post-rewrite
- Environment Variables :
Git_author_date= ' @1407773551-0400 '
[Email protected]
Git_author_name= ' Demo User '
Git_dir=.git
git_prefix=
working directory :/home/demo/test_hooks
These are the environments that git sees when it calls different hooks. With this information, we can go back and write our script.
Go back and write the script.We now know the post-commit
environment variables that the hook will change. Write down this information.
Git hooks is a standard script, so tell git what interpreter to use in the first line:
#!/bin/bash
Then we'll have git unpack the latest version of the Code repository (after the latest commit) to the Web server's root directory. This requires that the working directory be set to the Apache file root directory, and the Git directory is set as the repository directory.
At the same time, we need to make sure that this process succeeds every time, even if there is a conflict to enforce. The following script is written in this way:
#!/bin/bashgit --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f
At the this point, we is almost done. However, we need to look for extra close at the environmental variables, which is set each time the post-commit
hook is called. In particular, the are GIT_INDEX_FILE
set to .git/index
.
This is basically done. The next task is to work on environment variables. post-commit
One of the environment variables changed when the hook was called is GIT_INDEX_FILE
changed .git/index
, and this is the focus of our attention.
This path is relative to the working path, and our current working path is /var/www/html
, and here is no .git/index
directory, causing the script to go wrong. So, we need to manually change this variable back to the correct path. This unset instruction needs to be placed before the checkout directive, like this:
#!/bin/bashunset GIT_INDEX_FILEgit --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f
Many times, this kind of problem is difficult to follow. If you don't realize the problem of environment variables before using git hooks, you'll always be digging around.
In short, our script is complete and now save the exit.
Then, we need to add execute permissions to this script file:
chmod +x .git/hooks/post-commit
Now go back to the directory where the project is, and try a commit.
cd ~/projgit commit -m "here we go..."
Now look at the effect in the browser, is not what we just wrote index.html
:
http://你的服务器IP
As we can see, the code you just submitted has been automatically deployed to the Web server's file root directory. Try again to update the point content:
echo "<p>Here is a change.</p>" >> index.htmlgit add .git commit -m "First change"
Refresh the browser page to see if the change takes effect:
You see, this makes local testing much more convenient. Of course, as we said earlier, the production environment is not such a use. The code to be on the production environment must be carefully tested and validated.
Deploy to another production server using a git hookBelow I will demonstrate the correct posture for deploying code on a production environment server. I will use the Push-to-deploy model to trigger code updates for online Web servers when we push code to a bare git repository (bare git repo).
The machine we just had is now used as a development machine, and we will deploy it automatically after each commit, so we can see the change effect at any time.
Next, I'll set up another server to do our production server. There is a bare repository on this server to receive push, and a git hook that can be triggered by push behavior. Then, perform the following steps with the normal user under sudo permissions.
Set up the post-receive hook for the production serverFirst, install the Web server on the production server:
sudo apt-get updatesudo apt-get install apache2
Don't forget to set permissions on Git:
sudo chown -R `whoami`:`id -gn` /var/www/html
And don't forget to install Git:
sudo apt-get install git
Then, you create a project directory of the same name under the user's home directory. Then, initialize a bare repository under this directory. The bare repository is not a working path, and it is more suitable for servers that do not operate directly on a regular basis.
mkdir ~/projcd ~/projgit init --bare
Because this is a bare repository, it does not have a working path, and .git
all files under the path of a normal Git repository will appear directly in the root directory of the bare repository.
Now, create our post-receive
hook, which is triggered when the server receives the hook git push
. To open this file with an editor:
nano hooks/post-receive
The first line is still to define our script type. Then, tell git what we want to do, or, just like we post-commit
did before, unpack the file to the file root of the Web server:
#!/bin/bashgit --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f
Because it is a bare repository, --git-dir
you need to specify an absolute path. The others are the same.
Then we need to add some extra logic because we don't want to deploy the branch code labeled as a test-feature
production server. Our production server deploys only master
the content of the branch.
As you can see in the previous table, post-receive
hooks can accept three of the content written to the script via standard input from git, including the last version of the commit hash (), the latest version of the Commit hash (), and the reference name. We can use this information to check if ref is a master branch.
First we need to read the content from the standard input. When each ref is pushed, the above three messages are provided to the script in the format of the standard input, and three messages are separated by a space. We can read this while
information in a loop and put the git command above into this loop:
#!/bin/bashwhile read oldrev newrev refdo git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -fdone
Then we need to add a decision condition. A push from the master branch, whose ref usually contains a refs/heads/master
field. This can be used as a basis for our determination:
#!/bin/bashwhile read oldrev newrev refdo if [[ $ref =~ .*/master$ ]]; then git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f fidone
On the other hand, server-side hooks allow Git to pass some messages back to the client. The content sent to the standard output is forwarded to the client, and we can use this feature to send notifications to the user.
This notification should contain a description of the scenario and what the system eventually performed. For push from non-master, we should also return information to the user and tell them why the push was successful but the code was not deployed to the line:
#!/bin/bashwhile read oldrev newrev refdo 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." fidone
When you are finished editing, save the exit.
Finally, don't forget to set the script file to executable:
chmod +x hooks/post-receive
Now, we can access this remote server on our client.
Configure the remote server on the clientNow go back to our client, the development machine, into the project directory:
cd ~/proj
We are going to add our remote server in this directory, called production
. You need to know the user name on the remote server, the IP or domain name of the server, and the path of the bare repository relative to the user's home directory. The entire operating instruction looks like this:
git remote add production [email protected]_domain_or_IP:proj
To push a look at:
git push production master
If your SSH key is not set yet, you will need to have your password typed in. The content returned by the server should look 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 [email protected]:proj 009183f..f1b9027 master -> master
We can see the information we just post-receive
wrote in the hook here. If we access the IP or domain name of the remote server from the browser, we should be able to see the latest version of the page:
It seems that this hook has successfully deployed our code to the production environment.
Now continue to test. We create a new branch on the development machine test_feature
, check it in to the branch below:
git checkout -b test_feature
Now, the changes we make will be test_feature
made in this Test branch. To pity Dorado things first:
echo "
After this commit, enter the developer's IP in the browser and you should be able to see the change:
As we need, the Web server content on the development machine is updated. This is convenient for local testing.
Then, try test_feature
pushing this to the remote server:
git push production test_feature
The post-receive
results returned from the hook should look like this:
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 serverTo [email protected]:proj 83e9dc4..5617b50 test_feature -> test_feature
Enter the IP address of the production server in the browser and there should be no change. This is exactly what we need because our changes were not submitted to master.
Now, if we finish the test and want to push the change to the production server, we can do that. First, check in to the master
branch and merge the test_feature
branches just now:
git checkout mastergit merge test_feature
Once the merge is complete, push to the production server:
git push production master
Now go to the browser to enter the IP of the production server to see, the changes were successfully deployed:
This workflow, implemented in real-time deployment on the development machine, achieved the push master deployment in the production environment, and was happy.
SummarizeAt this point, you should have a general idea of how git hooks is used, and how you can use it to automate your tasks. It can be used to deploy code that can be used to maintain code quality and reject any changes that do not conform to the requirements.
Although Git hooks is easy to use, it is often not easy to master, and the process of troubleshooting after a problem is annoying. In order to write efficient hooks, long-term practice is required to play clear on various configurations, parameters, standard inputs, and environment variables. This can take a long time, but these inputs will ultimately help you and your team to avoid a lot of manual work, resulting in higher returns.
This article originates from Digitalocean Community. English Original: How to use Git Hooks to automate development and Deployment Tasks by Justin Ellingwood
Translation: Lazycai
Automating development deployment tasks with Git hooks