The commit-msg hook validates commit messages before they’re accepted. Use this client-side hook to ensure all commits reference valid Jira tickets.
On this page:
Understand the Commit-msg Hook
This hook enables developers to:
- Commit code locally multiple times before pushing to the server
- Merge auto-commits with auto-messages that don’t reference Jira tickets
The commit-msg hook is a Python script that runs on each developer’s local machine. No server-side hook is required.
Install the Hook
-
Download the sample commit-msg file.
-
Place the file in your local repository at:
.git/hooks/commit-msg -
Set executable permissions (Linux/macOS only):
chmod +x .git/hooks/commit-msg
Windows users: Executable permissions are not required. To run the hook without Python installed, see Python on Windows FAQ.
Configure the Script
Edit these settings in the script file:
| Setting | Description | Example |
|---|---|---|
JIRA_XMLRPC |
Path to your Jira instance | https://jira.example.com/rpc/xmlrpc |
JIRA_USER |
Developer’s Jira username | developer1 |
JIRA_PASSWORD |
Developer’s Jira password | password123 |
JIRA_TICKET_PATTERN |
Regex pattern to find ticket references | re.compile(r'[(w+7-d+?)]') |
Sample Script
#!/usr/bin/python
#
# This script is intended to be run as a commit-msg script in a GIT
# repository and check the presence of Jira ticket numbers in the log messages.
#
# - NO_Jira_TICKET_MESSAGE (an error message returned to the user when the
# svn commit message doesn't contain a jira ticket);
# - INVALID_JIRA_TICKET_MESSAGE (an error message returned to the user when
# the svn commit message contains an invalid jira ticket);
# - JIRA_XMLRPC (url of the Jira XML-RPC server);
# - JIRA_USER (name of the Jira user who has permission to look up issues in
# the Jira server);
# - JIRA_PASSWORD (password of the Jira user described above);
import sys
import re
import xmlrpclib
NO_JIRA_TICKET_MESSAGE =
'No Jira ticket present in the commit message.
Please include the Jira ticket enclosed in brackets: [ABC-789].'
INVALID_JIRA_TICKET_MESSAGE =
'Proper Jira ticket syntax was found, but none were valid tickets.
Please check the tickets and try again.'
TOO_MANY_JIRA_TICKETS_MESSAGE =
'Only 1 Jira ticket is allowed per commit. Please commit only 1 change at a time.'
INVALID_ISSUE_TYPE_MESSAGE =
'You may not commit against subtasks or task-splits.
Please commit against the parent ticket.'
JIRA_XMLRPC = 'https://jira.example.com/rpc/xmlrpc'
JIRA_USER = 'user'
JIRA_PASSWORD = 'password'
JIRA_TICKET_PATTERN = re.compile(r'[(w+?-d+?)]')
FAULT_MSG_ISSUE_NOT_FOUND = 'com.atlassian.jira.rpc.exception.RemotePermissionException'
def check_message(message):
tickets = JIRA_TICKET_PATTERN.findall(message)
if not tickets:
return NO_JIRA_TICKET_MESSAGE
if len(tickets) > 1:
return TOO_MANY_JIRA_TICKETS_MESSAGE
ticket = tickets[0]
try:
issue = proxy.jira1.getIssue(auth, ticket)
except xmlrpclib.Fault, e:
if e.faultString.find(FAULT_MSG_ISSUE_NOT_FOUND) >= 0:
return INVALID_JIRA_TICKET_MESSAGE
else:
raise
# Check if issue is subtask or task-split
if issue['type'] == '8' or issue['type'] == '5':
return INVALID_ISSUE_TYPE_MESSAGE
return None
proxy = xmlrpclib.ServerProxy(JIRA_XMLRPC)
try:
auth = proxy.jira1.login(JIRA_USER, JIRA_PASSWORD)
except:
print >> sys.stderr, 'Cannot connect to Jira: ' + str(sys.exc_info()[1])
sys.exit(2)
msg_file = open(sys.argv[1], 'r')
msg = msg_file.read()
err_msg = check_message(msg)
if err_msg:
print >> sys.stderr, 'Error: %snCommit message:n%s' % (err_msg, msg)
sys.exit(1)
Last updated: December 2025