Automating a Ticket ID to Every Git Commit with Git Hooks

Why did I want to automate the commit message?

When I first started at Acorns and got access to the repository for the web application, I saw we were working with a big React application. Lots of routes, lots of products, and lots of functionality. On top of that, LOTS of hands touching the app. When I was at Glidewell, I think we had 1 team of developers manage each application, which was about 3-6 developers per repository. At Acorns, we were talking about 3-6 TEAMS touching the same codebase.

Because of the huge number of commits, there was a standard for creating branch names as well as Git commits. We were supposed to prepend each Git commit with the ticket ID. For example, each developer was assigned a ticket that told us what feature or task we were supposed to implement or what bug we had to fix.

Branch names should follow a format similar to this:

feat/AFW-1234/add-special-components

For every commit to the app, it would follow a pattern that looked like this:

[AFW-1234] added special components
[AFW-1234] deleted unused imports
[AFW-1234] added tests for special components

In this example, the ticket ID is AFW-1234.

I was new, and it was a standard, and I love abiding by standards, so I went along went with this. Took me a few times because my hands were just so used to typing git commit -m "my message", without adding a ticket ID, byproduct of how we did it at my old work and on my freelance projects. Then I started getting used to it and adding the ticket. Then a few times, my fingers stumbled across the keyboard and instead of [AFW-1234], I typed [AFW-1324]. Then sometimes I forgot one bracket, [AFW-1234. The most accurate way was to enter square brackets `[]` and then copy and paste the ticket ID between the brackets, but do I really have to go to another webpage to copy and paste the ticket ID everytime?

Acorns is pretty good about having required checks on your repository. The app I was working on had many required checks, including wrong commit formatting. If you get the commit format wrong, the Git repository checks scream at you in red. I’ve been a good student all my life. I don’t like being screamed at. 🙁 On top of that, you either have to run git commit --amend if it was your very last commit, or git rebase -i HEAD~n (replace n with a number of commits that you want to go back to revise). You can see how to change a commit message in the Github docs. It’s ok if it’s the very last commit, but more than that, and it’s not pretty. I’m embarrassed to say that I’ve probably spent over an hour trying to rebase my commits in the beginning on multiple occasions.

After 2 days at Acorns, enough was enough.

  1. There’s a format for the branch name.
  2. There’s a format for the Git commits.

We can automate this.

I really want to reserve my brain power for the content of the ticket, not the format of Git commits. I ended up writing a node script that parsed the branch name to get the ticket ID and prepended it to the Git commits. I created a shortcut to this script by adding this script to the package.json file under the "scripts" object. Then I found a better way!

What are Git Hooks?

So Git has hooks! (And not the kind of hook that Captain Hook in Peter Pan has…har har har.) In technology, hooks usually refer to attaching to or “hooking” onto a lifecycle event or some events that commonly happen. React has hooks, WordPress has hooks, and apparently, Git also has hooks!

When a series of common Git actions occur like committing or pushing a commit, Git hooks allows you to hook into them and perform an action or execute a script before or after that action.

The prepare-commit-message hook basically executes right after the commit is prepared. The purpose of the hook is to edit the message in place, and it is not ignored by the --no-verify flag.

How to implement Git hooks

To add a hook, add a hooks folder in the root folder of your Git directory, and add prepare-commit-message file under that folder.

Directory: [your-app-root-folder]/.git/hooks/prepare-commit-message

#!/bin/sh
#husky 0.13.4

# This script serves as a shortcut to commit with your ticket id (that comes from branch name) prepended to your commit messages
#   Ex: if your branch name is "feat/AFW-1234/best-feature-ever", then when you run the command `git commit -m "test commit"`, it` will commit the message "[AFW-1234] test commit"

# get name of current branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

# get ticketid from branch name
getticketid() {
    # regex pattern to get ticket id (matching 2-6 letters, hypen, 1-5 numbers)
    ticketidpattern="([A-Z]{2,6})-([0-9]{1,5})"
    [[ $CURRENT_BRANCH =~ $ticketidpattern ]]
    TICKET_ID="${BASH_REMATCH[0]}"

    # check that the branch name has a ticket id that can be parsed
    if [ -z "$TICKET_ID" ]
    then
        # if branch name starts with task, then ticketID is TASK
        startwithtask="(^task|TASK)"
        if [[ $CURRENT_BRANCH =~ $startwithtask ]]
            then
                TICKET_ID="TASK"
            else
                echo "Error: Your branch name ($CURRENT_BRANCH) does not have a ticket id in the correct format. Proceed commiting without ticket id."
        fi
    fi
    echo "Ticket id: $TICKET_ID"
}


# don't do this on master branch
if [ -z "$BRANCHES_TO_SKIP" ]; then
  BRANCHES_TO_SKIP=(master HEAD)
fi
# get ticketid from branch name
getticketid

# Get the current branch name and check if it is excluded
BRANCH_EXCLUDED=$(printf "%s\n" "${BRANCHES_TO_SKIP[@]}" | grep -c "^$CURRENT_BRANCH$")
# Don't double-add prepending the ticket id if it's already in the commit
TICKET_ID_IN_COMMIT=$(grep -c "\[$TICKET_ID\]" $1)


if [ -n "$TICKET_ID" ] && ! [[ $BRANCH_EXCLUDED -eq 1 ]] && ! [[ $TICKET_ID_IN_COMMIT -ge 1 ]]; then 
  sed -i.bak -e "1s/^/[$TICKET_ID] /" $1
fi

Result of prepare-commit-message Git hook

If I am in the Git root folder of the app, and I have checked out the Git branch feat/AFW-1234/add-special-components, then when I run the Git commands:

git add .
git commit -m "added special components"

the commit will show up in the Git history of the repository as

[AFW-1234] added special components

Finally, the commits still have the ticket ID, and I don’t have to manually type in the ticket ID anymore! The Git hook just automatically adds the ticket ID to the beginning of the commit message. Less work, more value. Automating is the best!

Add a Comment

Your email address will not be published. Required fields are marked *