Skip to content

Instantly share code, notes, and snippets.

@osulyanov
Forked from Earendil95/task_hooks.md
Created December 5, 2017 13:15
Show Gist options
  • Save osulyanov/03dfaab4904c3ced24803d49b2b83612 to your computer and use it in GitHub Desktop.
Save osulyanov/03dfaab4904c3ced24803d49b2b83612 to your computer and use it in GitHub Desktop.
Git hooks for automatic reference issues in commit

Description

This hooks will remind you to reference task in your commit, and remember your task ref for branch. Your commit messages will have style "[reference] message"

Usage

  1. Create two files in your repo - e.g. [PROJECT_ROOT]/hooks/prepare-commit-msg.rb and [PROJECT_ROOT]/hooks/post-checkout.rb
  2. Copy to first file (here will assume that this is a [PROJECT_ROOT]/hooks/prepare-commit-msg.rb):
#!/usr/bin/env ruby

require 'fileutils'

MESSAGE_WITH_ISSUE_PATTERN = /^\[(#\d+)\]/ # Regexp that matches messages with specified issue
                                           # Remember that actual issue link will be taken from 
                                           # message.match(MESSAGE_WITH_ISSUE_PATTERN)[1]
                                           # so currrent example is for github issues references
ISSUE_PATTERN = /^#\d+$/ # Regexp that matches issue reference
                         # e.g. if you want to reference github issues it will be /^#\d+$/
                         # or may be you want to reference trello -- /^https:\/\/trello.com/.+/

# Have no idea why STDIN.reopen is not working, so this
def read_stdin
  open '/dev/tty' do |f|
    input = f.gets.chomp
    yield input
  end
end

def save_issue(number)
  File.write '.git/ISSUE', number
  branch = File.read('.git/HEAD').match /^ref: refs\/heads\/(.+)/
  FileUtils.mkdir_p '.git/refs/issues'
  File.write ".git/refs/issues/#{branch[1]}", number if branch
end

def cache_issue(number, force: false)
  if !force && File.exists?('.git/ISSUE')
    return if number.to_s == File.read('.git/ISSUE')

    puts "Do you want to update issue in branch? y(es)/anything else"

    read_stdin do |input|
      save_issue number if input.downcase.strip.match? /^y(es)?$/
    end
  else
    save_issue number
  end
end

msg = File.read(ARGV[0])

issue_match = msg.match MESSAGE_WITH_ISSUE_PATTERN
unless issue_match.nil?
  cache_issue issue_match[1]
  exit 0
end

issue = File.read('.git/ISSUE') if File.exists? '.git/ISSUE'

File.write(ARGV[0], "[#{issue}] #{msg}") && exit(0) if !issue.nil? && !issue.empty?

puts "Do you want to specify issue? Enter a reference if yes or anything else if no."

read_stdin do |input|
  if input.match? ISSUE_PATTERN
    File.write ARGV[0], "[#{input}] #{msg}"
    cache_issue input, force: true
  end
end

exit 0
  1. Specify your own regexp in MESSAGE_WITH_ISSUE_PATTERN and ISSUE_PATTERN which will satisfy your needs
  2. Copy to second file (assume that this is [PROJECT_ROOT]/hooks/post-checkout.rb):
#!/usr/bin/env ruby

heads = '.git/refs/heads/'
issues = '.git/refs/issues/'

branch = File.read('.git/HEAD').match /^ref: refs\/heads\/(.+)/

if !branch.nil? && File.exists?(issues + branch[1])
  issue = File.read issues + branch[1]
  puts "Current issue is #{issue}"
  File.write '.git/ISSUE', issue
else
  puts "Current issue is not specified yet"
  File.write '.git/ISSUE', ''
end

exit 0
  1. Create links:
ln -s ../../hooks/prepare-commit-msg.rb .git/hooks/prepare-commit-msg
ln -s ../../hooks/post-checkout.rb .git/hooks/post-checkout
  1. Add permissions:
chmod u+x .git/hooks/prepare-commit-msg
chmod u+x .git/hooks/post-checkout
  1. When you will run git commit -m "My awesome commit!" you will be asked for issue. For next time, your commit in same branch will marked automatically.
  2. If you want to redefine issue in branch, you just need to ref new issue. E.g. with default values (reference to GitHub) git commit -m "[#5] One more awesome commit". You will be asked if you want to update issue ref permanently or just for this commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment