Skip to content

Instantly share code, notes, and snippets.

@insoul
Created April 10, 2012 07:14

Revisions

  1. insoul revised this gist May 15, 2012. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -87,8 +87,13 @@ def initialize(commit_msg_file)
    end

    msg = match[0][1] || ""
    msg = msg.strip[1..-1] if msg.strip.start_with?("\n")
    msg = msg.strip[1..-1] if msg.strip.start_with?($/)
    msg = msg.split(/diff --git/)[0] || ""
    # 원본 메시지가 @로 시작하는 경우 msg의 첫번째라인은 무시한다.
    # redmine 제목이고 자동으로 붙여지기 때문.
    msg = msg.split($/, 2)[1] || "" if @msg.start_with?("@")
    # msg의 마지막이 redmine주소인 경우 제거한다.
    msg = msg.gsub(/#{$/}#{redmine_url}\/issues\/\d+#{$/}*$/, '')

    @issue_id = match[0][0].to_i
    @message = msg
  2. insoul revised this gist May 15, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -79,7 +79,7 @@ def initialize(commit_msg_file)
    end
    end

    match = @msg.scan(/^#(\d+) (.*)/m)
    match = @msg.scan(/^@(\d+) (.*)/m)
    match = @msg.scan(/^(\d+) (.*)/m) if match.empty?
    match = @msg.scan(/^#{redmine_url}\/issues\/(\d+) (.*)/m) if match.empty?
    if match.empty?
  3. insoul revised this gist May 15, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -114,7 +114,7 @@ def diff

    # commit is a InputCommitMessage instance
    def self.remake(commit, issue)
    remake_msg = "##{commit.issue_id} #{issue.subject}"
    remake_msg = "@#{commit.issue_id} #{issue.subject}"
    unless commit.message.empty?
    remake_msg << "\n#{commit.message}"
    end
    @@ -125,7 +125,7 @@ def self.remake(commit, issue)
    # res is a string of remade commit message
    def parse(res)
    res = res.split("\n")
    @issue_id = res.delete_at(0).scan(/^#(\d+) /)[0][0] rescue nil
    @issue_id = res.delete_at(0).scan(/^@(\d+) /)[0][0] rescue nil
    if @issue_id.nil? or @issue_id.empty?
    raise MessageFormatError.new("cannot find issue id\n\n#{res}")
    end
  4. insoul revised this gist Apr 12, 2012. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,7 @@ module GitRedmine
    class MessageFormatError < RuntimeError; end
    class CommitLogError < RuntimeError; end
    class DiffError < RuntimeError; end
    class GitVersionError < RuntimeError; end

    module Config
    # host: Issue tracker host
    @@ -37,7 +38,9 @@ def backward_commit_hash(backward_number)
    end

    def last_commit_log
    `git log -1 --format="%B"`
    l = `git log -1 --format="%B"`
    raise GitVersionError.new('git version ~> 1.7.9') if l =~ /%B/
    l
    end

    def diff_from(hash)
  5. insoul revised this gist Apr 10, 2012. 1 changed file with 9 additions and 5 deletions.
    14 changes: 9 additions & 5 deletions git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -148,27 +148,30 @@ module Hooks
    include Config

    def commit_msg(commit_msg_file)
    puts 'Hook: commit-msg'
    commit = InputCommitMessage.new(commit_msg_file)
    issue = redmine.issue(commit.issue_id)
    remake_msg = RemakeCommitMessage.remake(commit, issue)
    File.open(commit_msg_file, 'w+') { |f| f << remake_msg }
    ret = 0
    rescue MessageFormatError => e
    error("Commit message format is not valid")
    puts "Commit message format:"
    puts " <issue id> <message>"
    puts "Example:"
    puts " 450 fix error"
    puts " #{redmine_url}/issues/450 fix error"
    return 1
    ret = 1
    rescue => e
    error(e.message)
    return 1
    ret = 1
    ensure
    puts e.backtrace.join("\n") if verbose and defined?(e) and not e.nil?
    return 0
    ret
    end

    def post_commit
    puts 'Hook: post-commit'
    out = "Commit completed, "
    commit = RemakeCommitMessage.new
    issue = redmine.issue(commit.issue_id)
    @@ -182,12 +185,13 @@ def post_commit
    end
    note = "#{commit.message}\n\n#{commit_link}\n#{diff}"
    issue.update(note)
    ret = 0
    rescue => e
    warn("#{out}, But error occurred... #{e.message}")
    return 1
    ret = 1
    ensure
    puts e.backtrace.join("\n") if verbose and defined?(e) and not e.nil?
    return 0
    ret
    end

    def error(msg)
  6. insoul revised this gist Apr 10, 2012. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions install
    Original file line number Diff line number Diff line change
    @@ -7,3 +7,8 @@ chmod +x .git_redmine/commit-msg
    chmod +x .git_redmine/post-commit
    chmod +x .git_redmine/git_redmine
    echo 'alias git_redmine=~/.git_redmine/git_redmine' >> .bash_profile

    echo ''
    echo 'Install git_redmine complete!!'
    echo 'You must edit ~/.git_redmine/config.yml'
    echo "Usage is run 'git_redmine' in git working copy"
  7. insoul revised this gist Apr 10, 2012. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion commit-msg
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/usr/bin/env ruby

    BASEDIR = ENV["HOME"] + "/.thinkreals/hooks"
    BASEDIR = ENV["HOME"] + "/.git_redmine"
    require BASEDIR + "/git_redmine"

    include GitRedmine::Hooks
    2 changes: 1 addition & 1 deletion post-commit
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/usr/bin/env ruby

    BASEDIR = ENV["HOME"] + "/.thinkreals/hooks"
    BASEDIR = ENV["HOME"] + "/.git_redmine"
    require BASEDIR + "/git_redmine"

    include GitRedmine::Hooks
  8. insoul revised this gist Apr 10, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions git_redmine
    Original file line number Diff line number Diff line change
    @@ -18,5 +18,7 @@ if [ -e `pwd`/.git/hooks/post-commit ]; then
    exit 1
    fi

    echo 'symlink to .git/hooks/commit-msg'
    ln -s $GIT_REDMINE/commit-msg .git/hooks/commit-msg
    echo 'symlink to .git/hooks/post-commit'
    ln -s $GIT_REDMINE/post-commit .git/hooks/post-commit
  9. insoul revised this gist Apr 10, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions git_redmine
    Original file line number Diff line number Diff line change
    @@ -18,5 +18,5 @@ if [ -e `pwd`/.git/hooks/post-commit ]; then
    exit 1
    fi

    ln -s $GIT_REDMINE/commit-msg commit-msg
    ln -s $GIT_REDMINE/post-commit post-commit
    ln -s $GIT_REDMINE/commit-msg .git/hooks/commit-msg
    ln -s $GIT_REDMINE/post-commit .git/hooks/post-commit
  10. insoul revised this gist Apr 10, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions install
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,6 @@
    #!/usr/bin/env bash

    gem install redminer
    cd $HOME
    git clone git://gist.github.com/2348958.git .git_redmine
    chmod +x .git_redmine/commit-msg
  11. insoul revised this gist Apr 10, 2012. 2 changed files with 30 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions git_redmine
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    #!/usr/bin/env bash
    GIT_REDMINE=$HOME/.git_redmine

    if [ -e $GIT_REDMINE ]; then
    echo "git_redmine directory $GIT_REDMINE"
    else
    echo "ERROR: git_redmine directory $GIT_REDMINE not exists"
    exit 1
    fi
    if [ -e `pwd`/.git/hooks/commit-msg ]; then
    echo 'ERROR: Already there is commit-msg hook'
    echo ' Remove first .git/hooks/commit-msg file'
    exit 1
    fi
    if [ -e `pwd`/.git/hooks/post-commit ]; then
    echo 'ERROR: Already there is post-commit hook'
    echo ' Remove first .git/hooks/post-commit file'
    exit 1
    fi

    ln -s $GIT_REDMINE/commit-msg commit-msg
    ln -s $GIT_REDMINE/post-commit post-commit
    8 changes: 8 additions & 0 deletions install
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    #!/usr/bin/env bash

    cd $HOME
    git clone git://gist.github.com/2348958.git .git_redmine
    chmod +x .git_redmine/commit-msg
    chmod +x .git_redmine/post-commit
    chmod +x .git_redmine/git_redmine
    echo 'alias git_redmine=~/.git_redmine/git_redmine' >> .bash_profile
  12. insoul revised this gist Apr 10, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions commit-msg
    Original file line number Diff line number Diff line change
    @@ -5,3 +5,4 @@ require BASEDIR + "/git_redmine"

    include GitRedmine::Hooks
    exit commit_msg(ARGV[0])

  13. insoul revised this gist Apr 10, 2012. 3 changed files with 19 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions commit-msg
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    #!/usr/bin/env ruby

    BASEDIR = ENV["HOME"] + "/.thinkreals/hooks"
    require BASEDIR + "/git_redmine"

    include GitRedmine::Hooks
    exit commit_msg(ARGV[0])
    5 changes: 5 additions & 0 deletions config.yml.sample
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    #host: localhost
    #access_key: 1234567890abcdefg1234567890abcdefg123456
    #port: 80 # default 80
    #verbose: true # default nil
    #reqtrace: true # default nil
    7 changes: 7 additions & 0 deletions post-commit
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    #!/usr/bin/env ruby

    BASEDIR = ENV["HOME"] + "/.thinkreals/hooks"
    require BASEDIR + "/git_redmine"

    include GitRedmine::Hooks
    exit post_commit
  14. insoul created this gist Apr 10, 2012.
    205 changes: 205 additions & 0 deletions git_redmine.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,205 @@
    require 'rubygems'
    require 'redminer'
    require 'yaml'

    module GitRedmine
    class MessageFormatError < RuntimeError; end
    class CommitLogError < RuntimeError; end
    class DiffError < RuntimeError; end

    module Config
    # host: Issue tracker host
    # port: Issue tracker port # default 80
    # access_key: Issue tracker api access_key
    # verbose: Trace error option # default false
    CONFIG = YAML.load_file(File.dirname(__FILE__) + '/config.yml') rescue {}

    def host; CONFIG["host"] end
    def port; CONFIG["port"] || 80 end
    def access_key; CONFIG["access_key"] end
    def verbose; CONFIG["verbose"] end

    def redmine
    opts = {:port=>port, :verbose=>verbose, :reqtrace=>CONFIG["reqtrace"]}
    @redmine ||= Redminer::Base.new(host, access_key, opts)
    end

    def redmine_url
    ret = "http://#{host}"
    ret << ":#{port}" if port != 80
    ret
    end
    end

    module GitCommands
    def backward_commit_hash(backward_number)
    `git log -#{backward_number} --format='%H'`.split("\n").last
    end

    def last_commit_log
    `git log -1 --format="%B"`
    end

    def diff_from(hash)
    `git diff #{hash}..HEAD --stat`
    end

    def origin_path
    remote = `git remote -v`.split("\n")[0]
    return if remote.nil?

    url = remote.split(" ")[1]
    url = url.gsub(/.*@/, '').gsub(/\.git$/, '').gsub(':', '/')
    url
    end
    end

    class CommitMessage
    include Config
    attr_accessor :issue_id, :message

    def issue_url
    "#{redmine_url}/issues/#{issue_id}"
    end
    end

    class InputCommitMessage < CommitMessage
    def initialize(commit_msg_file)
    @msg = ""
    File.open(commit_msg_file) do |f|
    while true
    begin
    @msg << f.readline
    rescue EOFError
    break
    end
    end
    end

    match = @msg.scan(/^#(\d+) (.*)/m)
    match = @msg.scan(/^(\d+) (.*)/m) if match.empty?
    match = @msg.scan(/^#{redmine_url}\/issues\/(\d+) (.*)/m) if match.empty?
    if match.empty?
    raise MessageFormatError.new("commit message format error\n\n#{@msg}")
    end

    msg = match[0][1] || ""
    msg = msg.strip[1..-1] if msg.strip.start_with?("\n")
    msg = msg.split(/diff --git/)[0] || ""

    @issue_id = match[0][0].to_i
    @message = msg
    end
    end

    class RemakeCommitMessage < CommitMessage
    include GitCommands
    attr_accessor :hash

    def initialize
    @hash = backward_commit_hash(2)
    res = last_commit_log
    raise CommitLogError.new if res.length < 3
    self.parse(res)
    end

    def diff
    df = diff_from(self.hash)
    raise DiffError.new("cannot retrieve diff") if df.empty?
    df
    end

    # commit is a InputCommitMessage instance
    def self.remake(commit, issue)
    remake_msg = "##{commit.issue_id} #{issue.subject}"
    unless commit.message.empty?
    remake_msg << "\n#{commit.message}"
    end
    remake_msg << "\n\n#{redmine_url}/issues/#{issue.id}"
    remake_msg
    end

    # res is a string of remade commit message
    def parse(res)
    res = res.split("\n")
    @issue_id = res.delete_at(0).scan(/^#(\d+) /)[0][0] rescue nil
    if @issue_id.nil? or @issue_id.empty?
    raise MessageFormatError.new("cannot find issue id\n\n#{res}")
    end
    @issue_id = issue_id.to_i

    issue_link = res.index { |ln| ln =~ /^http:\/\/#{host}/ }
    unless issue_link.nil?
    res.delete_at(issue_link) or
    (res[its-1].strip.empty? and res.delete_at(issue_link-1))
    end
    @message = res
    end

    def origin_url
    path = origin_path
    return if path.nil?
    "http://#{path}/commit/#{hash}"
    end
    end

    module Hooks
    include GitCommands
    include Config

    def commit_msg(commit_msg_file)
    commit = InputCommitMessage.new(commit_msg_file)
    issue = redmine.issue(commit.issue_id)
    remake_msg = RemakeCommitMessage.remake(commit, issue)
    File.open(commit_msg_file, 'w+') { |f| f << remake_msg }
    rescue MessageFormatError => e
    error("Commit message format is not valid")
    puts "Commit message format:"
    puts " <issue id> <message>"
    puts "Example:"
    puts " 450 fix error"
    puts " #{redmine_url}/issues/450 fix error"
    return 1
    rescue => e
    error(e.message)
    return 1
    ensure
    puts e.backtrace.join("\n") if verbose and defined?(e) and not e.nil?
    return 0
    end

    def post_commit
    out = "Commit completed, "
    commit = RemakeCommitMessage.new
    issue = redmine.issue(commit.issue_id)
    diff = commit.diff
    diff = "<pre>#{diff}</pre>"
    commit_link = commit.origin_url
    commit_link = if commit_link
    "commit:\"#{commit.hash[0..6]}\":#{commit.origin_url}"
    else
    "commit:#{commit.hash[0..6]}"
    end
    note = "#{commit.message}\n\n#{commit_link}\n#{diff}"
    issue.update(note)
    rescue => e
    warn("#{out}, But error occurred... #{e.message}")
    return 1
    ensure
    puts e.backtrace.join("\n") if verbose and defined?(e) and not e.nil?
    return 0
    end

    def error(msg)
    puts "ERROR!\n\t#{msg}"
    puts "\tWanna skip verification? use --no-verify option\n"
    true
    end

    def warn(msg)
    puts "WARNING!\n\t#{msg}\n"
    true
    end
    end

    end