Commit messages are now scanned for referenced or fixed issue IDs.
Keywords and the status to apply to fixed issues can be defined in Admin -> Settings. Default keywords: - for referencing issues: refs, references, IssueID - for fixing issues: fixes,closes There's no default status defined for fixed issue. You'll have to specify it if you want to enable auto closure of issues. Example of a working commit message: "This commit references #1, #2 and fixes #3" git-svn-id: http://redmine.rubyforge.org/svn/trunk@473 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
class Changeset < ActiveRecord::Base
|
||||
belongs_to :repository
|
||||
has_many :changes, :dependent => :delete_all
|
||||
has_and_belongs_to_many :issues
|
||||
|
||||
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
|
||||
validates_numericality_of :revision, :only_integer => true
|
||||
@@ -27,4 +28,41 @@ class Changeset < ActiveRecord::Base
|
||||
self.commit_date = date
|
||||
super
|
||||
end
|
||||
|
||||
def after_create
|
||||
scan_comment_for_issue_ids
|
||||
end
|
||||
|
||||
def scan_comment_for_issue_ids
|
||||
return if comment.blank?
|
||||
# keywords used to reference issues
|
||||
ref_keywords = Setting.commit_ref_keywords.downcase.split(",")
|
||||
# keywords used to fix issues
|
||||
fix_keywords = Setting.commit_fix_keywords.downcase.split(",")
|
||||
# status applied
|
||||
fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id)
|
||||
|
||||
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw.strip)}.join("|")
|
||||
return if kw_regexp.blank?
|
||||
|
||||
# remove any associated issues
|
||||
self.issues.clear
|
||||
|
||||
comment.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
|
||||
action = match[0]
|
||||
target_issue_ids = match[1].scan(/\d+/)
|
||||
target_issues = repository.project.issues.find_all_by_id(target_issue_ids)
|
||||
if fix_status && fix_keywords.include?(action.downcase)
|
||||
# update status of issues
|
||||
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
|
||||
target_issues.each do |issue|
|
||||
# don't change the status is the issue is already closed
|
||||
next if issue.status.is_closed?
|
||||
issue.status = fix_status
|
||||
issue.save
|
||||
end
|
||||
end
|
||||
self.issues << target_issues
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@ class Issue < ActiveRecord::Base
|
||||
has_many :time_entries
|
||||
has_many :custom_values, :dependent => :delete_all, :as => :customized
|
||||
has_many :custom_fields, :through => :custom_values
|
||||
|
||||
has_and_belongs_to_many :changesets, :order => "revision ASC"
|
||||
|
||||
acts_as_watchable
|
||||
|
||||
validates_presence_of :subject, :description, :priority, :tracker, :author, :status
|
||||
|
||||
@@ -79,10 +79,19 @@ class Repository < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def scan_changesets_for_issue_ids
|
||||
self.changesets.each(&:scan_comment_for_issue_ids)
|
||||
end
|
||||
|
||||
# fetch new changesets for all repositories
|
||||
# can be called periodically by an external script
|
||||
# eg. ruby script/runner "Repository.fetch_changesets"
|
||||
def self.fetch_changesets
|
||||
find(:all).each(&:fetch_changesets)
|
||||
end
|
||||
|
||||
# scan changeset comments to find related and fixed issues for all repositories
|
||||
def self.scan_changesets_for_issue_ids
|
||||
find(:all).each(&:scan_changesets_for_issue_ids)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +44,12 @@ end %>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<br />
|
||||
|
||||
<% if @issue.changesets.any? %>
|
||||
<div style="float:right;">
|
||||
<em><%= l(:label_revision_plural) %>: <%= @issue.changesets.collect{|changeset| link_to(changeset.revision, :controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision)}.join(", ") %></em>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<b><%=l(:field_description)%> :</b><br /><br />
|
||||
<%= textilizable @issue.description %>
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
<p><em><%= @changeset.committer %>, <%= format_time(@changeset.committed_on) %></em></p>
|
||||
<%= textilizable @changeset.comment %>
|
||||
|
||||
<% if @changeset.issues.any? %>
|
||||
<h3><%= l(:label_related_issues) %></h3>
|
||||
<ul>
|
||||
<% @changeset.issues.each do |issue| %>
|
||||
<li><%= link_to_issue issue %>: <%=h issue.subject %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<div style="float:right;">
|
||||
<div class="square action_A"></div> <div style="float:left;"><%= l(:label_added) %> </div>
|
||||
<div class="square action_M"></div> <div style="float:left;"><%= l(:label_modified) %> </div>
|
||||
|
||||
@@ -50,8 +50,18 @@
|
||||
|
||||
<p><label><%= l(:setting_sys_api_enabled) %></label>
|
||||
<%= check_box_tag 'settings[sys_api_enabled]', 1, Setting.sys_api_enabled? %><%= hidden_field_tag 'settings[sys_api_enabled]', 0 %></p>
|
||||
|
||||
</div>
|
||||
|
||||
<fieldset class="box"><legend><%= l(:text_issues_ref_in_commit_messages) %></legend>
|
||||
<p><label><%= l(:setting_commit_ref_keywords) %></label>
|
||||
<%= text_field_tag 'settings[commit_ref_keywords]', Setting.commit_ref_keywords, :size => 30 %><br /><em><%= l(:text_coma_separated) %></em></p>
|
||||
|
||||
<p><label><%= l(:setting_commit_fix_keywords) %></label>
|
||||
<%= text_field_tag 'settings[commit_fix_keywords]', Setting.commit_fix_keywords, :size => 30 %>
|
||||
<%= l(:label_applied_status) %>: <%= select_tag 'settings[commit_fix_status_id]', options_for_select( [["", 0]] + IssueStatus.find(:all).collect{|status| [status.name, status.id.to_s]}, Setting.commit_fix_status_id) %>
|
||||
<br /><em><%= l(:text_coma_separated) %></em></p>
|
||||
</fieldset>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
</div>
|
||||
<% end %>
|
||||
Reference in New Issue
Block a user