Skip to content

Instantly share code, notes, and snippets.

@jeremy-w
Last active August 23, 2023 17:44
Show Gist options
  • Save jeremy-w/818b2980f0d9768da83249458aa67678 to your computer and use it in GitHub Desktop.
Save jeremy-w/818b2980f0d9768da83249458aa67678 to your computer and use it in GitHub Desktop.
A git hook to tag your commits with the JIRA issue ID. Requires a git client that runs the hook like Git Fork, Git Kraken, or anything TUI/CLI.
#!/bin/bash
#
# jira-prepare-commit-msg: Easy commit tagging for Jira
#
# https://gist.github.com/jeremy-w/818b2980f0d9768da83249458aa67678
#
# If you name your branch to include the Jira issue key,
# this hook will automatically add the issue key to your commit messages.
# This makes it easy to trace work back to Jira, and it also means
# your commits show up in the Activity tab for that issue.
#
# To enable this hook:
#
# cd gitRepoParentDirectory
# ln -sf PATH/TO/jira-prepare-commit-msg */.git/hooks/prepare-commit-msg
#
# Execution context:
#
# - called by `git commit` in the git directory
#
# Parameters:
#
# - $1 is the filename holding the commit message. It already has stuff in it.
# - $2 is the commit message source (message, template, squash, commit, ...)
# - $3 is a commit ID - it's only present committing with -c, -C, or --amend
#
# The hook's purpose is to edit the commit
# message file, $1. If the hook fails with a non-zero status,
# the commit is aborted.
#
# Jeremy W. Sherman (@jeremy-w on GitHub)
# 2019-04-05
#
# 2020-05-19:
# * NEW: Use jira.baseUrl config variable, if present, to include a link rather
# than just the issue ID in the commit messages.
# Example: git config --set jira.baseUrl https://YourCorp.atlassian.net/browse
#
# 2021-11-02:
# * FIXED: With newer git versions, the first commit on a branch was not
# picking up the branch name.
#
# 2023-08-23:
# * FIXED: A detached head rebase only saw the branch as HEAD, so failed to
# read the ticket ID from the branch name. Now checks several files for the
# branch name; see the rebasing-branch function for details.
PROGNAME=$0
function debug() {
# comment out next line to shut it up, uncomment to get some output
# echo "$PROGNAME: $*" >&2
: # empty functions aren't allowed, so here's a NOP
}
debug "Begun. Args: $*"
# Project Key format is configurable, but defaults to roughly this.
# https://confluence.atlassian.com/adminjiraserver/changing-the-project-key-format-938847081.html
ISSUE_REGEX="([A-Z]{1}[A-Z0-9_]{1,}-[0-9]{1,})"
if test "x$ISSUE_ID" = x; then
# Infer from tail end of branch name.
# While rev-parse is more accurate, it used to not work during rebase, but on
# recent versions, reports HEAD, while describe reports a hash alone.
# See: https://github.com/git/git/blob/041f5ea1cf987a4068ef5f39ba0a09be85952064/contrib/completion/git-prompt.sh#L446-L447
branch=$(git rev-parse --abbrev-ref HEAD || git describe --contains --all --always)
debug "Issue ID unset; inferring from branch: $branch"
if [[ "$branch" = "HEAD" ]]; then
debug "Branch is just HEAD. Scrounging around."
# See answer: https://stackoverflow.com/a/59115583
# To question: https://stackoverflow.com/questions/34213120/find-branch-name-during-git-rebase
rebasing-branch() {
for location in rebase-merge rebase-apply BISECT_HEAD; do
path=$(git rev-parse --git-path ${location})
if test -d "${path}"; then
revision=$(<"${path}/head-name")
echo "${revision##refs/heads/}"
return 0
fi
done
}
branch="$(rebasing-branch)"
fi
if [[ "$branch" =~ $ISSUE_REGEX$ ]]; then
ISSUE_ID=${BASH_REMATCH[1]}
debug "Inferred ISSUE_ID=$ISSUE_ID"
fi
fi
case "$2,$3" in
merge,)
debug "Commenting out those conflicts lines for ya."
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1"
;;
# The checks here are:
# - do we have an issue id to inject?
# - is there already one in the message body that's not commented out?
# (this avoids treating the ID in the branch name as an already-present ID)
*) if test \! "x$ISSUE_ID" = x && ! grep "^[^#]" "$1" | grep -E -q "$ISSUE_REGEX"; then
debug "Detected no issue ID in commit body. Adding it now as $ISSUE_ID."
# If we have URL info, then use that.
JIRA_ISSUE_URL_BASE=$(git config --get jira.baseUrl)
if test \! "x$JIRA_ISSUE_URL_BASE" = x; then
debug "Detected JIRA_ISSUE_URL_BASE of $JIRA_ISSUE_URL_BASE."
ISSUE_ID="$JIRA_ISSUE_URL_BASE/$ISSUE_ID"
debug "ISSUE_ID has been updated to $ISSUE_ID."
fi
# Place the issue ID after the commit message.
printf '\n\n%s\n' "$ISSUE_ID" >>"$1"
# If GIT_EDITOR is literally a colon character, there is no editor.
# Otherwise, append some comments to help the person editing the
# commit message.
if test \! "$GIT_EDITOR" = :; then
debug "Running interactive - adding some Smart Commit Syntax advice."
cat <<EOT >>"$1"
# You can log time, add comments, and trigger
# transitions using Smart Commit syntax.
#
# Example:
# JRA-123 JRA-234 JRA-345 #resolve #time 2d 5h #comment Task completed ahead of schedule
#
# See: <https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html>
EOT
fi
else
debug "Either no ISSUE_ID was detected, or there appears to be an issue ID already in the commit body."
debug " ISSUE_REGEX=$ISSUE_REGEX"
debug " ISSUE_ID=$ISSUE_ID"
debug " Commit body:\n$(cat "$1")"
fi ;;
esac
debug "Done."
@abijlani
Copy link

abijlani commented Oct 27, 2020

You might have to make the .git/hooks/prepare-commit-msg executable:

  • chmod +x .git/hooks/prepare-commit-msg

Update! Do not use Git tracker. It does not work as Jeremy mentioned below.
Git tracker: to enable Git clients to add the Jira ticket ID use git-tracker
- brew install git-tracker
- git-tracker init

Tested the above script in Github Desktop Client and it works.

@jeremy-w
Copy link
Author

git-tracker supports Pivotal Tracker-style IDs, which are just numbers, but it won't include the project key that Jira prefixes to the issue ID. That's why this exists. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment