開発
Customizing git with hooks
denvazh
What are we talking about?
It is always nice if certain software ( and hardware as well ) provide good enough functionality by default. However, as soon as any decent human can potentially come up with enormous amount of ideas, for certain software/hardware it might be a tough case to deal with. Hopefully, git is flexible enough to provide great default functionality as well as possibility to customize certain actions with custom hooks.
Getting started
Reason why I started looking into hooks was very simple. Once in a while I have some ideas, and I want to start working on them right away. So I just create repository and start writing some test code to actually see the potential of the idea. Once in a while I might do it on a different remote server (usually virtual one), which absolutely lacks any decent development environment settings(such as colors, editors, username, e-mail and etc.). That’s where I started to think, if it would be nice to have some nice script to check those settings before making any commit.
Note: you still have to make basic setup of the project repository at least, but I will omit this important issue this time.
Writing “hooks”
For a simple test script I’ve chosen very easy function to implement: to check whether user had set username and e-mail in his ~/.gitconfig config file.
But before we start, you should know how those hooks works. In few words: there are number of pre-definied actions which is possible to customize. In .git folder of any project you can find hooks folder bundled with default sample hooks scripts (in fact, we will be using this as well). All action hooks have generic template: %timing%-%action%, where %action%
is a command you usually use with git, for example commit, and %timing% – moment when hook will be fired up ( this can be either pre, post or any other). We will be using pre-commit hook, because we would like git to run some script right before committing code to the project.
So let’s create small nice script for some project you already have (or you just can create new one with git init).
First, we will create file in .git/hooks and name it pre-commit.
Secondly, we will be using bash so we add this
1 | #!/usr/bin/env bash |
to the first line of the file.
Yay! Now, we finally can start writing the script.
Let’s setup some basic variables where we store error codes for tests we run, and then add also option for default email domain:
1 2 3 | COMPANY_DOMAIN= "some.domain" USER_ERR=0 EMAIL_ERR=0 |
To read defined user information it is possible to use git config -l command to list all possible settings in the system and then parse output for the lines we want:
1 2 | USER= "$(git config -l | grep " user.name " | awk -F" = " '{print $2}')" EMAIL= "$(git config -l | grep " user.email " | awk -F" = " '{print $2}')" |
When we got information we wanted, we can run tests and see whether user have everything in order:
1 2 3 4 5 6 7 8 9 10 11 12 13 | if [ -z "${USER}" ]; then USER_ERR=1 fi if [ -z "${EMAIL}" ]; then EMAIL_ERR=1 else if [ ! -z "${COMPANY_DOMAIN}" ]; then MAIL=$( echo "${EMAIL}" | awk -F "@" '{print $2}' ) if [ "${MAIL}" != "${COMPANY_DOMAIN}" ]; then EMAIL_ERR=2 fi fi fi |
After this part of the code is finished all error flags should be set.
Note: if COMPANY_DOMAIN variable is set (and not zero length) then it will try to make domain check, and in case its different from user settings, it will set EMAIL_ERR flag to 2.
Now we can have some output to inform user about what happened if there was any error, or do nothing if everything was in order.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | if [[ $USER_ERR - ne 0 || $EMAIL_ERR - ne 0 ]]; then echo "Error: aborting commit. See why below." if [ $USER_ERR - eq 1 ]; then echo -e "\nReason: username was not set" helpUser fi if [ $EMAIL_ERR - eq 1 ]; then echo -e "\nReason: email was not set" helpEmail fi if [[ ! -z "${COMPANY_DOMAIN}" && $EMAIL_ERR - eq 2 ]]; then echo -e "\nReason: email domain do not match expected one." echo "Entered email domain: $(echo " ${EMAIL} " | awk -F" @ " '{print $2}')" echo "Expected email domain: ${COMPANY_DOMAIN}" fi exit 1 else exit 0 fi |
Note: exit codes are important! when running hooks git is tracking exit codes of the script to decide whether to go on, or to about running action.
Code above also has these commands: helpUser, helpEmail. Those are custom functions you can define in bash:
1 2 3 4 5 6 7 8 9 10 11 | function helpUser(){ echo -e "To setup username (it is recommended to set it up globally) run the following command:" echo 'git config --global user.name "%Firstname Lastname%"' echo -e "where %Firstname Lastname% is your first name and last name" } function helpEmail(){ echo -e "To setup e-mail (it is recommended to set it up globally) run the following command:" echo 'git config --global user.email "%e-mail@e-mail.com%"' echo -e "where %e-mail@e-mail.com% is your e-mail address" } |
That’s it! We made our first git-hook! But…let’s see how it works:)
Using git hook
Let’s try case, when script exits with error code 0.
For this I have both, e-mail and username already defined.
1 2 3 | $: git config -l | grep user user.name=Denis Vazhenin user.email=denis@some.domain |
In this case, if I run git commit everything should work fine (unless something terrible happens, like arrival of ufo or something…
but this is out of scope of this blog post).
Next, we delete (or move elsewhere) file ~/.gitconfig, and enjoy some nice error messages we created few moments ago:
1 | $: git config -l | grep user |
Let’s try. If we try to commit, we will see the following:
1 2 3 4 5 6 7 8 9 10 11 12 | $ git commit Error: aborting commit. See why below. Reason: username was not set To setup username (it is recommended to set it up globally) run the following command : git config --global user.name "%Firstname Lastname%" where %Firstname Lastname% is your first name and last name Reason: email was not set To setup e-mail (it is recommended to set it up globally) run the following command : git config --global user.email "%e-mail@e-mail.com%" where %e-mail@e-mail.com% is your e-mail address |
For the last case we need to setup wrong domain for user email:
1 | $: git config --global user.email "denis@denis.denis" |
Now error message changes to:
1 2 3 4 5 6 7 8 9 10 11 | $ git commit Error: aborting commit. See reason below. Reason: username was not set To setup username (it is recommended to set it up globally) run the following command : git config --global user.name "%Firstname Lastname%" where %Firstname Lastname% is your first name and last name Reason: email domain do not match expected one. Entered email domain: denis.denis Expected email domain: some.domain |
Conclusion
We have successfully created hook for git-commit action and made our first steps in the world of git customization.