Creating a dynamic commit message template

Developers can be encouraged to do the right thing, or developers can be forced to do the right thing; however, in the end, developers need to spend time coding. So, if a good commit message is required, we can use the prepare-commit-msg hook to assist the developer.

In this example, we will create a commit message for developers that contains information about the state of the work area. It will also insert some information from a web page; this could just as well be defect information from Bugzilla for instance.

Getting ready

To start with this exercise, we will not be cloning a repository, but we will be creating one. For doing this, we will be using git init, as shown in the following code. You can use git init <directory> to create a new repository somewhere, or you can also go to a directory and execute git init, and Git will create a repository for you.

$ git init chapter7
Initialized empty Git repository in c:/Users/Rasmus/repos/chapter7/.git/
$ cd chapter7

How to do it...

We have our chapter7 directory where we just initialized our repository. In this directory, the hooks are already available. Just look into the .git/hooks directory. We will be using the prepare-commit-msg hook. Perform the following steps:

  1. Start by looking into the folder with the following hooks:
    $ ls .git/hooks/
    applypatch-msg.sample  pre-applypatch.sample  
    pre-rebase.sample      commit-msg.sample
    pre-commit.sample      prepare-commit-msg.sample
    post-update.sample     pre-push.sample        
    update.sample
    
  2. As you can see, there are plenty of hooks in each of the hook files. There is an example script and a small explanation to what the hook does and when it is executed. To enable prepare-commit-msg, rename the file as shown in the following code:
    $ cd .git/hooks/
    $ mv prepare-commit-msg.sample prepare-commit-msg
    $ cd ../..
    
  3. Open the prepare-commit-msg file in you preferred editor; I prefer gVim.
  4. You can read the information in the file, but for our examples, we will clear the file so that we can include the script.
  5. Now include the following command in the file:
    #!/bin/bash 
    echo "I refuse to commit"
    exit 1
    
  6. Save the file.
  7. Finally, try to commit something or nothing. Usually, you cannot make a commit that is empty, but with the --allow-empty option, you can create an empty commit as follows:
    $ git commit --allow-empty
    I refuse to commit
    
  8. As you can see, we get the message we put in the prepare-commit-msg script file. You can check whether we don't have a commit by using git log -1 as follows:
    $ git log -1
    fatal: bad default revision 'HEAD'
    

    There is no commit, and we get an error message that we have not seen before. The message has to be there because there is no commit so far in this repository. Before we make further changes to the script, we should know that the prepare commit message hook takes some arguments depending on the situation. The first argument is always .git/COMMIT_EDITMSG, and the second argument can be merge, commit, squash, or template, depending on the situation. We can use these in the script.

  9. Change the script so that we can reject amending commits as follows:
    #!/bin/bash
    if [ "$2" == "commit" ]; then 
      echo "Not allowed to amend"
      exit 1
    fi
    
  10. Now that we have changed the script, let's create a commit and try to amend it as follows:
    $ echo "alot of fish" > fishtank.txt
    $ git add fishtank.txt
    $ git commit -m "All my fishes are belong to us"
    [master (root-commit) f605886] All my fishes are belong to us
     1 file changed, 1 insertion(+)
     create mode 100644 fishtank.txt
    
  11. Now that we have a commit, let's try to amend it using git commit --amend:
    $ git commit --amend
    Not allowed to amend
    
  12. As we expected, we were not allowed to amend the commit. If we wish to extract some information, for instance, from a bug handling system, we will have to put this information in to the file before opening the editor. So, again, we will change the script as follows:
    #!/bin/bash
    if [ "$2" == "commit" ]; then 
      echo "Not allowed to amend"
      exit 1
    fi
    MESSAGE=$(curl http://whatthecommit.com | grep "<p>" | sed 's/<p>//')
    sed -i -r "s/# Please/$MESSAGE
    &/" $1:
    
  13. This script downloads a commit message from http://www.whatthecommit.com/ and inserts it into the commit message; so, every time you commit, you will get a new message from the web page. Let's give it a try by using the following command:
    $ echo "gravel, plants, and food" >>fishtank.txt
    $ git add fishtank.txt
    $ git commit
    
  14. When the commit message editor opens, you should see a message from whatthecommit.com. Close the editor, and using git log -1, verify whether we have the commit as follows:
    git log -1
    commit c087f75665bf516af2fe30ef7d8ed1b775bcb97d
    Author: Rasmus Voss <[email protected]>
    Date:   Wed Mar 5 21:12:13 2014 +0100
    
        640K ought to be enough for anybody
    
  15. As expected, we have succeeded with the commit. Obviously, this is not the best message to have for the committer. However, what I have done for my current employer is listed the bugs assigned to the developer as follows in the commit message:
    # You have the following artifacts assigned
    # Remove the # to add the artifact ID to the commit message
    
    #[artf23456] Error 02 when using update handler on wlan
    #[artf43567] Enable Unicode characters for usernames
    #[artf23451] Use stars instead of & when keying pword
    
  16. This way, the developer can easily select the correct bug ID, or the artefact ID from team Forge in this case, using the correct format for the other systems that will look into the commit messages.

There's more...

You can extend the functionalities of the prepare commit message hook very easily, but you should bear in mind that the waiting time for fetching some information should be worth the benefits. One thing that is usually easy to check is a dirty work area.

Here, we need to use the git status command in the prepare commit message hook, and we need to predict whether we will have modified files after the commit:

  1. To check this, we need to have something staged for committing and some unstaged changes as follows:
    $ git status
    On branch master
    nothing to commit, working directory clean
    
  2. Now, modify the fishtank.txt file:
    $ echo "saltwater" >>fishtank.txt
    
  3. Use git status --porcelain to check the work area:
    $ git status --porcelain
     M fishtank.txt
    
  4. Add the file to the staging area using git add:
    $ git add fishtank.txt
    
  5. Now try git status –porcelain:
    $ git status --porcelain
    M  fishtank.txt
    
  6. What you should note is the space before M the first time we use the --porcelain option for git status. The porcelain option provides a machine-friendly output that shows the state of the files for git status. The first character is the status in the staging area, whereas the second character is the status in the work area. So, MM fishtank.txt would mean the file is modified in the work area and in the staging area. So, if you modify fishtank.txt again, the following is the result you can expect:
    $ echo "sharks and oysters" >> fishtank.txt
    $ git status --porcelain
    MM fishtank.txt
    
  7. As expected, the output from git status is MM fishtank.txt. We can use this in the hook to tell whether the work area will have uncommitted changes after we commit. Add the following command to the prepare-commit-msg file:
    for file in $(git status --porcelain)
    do
      if [ ${file:1:1} ]; then 
        DIRTY=1
      fi
    done
    if [ "${DIRTY}" ]; then 
      sed -i -r "s/# Please/You have a dirty workarea are you sure you wish to commit ?
    &/" $1
    fi
    
  8. First, we list all the files that have changed with git status --porcelain. Then, for each of these, we check whether there is a second character. If this is true, we will have a dirty work area after the commit. In the end, we just insert the message in the commit message so that it is available for the developer to see. Let's try and commit the change by using the following command:
    $ git commit
    
  9. Note that you have a message like the following one. The first line might be different as we still have the message from http://www.whatthecommit.com/:
    somebody keeps erasing my changes.
    You have a dirty workarea are you sure you wish to commit ?
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    # On branch master
    # Changes to be committed:
    #       modified:   fishtank.txt
    #  
    # Changes not staged for commit:
    #       modified:   fishtank.txt
    #
    
  10. Saving the file and closing the editor will create the commit. Verify with git log -1 as follows:
    $ git log -1
    commit 70cad5f7a2c3f6a8a4781da9c7bb21b87886b462
    Author: Rasmus Voss <[email protected]>
    Date:   Thu Mar 6 08:25:21 2014 +0100
    
        somebody keeps erasing my changes.
        You have a dirty workarea are you sure you wish to commit ?
    
  11. We have the information we expected. The text about the dirty work area is in the commit message .To clean up nicely before the next exercise, we should reset our work area to HEAD as follows:
    $ git reset --hard HEAD
    HEAD is now at 70cad5f somebody keeps erasing my changes.
    

Now, it is just a matter of finding out what suits you. Is there any information you would like to check before you commit and potentially push the code to a remote? I can think of the following options:

  • Style checks in code
  • Pylint to check your Python scripts
  • Check for files that you are not allowed to add to Git

There are several other items, probably one for every organization or development team in the world. However, this clearly is one way of taking tedious manual work away from the developer so that he or she can focus on coding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset