Merge pull request #109 from warpc/18-commit_comments
18 commit comments
This commit is contained in:
commit
4ba268dd67
|
@ -1,7 +1,7 @@
|
||||||
class CommentsController < ApplicationController
|
class CommentsController < ApplicationController
|
||||||
before_filter :authenticate_user!
|
before_filter :authenticate_user!
|
||||||
before_filter :set_commentable, :only => [:index, :edit, :create]
|
before_filter :set_commentable, :only => [:index, :edit, :create, :update, :destroy]
|
||||||
before_filter :find_project, :only => [:index]
|
#before_filter :find_project, :only => [:index, :edit]
|
||||||
before_filter :find_comment, :only => [:edit, :update, :destroy]
|
before_filter :find_comment, :only => [:edit, :update, :destroy]
|
||||||
|
|
||||||
authorize_resource :only => [:show, :edit, :update, :destroy]
|
authorize_resource :only => [:show, :edit, :update, :destroy]
|
||||||
|
@ -12,11 +12,12 @@ class CommentsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@comment = @commentable.comments.build(params[:comment])
|
@comment = @commentable.comments.build(params[:comment]) if @commentable.class == Issue
|
||||||
|
@comment = Comment.new(params[:comment].merge(:commentable_id => @commentable.id, :commentable_type => @commentable.class.name)) if @commentable.class == Grit::Commit
|
||||||
@comment.user = current_user
|
@comment.user = current_user
|
||||||
if @comment.save
|
if @comment.save
|
||||||
flash[:notice] = I18n.t("flash.comment.saved")
|
flash[:notice] = I18n.t("flash.comment.saved")
|
||||||
redirect_to [@commentable.project, @commentable]
|
redirect_to commentable_path
|
||||||
else
|
else
|
||||||
flash[:error] = I18n.t("flash.comment.save_error")
|
flash[:error] = I18n.t("flash.comment.save_error")
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
|
@ -24,15 +25,19 @@ class CommentsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@issue = @commentable
|
@update_url = case @commentable.class.name
|
||||||
@project = @issue.project
|
when "Issue"
|
||||||
|
project_issue_comment_path(@project, @commentable, @comment)
|
||||||
|
when "Grit::Commit"
|
||||||
|
project_commit_comment_path(@project, @commentable, @comment)
|
||||||
|
end
|
||||||
|
@commentable_path = commentable_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if @comment.update_attributes(params[:comment])
|
if @comment.update_attributes(params[:comment])
|
||||||
flash[:notice] = I18n.t("flash.comment.saved")
|
flash[:notice] = I18n.t("flash.comment.saved")
|
||||||
#redirect_to :back
|
redirect_to commentable_path
|
||||||
redirect_to [@comment.commentable.project, @comment.commentable]
|
|
||||||
else
|
else
|
||||||
flash[:error] = I18n.t("flash.comment.save_error")
|
flash[:error] = I18n.t("flash.comment.save_error")
|
||||||
render :action => 'new'
|
render :action => 'new'
|
||||||
|
@ -43,7 +48,7 @@ class CommentsController < ApplicationController
|
||||||
@comment.destroy
|
@comment.destroy
|
||||||
|
|
||||||
flash[:notice] = t("flash.comment.destroyed")
|
flash[:notice] = t("flash.comment.destroyed")
|
||||||
redirect_to :back
|
redirect_to commentable_path
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -55,18 +60,31 @@ class CommentsController < ApplicationController
|
||||||
# end
|
# end
|
||||||
#end
|
#end
|
||||||
#nil
|
#nil
|
||||||
|
if params[:issue_id].present?
|
||||||
return Issue.find_by_serial_id_and_project_id(params[:issue_id], params[:project_id])
|
return Issue.find_by_serial_id_and_project_id(params[:issue_id], params[:project_id])
|
||||||
|
elsif params[:commit_id].present?
|
||||||
|
return @project.git_repository.commit(params[:commit_id])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_commentable
|
def set_commentable
|
||||||
|
find_project
|
||||||
@commentable = find_commentable
|
@commentable = find_commentable
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_comment
|
def find_comment
|
||||||
@comment = Comment.find(params[:id])
|
@comment = Comment.find(params[:id])
|
||||||
|
@comment.project = @project if @comment.commentable_type == 'Grit::Commit'
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_project
|
def find_project
|
||||||
@project = @comment.commentable.project
|
@project = Project.find(params[:project_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def commentable_path
|
||||||
|
@commentable.class == Issue ? [@project, @commentable] : commit_path(@project, @commentable.id)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -91,9 +91,10 @@ class Ability
|
||||||
can([:update, :destroy], Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)}
|
can([:update, :destroy], Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)}
|
||||||
cannot :manage, Issue, :project => {:has_issues => false} # switch off issues
|
cannot :manage, Issue, :project => {:has_issues => false} # switch off issues
|
||||||
|
|
||||||
can(:create, Comment) {|comment| can? :read, comment.commentable.project}
|
can(:create, Comment) {|comment| can? :read, comment.project || comment.commentable.project}
|
||||||
can(:update, Comment) {|comment| comment.user_id == user.id or local_admin?(comment.commentable.project)}
|
can(:update, Comment) {|comment| comment.user_id == user.id or local_admin?(comment.project || comment.commentable.project)}
|
||||||
cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues
|
#cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues
|
||||||
|
cannot(:manage, Comment) {|comment| comment.commentable_type == 'Issue' && !comment.commentable.project.has_issues} # switch off issues
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
class Comment < ActiveRecord::Base
|
class Comment < ActiveRecord::Base
|
||||||
belongs_to :commentable, :polymorphic => true
|
belongs_to :commentable, :polymorphic => true
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
attr_accessor :project
|
||||||
|
|
||||||
validates :body, :user_id, :commentable_id, :commentable_type, :presence => true
|
validates :body, :user_id, :commentable_id, :commentable_type, :presence => true
|
||||||
|
|
||||||
after_create :subscribe_on_reply
|
# FIXME
|
||||||
after_create :deliver_new_comment_notification
|
after_create :subscribe_on_reply, :unless => "commentable_type == 'Grit::Commit'"
|
||||||
|
after_create :deliver_new_comment_notification, :unless => "commentable_type == 'Grit::Commit'"
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,13 @@ class Issue < ActiveRecord::Base
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
has_many :comments, :as => :commentable
|
has_many :comments, :as => :commentable,
|
||||||
|
:finder_sql => proc { "comments.commentable_id = '#{self.id}' " +
|
||||||
|
" AND comments.commentable_type = '#{self.class.name}'"}
|
||||||
|
#'SELECT comments.* FROM comments ' +
|
||||||
|
#'WHERE comments.commentable_id = \'#{self.id}\' ' +
|
||||||
|
#' AND comments.commentable_type = \'#{self.class.name}\' ' +
|
||||||
|
#'ORDER BY comments.created_at'
|
||||||
has_many :subscribes, :as => :subscribeable
|
has_many :subscribes, :as => :subscribeable
|
||||||
|
|
||||||
validates :title, :body, :project_id, :presence => true
|
validates :title, :body, :project_id, :presence => true
|
||||||
|
|
|
@ -134,6 +134,13 @@ class Project < ActiveRecord::Base
|
||||||
@platforms ||= repositories.map(&:platform).uniq
|
@platforms ||= repositories.map(&:platform).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def commit_comments(commit, project)
|
||||||
|
comments = Comment.where(:commentable_id => commit.id, :commentable_type => 'Grit::Commit').order(:created_at)
|
||||||
|
comments.each {|x| x.project = project}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def build_path(dir)
|
def build_path(dir)
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
|
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
|
||||||
= t("layout.save")
|
= t("layout.save")
|
||||||
%span.text_button_padding= t("layout.or")
|
%span.text_button_padding= t("layout.or")
|
||||||
= link_to t("layout.cancel"), [@issue.project, @issue], :class => "text_button_padding link_button"
|
= link_to t("layout.cancel"), @commentable_path , :class => "text_button_padding link_button"
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
%a{ :name => "comments" }
|
||||||
|
.block#block-list
|
||||||
|
.content
|
||||||
|
%h2.title
|
||||||
|
= t("layout.issues.comments_header")
|
||||||
|
.inner
|
||||||
|
%ul.list
|
||||||
|
- list.each do |comment|
|
||||||
|
%li
|
||||||
|
.left
|
||||||
|
= link_to comment.user.uname, user_path(comment.user.uname)
|
||||||
|
.item
|
||||||
|
= comment.body
|
||||||
|
%br
|
||||||
|
%br
|
||||||
|
- if commentable.class == Issue
|
||||||
|
- edit_path = edit_project_issue_comment_path(project, commentable, comment)
|
||||||
|
- delete_path = project_issue_comment_path(project, commentable, comment)
|
||||||
|
- elsif commentable.class == Grit::Commit
|
||||||
|
- edit_path = edit_project_commit_comment_path(project, commentable, comment)
|
||||||
|
- delete_path = project_commit_comment_path(project, commentable, comment)
|
||||||
|
= link_to t("layout.edit"), edit_path if can? :update, comment
|
||||||
|
= link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), delete_path, :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
|
||||||
|
|
||||||
|
.block
|
||||||
|
.content
|
||||||
|
%h2.title
|
||||||
|
= t("layout.comments.new_header")
|
||||||
|
.inner
|
||||||
|
- new_path = project_issue_comments_path(project, commentable) if commentable.class == Issue
|
||||||
|
- new_path = project_commit_comments_path(project, commentable) if commentable.class == Grit::Commit
|
||||||
|
= form_for :comment, :url => new_path, :method => :post, :html => { :class => :form } do |f|
|
||||||
|
= render :partial => "comments/form", :locals => {:f => f}
|
|
@ -1,10 +1,10 @@
|
||||||
.block
|
.block
|
||||||
.secondary-navigation
|
.secondary-navigation
|
||||||
%ul.wat-cf
|
%ul.wat-cf
|
||||||
%li.first= link_to t("layout.issues.list"), project_issue_path(@project, @issue)
|
%li.first= link_to t(@comment.commentable_type == 'Grit::Commit' ? "layout.git.repositories.commits" : "layout.issues.list"), @commentable_path
|
||||||
.content
|
.content
|
||||||
%h2.title
|
%h2.title
|
||||||
= t("layout.issues.edit_header")
|
= t("layout.#{@comment.commentable_type == 'Grit::Commit' ? 'comments' : 'issues'}.edit_header")
|
||||||
.inner
|
.inner
|
||||||
= form_for @comment, :url => project_issue_comment_path(@project, @issue, @comment), :html => { :class => :form } do |f|
|
= form_for @comment, :url => @update_url, :html => { :class => :form } do |f|
|
||||||
= render :partial => "form", :locals => {:f => f}
|
= render :partial => "form", :locals => {:f => f}
|
||||||
|
|
|
@ -26,3 +26,5 @@
|
||||||
%p= t 'layout.git.repositories.commit_diff_too_big'
|
%p= t 'layout.git.repositories.commit_diff_too_big'
|
||||||
|
|
||||||
- content_for :sidebar, render(:partial => 'git/shared/sidebar')
|
- content_for :sidebar, render(:partial => 'git/shared/sidebar')
|
||||||
|
|
||||||
|
= render :partial => "comments/list", :locals => {:list => Project.commit_comments(@commit, @project), :project => @project, :commentable => @commit}
|
||||||
|
|
|
@ -29,28 +29,4 @@
|
||||||
- else
|
- else
|
||||||
= link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post
|
= link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post
|
||||||
|
|
||||||
%a{ :name => "comments" }
|
= render :partial => "comments/list", :locals => {:list => @issue.comments.order(:created_at), :project => @project, :commentable => @issue}
|
||||||
.block#block-list
|
|
||||||
.content
|
|
||||||
%h2.title
|
|
||||||
= t("layout.issues.comments_header")
|
|
||||||
.inner
|
|
||||||
%ul.list
|
|
||||||
- @issue.comments.order(:created_at).each do |comment|
|
|
||||||
%li
|
|
||||||
.left
|
|
||||||
= link_to comment.user.uname, user_path(comment.user.uname)
|
|
||||||
.item
|
|
||||||
= comment.body
|
|
||||||
%br
|
|
||||||
%br
|
|
||||||
= link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue, comment) if can? :update, comment
|
|
||||||
= link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
|
|
||||||
|
|
||||||
.block
|
|
||||||
.content
|
|
||||||
%h2.title
|
|
||||||
= t("layout.comments.new_header")
|
|
||||||
.inner
|
|
||||||
= form_for :comment, :url => project_issue_comments_path(@project, @issue), :method => :post, :html => { :class => :form } do |f|
|
|
||||||
= render :partial => "comments/form", :locals => {:f => f}
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ ru:
|
||||||
comments:
|
comments:
|
||||||
confirm_delete: Вы уверены, что хотите удалить комментарий?
|
confirm_delete: Вы уверены, что хотите удалить комментарий?
|
||||||
new_header: Новый комментарий
|
new_header: Новый комментарий
|
||||||
|
edit_header: Редактирование комментария
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
admin_id: Владелец
|
admin_id: Владелец
|
||||||
|
|
|
@ -150,7 +150,11 @@ Rosa::Application.routes.draw do
|
||||||
# Commits
|
# Commits
|
||||||
match '/projects/:project_id/git/commits/:treeish(/*path)', :controller => "git/commits", :action => :index, :treeish => /[0-9a-zA-Z_.\-]*/, :defaults => { :treeish => :master }, :as => :commits
|
match '/projects/:project_id/git/commits/:treeish(/*path)', :controller => "git/commits", :action => :index, :treeish => /[0-9a-zA-Z_.\-]*/, :defaults => { :treeish => :master }, :as => :commits
|
||||||
match '/projects/:project_id/git/commit/:id(.:format)', :controller => "git/commits", :action => :show, :defaults => { :format => :html }, :as => :commit
|
match '/projects/:project_id/git/commit/:id(.:format)', :controller => "git/commits", :action => :show, :defaults => { :format => :html }, :as => :commit
|
||||||
|
# Commit Comments
|
||||||
|
match '/projects/:project_id/git/commit/:commit_id/comments/:id(.:format)', :controller => "comments", :action => :edit, :as => :edit_project_commit_comment, :via => :get
|
||||||
|
match '/projects/:project_id/git/commit/:commit_id/comments/:id(.:format)', :controller => "comments", :action => :update, :as => :project_commit_comment, :via => :put
|
||||||
|
match '/projects/:project_id/git/commit/:commit_id/comments/:id(.:format)', :controller => "comments", :action => :destroy, :via => :delete
|
||||||
|
match '/projects/:project_id/git/commit/:commit_id/comments(.:format)', :controller => "comments", :action => :create, :as => :project_commit_comments, :via => :post
|
||||||
# Blobs
|
# Blobs
|
||||||
match '/projects/:project_id/git/blob/:treeish/*path', :controller => "git/blobs", :action => :show, :treeish => /[0-9a-zA-Z_.\-]*/, :defaults => { :treeish => :master }, :as => :blob
|
match '/projects/:project_id/git/blob/:treeish/*path', :controller => "git/blobs", :action => :show, :treeish => /[0-9a-zA-Z_.\-]*/, :defaults => { :treeish => :master }, :as => :blob
|
||||||
match '/projects/:project_id/git/commit/blob/:commit_hash/*path', :controller => "git/blobs", :action => :show, :project_name => /[0-9a-zA-Z_.\-]*/, :as => :blob_commit
|
match '/projects/:project_id/git/commit/blob/:commit_hash/*path', :controller => "git/blobs", :action => :show, :project_name => /[0-9a-zA-Z_.\-]*/, :as => :blob_commit
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class ChangeCommentableId < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
change_column :comments, :commentable_id, :string
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
change_column :comments, :commentable_id, :integer
|
||||||
|
end
|
||||||
|
end
|
17
db/schema.rb
17
db/schema.rb
|
@ -89,7 +89,7 @@ ActiveRecord::Schema.define(:version => 20120117110723) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "comments", :force => true do |t|
|
create_table "comments", :force => true do |t|
|
||||||
t.integer "commentable_id"
|
t.string "commentable_id"
|
||||||
t.string "commentable_type"
|
t.string "commentable_type"
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.text "body"
|
t.text "body"
|
||||||
|
@ -167,6 +167,13 @@ ActiveRecord::Schema.define(:version => 20120117110723) do
|
||||||
|
|
||||||
add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true
|
add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true
|
||||||
|
|
||||||
|
create_table "permissions", :force => true do |t|
|
||||||
|
t.integer "right_id"
|
||||||
|
t.integer "role_id"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "platforms", :force => true do |t|
|
create_table "platforms", :force => true do |t|
|
||||||
t.string "description"
|
t.string "description"
|
||||||
t.string "name"
|
t.string "name"
|
||||||
|
@ -263,6 +270,14 @@ ActiveRecord::Schema.define(:version => 20120117110723) do
|
||||||
t.string "owner_type"
|
t.string "owner_type"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "rights", :force => true do |t|
|
||||||
|
t.string "name", :null => false
|
||||||
|
t.string "controller", :null => false
|
||||||
|
t.string "action", :null => false
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "rpms", :force => true do |t|
|
create_table "rpms", :force => true do |t|
|
||||||
t.string "name", :null => false
|
t.string "name", :null => false
|
||||||
t.integer "arch_id", :null => false
|
t.integer "arch_id", :null => false
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
shared_examples_for 'user with create comment rights for commits' do
|
||||||
|
it 'should be able to perform create action' do
|
||||||
|
post :create, @create_params
|
||||||
|
response.should redirect_to(commit_path(@project, @commit.id))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create subscribe object into db' do
|
||||||
|
lambda{ post :create, @create_params }.should change{ Comment.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for 'user with update own comment rights for commits' do
|
||||||
|
it 'should be able to perform update action' do
|
||||||
|
put :update, {:id => @own_comment.id}.merge(@update_params)
|
||||||
|
response.should redirect_to(commit_path(@project, @commit.id))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update subscribe body' do
|
||||||
|
put :update, {:id => @own_comment.id}.merge(@update_params)
|
||||||
|
@own_comment.reload.body.should == 'updated'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for 'user with update stranger comment rights for commits' do
|
||||||
|
it 'should be able to perform update action' do
|
||||||
|
put :update, {:id => @comment.id}.merge(@update_params)
|
||||||
|
response.should redirect_to(commit_path(@project, @commit.id))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update comment title' do
|
||||||
|
put :update, {:id => @comment.id}.merge(@update_params)
|
||||||
|
@comment.reload.body.should == 'updated'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for 'user without update stranger comment rights for commits' do
|
||||||
|
it 'should not be able to perform update action' do
|
||||||
|
put :update, {:id => @comment.id}.merge(@update_params)
|
||||||
|
response.should redirect_to(forbidden_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not update comment title' do
|
||||||
|
put :update, {:id => @comment.id}.merge(@update_params)
|
||||||
|
@comment.reload.body.should_not == 'updated'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for 'user without destroy comment rights for commits' do
|
||||||
|
it 'should not be able to perform destroy action' do
|
||||||
|
delete :destroy, :id => @comment.id, :commit_id => @commit.id, :project_id => @project.id
|
||||||
|
response.should redirect_to(forbidden_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not reduce comments count' do
|
||||||
|
lambda{ delete :destroy, :id => @comment.id, :commit_id => @commit.id, :project_id => @project.id }.should change{ Comment.count }.by(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#shared_examples_for 'user with destroy rights' do
|
||||||
|
# it 'should be able to perform destroy action' do
|
||||||
|
# delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :project_id => @project.id
|
||||||
|
# response.should redirect_to([@project, @issue])
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# it 'should reduce comments count' do
|
||||||
|
# lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :project_id => @project.id }.should change{ Comment.count }.by(-1)
|
||||||
|
# end
|
||||||
|
#end
|
||||||
|
|
||||||
|
describe CommentsController do
|
||||||
|
before(:each) do
|
||||||
|
stub_rsync_methods
|
||||||
|
|
||||||
|
@project = Factory(:project)
|
||||||
|
%x(cp -Rf #{Rails.root}/spec/tests.git/* #{@project.git_repository.path}) # maybe FIXME ?
|
||||||
|
@commit = @project.git_repository.commits.first
|
||||||
|
|
||||||
|
@comment = Factory(:comment)
|
||||||
|
@comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
@create_params = {:comment => {:body => 'I am a comment!'}, :project_id => @project.id, :commit_id => @commit.id}
|
||||||
|
@update_params = {:comment => {:body => 'updated'}, :project_id => @project.id, :commit_id => @commit.id}
|
||||||
|
|
||||||
|
any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
|
||||||
|
|
||||||
|
@request.env['HTTP_REFERER'] = commit_path(@project, @commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project admin user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
set_session_for(@user)
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin')
|
||||||
|
@own_comment = Factory(:comment, :user => @user)
|
||||||
|
@own_comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'user with create comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update stranger comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update own comment rights for commits'
|
||||||
|
it_should_behave_like 'user without destroy comment rights for commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project owner user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = @project.owner
|
||||||
|
set_session_for(@user)
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin')
|
||||||
|
|
||||||
|
@own_comment = Factory(:comment, :user => @user)
|
||||||
|
@own_comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'user with create comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update stranger comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update own comment rights for commits'
|
||||||
|
it_should_behave_like 'user without destroy comment rights for commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project reader user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
set_session_for(@user)
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'reader')
|
||||||
|
|
||||||
|
@own_comment = Factory(:comment, :user => @user)
|
||||||
|
@own_comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'user with create comment rights for commits'
|
||||||
|
it_should_behave_like 'user without update stranger comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update own comment rights for commits'
|
||||||
|
it_should_behave_like 'user without destroy comment rights for commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project writer user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
set_session_for(@user)
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer')
|
||||||
|
|
||||||
|
@own_comment = Factory(:comment, :user => @user)
|
||||||
|
@own_comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'user with create comment rights for commits'
|
||||||
|
it_should_behave_like 'user without update stranger comment rights for commits'
|
||||||
|
it_should_behave_like 'user with update own comment rights for commits'
|
||||||
|
it_should_behave_like 'user without destroy comment rights for commits'
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,12 +49,12 @@ end
|
||||||
|
|
||||||
shared_examples_for 'user without destroy comment rights' do
|
shared_examples_for 'user without destroy comment rights' do
|
||||||
it 'should not be able to perform destroy action' do
|
it 'should not be able to perform destroy action' do
|
||||||
delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id
|
delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :project_id => @project.id
|
||||||
response.should redirect_to(forbidden_path)
|
response.should redirect_to(forbidden_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not reduce comments count' do
|
it 'should not reduce comments count' do
|
||||||
lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(0)
|
lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :project_id => @project.id }.should change{ Issue.count }.by(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require "cancan/matchers"
|
||||||
|
|
||||||
|
def set_comments_data_for_commit
|
||||||
|
@ability = Ability.new(@user)
|
||||||
|
|
||||||
|
@project = Factory(:project)
|
||||||
|
%x(cp -Rf #{Rails.root}/spec/tests.git/* #{@project.git_repository.path}) # maybe FIXME ?
|
||||||
|
@commit = @project.git_repository.commits.first
|
||||||
|
|
||||||
|
@comment = Factory(:comment, :user => @user)
|
||||||
|
@comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id)
|
||||||
|
|
||||||
|
@stranger_comment = Factory(:comment, :user => @stranger)
|
||||||
|
@stranger_comment.update_attributes(:commentable_type => @commit.class.name, :commentable_id => @commit.id, :project => @project)
|
||||||
|
|
||||||
|
@create_params = {:commentable_type => @commit.class.name, :commentable_id => @commit.id, :user => @user, :project => @project}
|
||||||
|
|
||||||
|
any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Comment do
|
||||||
|
context 'for global admin user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:admin)
|
||||||
|
@stranger = Factory(:user)
|
||||||
|
|
||||||
|
set_comments_data_for_commit
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create comment' do
|
||||||
|
@ability.should be_able_to(:create, Comment.new(@create_params))
|
||||||
|
end
|
||||||
|
|
||||||
|
pending "sends an e-mail" do
|
||||||
|
ActionMailer::Base.deliveries.last.to.include?(@stranger.email).should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update comment' do
|
||||||
|
@ability.should be_able_to(:update, @comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update stranger comment' do
|
||||||
|
@ability.should be_able_to(:update, @stranger_comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should destroy own comment' do
|
||||||
|
@ability.should be_able_to(:destroy, @comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should destroy stranger comment' do
|
||||||
|
@ability.should be_able_to(:destroy, @stranger_comment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project admin user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
@stranger = Factory(:user)
|
||||||
|
|
||||||
|
set_comments_data_for_commit
|
||||||
|
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create comment' do
|
||||||
|
@ability.should be_able_to(:create, Comment.new(@create_params))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update comment' do
|
||||||
|
@ability.should be_able_to(:update, @comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update stranger comment' do
|
||||||
|
@ability.should be_able_to(:update, @stranger_comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not destroy comment' do
|
||||||
|
@ability.should_not be_able_to(:destroy, @comment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for project owner user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
@stranger = Factory(:user)
|
||||||
|
|
||||||
|
set_comments_data_for_commit
|
||||||
|
|
||||||
|
@project.update_attribute(:owner, @user)
|
||||||
|
@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create comment' do
|
||||||
|
@ability.should be_able_to(:create, Comment.new(@create_params))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update comment' do
|
||||||
|
@ability.should be_able_to(:update, @comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update stranger comment' do
|
||||||
|
@ability.should be_able_to(:update, @stranger_comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not destroy comment' do
|
||||||
|
@ability.should_not be_able_to(:destroy, @comment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for simple user' do
|
||||||
|
before(:each) do
|
||||||
|
@user = Factory(:user)
|
||||||
|
@stranger = Factory(:user)
|
||||||
|
|
||||||
|
set_comments_data_for_commit
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create comment' do
|
||||||
|
@ability.should be_able_to(:create, Comment.new(@create_params))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should update comment' do
|
||||||
|
@ability.should be_able_to(:update, @comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not update stranger comment' do
|
||||||
|
@ability.should_not be_able_to(:update, @stranger_comment)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not destroy comment' do
|
||||||
|
@ability.should_not be_able_to(:destroy, @comment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
|
|
@ -0,0 +1,11 @@
|
||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = false
|
||||||
|
logallrefupdates = true
|
||||||
|
[remote "origin"]
|
||||||
|
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||||
|
url = git://github.com/tpope/vim-ragtag.git
|
||||||
|
[branch "master"]
|
||||||
|
remote = origin
|
||||||
|
merge = refs/heads/master
|
|
@ -0,0 +1 @@
|
||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message taken by
|
||||||
|
# applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit. The hook is
|
||||||
|
# allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "applypatch-msg".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||||
|
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||||
|
:
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to check the commit log message.
|
||||||
|
# Called by "git commit" with one argument, the name of the file
|
||||||
|
# that has the commit message. The hook should exit with non-zero
|
||||||
|
# status after issuing an appropriate message if it wants to stop the
|
||||||
|
# commit. The hook is allowed to edit the commit message file.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "commit-msg".
|
||||||
|
|
||||||
|
# Uncomment the below to add a Signed-off-by line to the message.
|
||||||
|
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||||
|
# hook is more suited to it.
|
||||||
|
#
|
||||||
|
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||||
|
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||||
|
|
||||||
|
# This example catches duplicate Signed-off-by lines.
|
||||||
|
|
||||||
|
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||||
|
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||||
|
echo >&2 Duplicate Signed-off-by lines.
|
||||||
|
exit 1
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script that is called after a successful
|
||||||
|
# commit is made.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "post-commit".
|
||||||
|
|
||||||
|
: Nothing
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script for the "post-receive" event.
|
||||||
|
#
|
||||||
|
# The "post-receive" script is run after receive-pack has accepted a pack
|
||||||
|
# and the repository has been updated. It is passed arguments in through
|
||||||
|
# stdin in the form
|
||||||
|
# <oldrev> <newrev> <refname>
|
||||||
|
# For example:
|
||||||
|
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
|
||||||
|
#
|
||||||
|
# see contrib/hooks/ for a sample, or uncomment the next line and
|
||||||
|
# rename the file to "post-receive".
|
||||||
|
|
||||||
|
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to prepare a packed repository for use over
|
||||||
|
# dumb transports.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "post-update".
|
||||||
|
|
||||||
|
exec git update-server-info
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to verify what is about to be committed
|
||||||
|
# by applypatch from an e-mail message.
|
||||||
|
#
|
||||||
|
# The hook should exit with non-zero status after issuing an
|
||||||
|
# appropriate message if it wants to stop the commit.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-applypatch".
|
||||||
|
|
||||||
|
. git-sh-setup
|
||||||
|
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||||
|
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
||||||
|
:
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to verify what is about to be committed.
|
||||||
|
# Called by "git commit" with no arguments. The hook should
|
||||||
|
# exit with non-zero status after issuing an appropriate message if
|
||||||
|
# it wants to stop the commit.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "pre-commit".
|
||||||
|
|
||||||
|
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
against=HEAD
|
||||||
|
else
|
||||||
|
# Initial commit: diff against an empty tree object
|
||||||
|
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If you want to allow non-ascii filenames set this variable to true.
|
||||||
|
allownonascii=$(git config hooks.allownonascii)
|
||||||
|
|
||||||
|
# Cross platform projects tend to avoid non-ascii filenames; prevent
|
||||||
|
# them from being added to the repository. We exploit the fact that the
|
||||||
|
# printable range starts at the space character and ends with tilde.
|
||||||
|
if [ "$allownonascii" != "true" ] &&
|
||||||
|
# Note that the use of brackets around a tr range is ok here, (it's
|
||||||
|
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||||
|
# the square bracket bytes happen to fall in the designated range.
|
||||||
|
test "$(git diff --cached --name-only --diff-filter=A -z $against |
|
||||||
|
LC_ALL=C tr -d '[ -~]\0')"
|
||||||
|
then
|
||||||
|
echo "Error: Attempt to add a non-ascii file name."
|
||||||
|
echo
|
||||||
|
echo "This can cause problems if you want to work"
|
||||||
|
echo "with people on other platforms."
|
||||||
|
echo
|
||||||
|
echo "To be portable it is advisable to rename the file ..."
|
||||||
|
echo
|
||||||
|
echo "If you know what you are doing you can disable this"
|
||||||
|
echo "check using:"
|
||||||
|
echo
|
||||||
|
echo " git config hooks.allownonascii true"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec git diff-index --check --cached $against --
|
|
@ -0,0 +1,172 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||||
|
#
|
||||||
|
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||||
|
# its job, and can prevent the command from running by exiting with
|
||||||
|
# non-zero status.
|
||||||
|
#
|
||||||
|
# The hook is called with the following parameters:
|
||||||
|
#
|
||||||
|
# $1 -- the upstream the series was forked from.
|
||||||
|
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||||
|
#
|
||||||
|
# This sample shows how to prevent topic branches that are already
|
||||||
|
# merged to 'next' branch from getting rebased, because allowing it
|
||||||
|
# would result in rebasing already published history.
|
||||||
|
|
||||||
|
publish=next
|
||||||
|
basebranch="$1"
|
||||||
|
if test "$#" = 2
|
||||||
|
then
|
||||||
|
topic="refs/heads/$2"
|
||||||
|
else
|
||||||
|
topic=`git symbolic-ref HEAD` ||
|
||||||
|
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$topic" in
|
||||||
|
refs/heads/??/*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0 ;# we do not interrupt others.
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Now we are dealing with a topic branch being rebased
|
||||||
|
# on top of master. Is it OK to rebase it?
|
||||||
|
|
||||||
|
# Does the topic really exist?
|
||||||
|
git show-ref -q "$topic" || {
|
||||||
|
echo >&2 "No such branch $topic"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Is topic fully merged to master?
|
||||||
|
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||||
|
if test -z "$not_in_master"
|
||||||
|
then
|
||||||
|
echo >&2 "$topic is fully merged to master; better remove it."
|
||||||
|
exit 1 ;# we could allow it, but there is no point.
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||||
|
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||||
|
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||||
|
if test "$only_next_1" = "$only_next_2"
|
||||||
|
then
|
||||||
|
not_in_topic=`git rev-list "^$topic" master`
|
||||||
|
if test -z "$not_in_topic"
|
||||||
|
then
|
||||||
|
echo >&2 "$topic is already up-to-date with master"
|
||||||
|
exit 1 ;# we could allow it, but there is no point.
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||||
|
/usr/bin/perl -e '
|
||||||
|
my $topic = $ARGV[0];
|
||||||
|
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||||
|
my (%not_in_next) = map {
|
||||||
|
/^([0-9a-f]+) /;
|
||||||
|
($1 => 1);
|
||||||
|
} split(/\n/, $ARGV[1]);
|
||||||
|
for my $elem (map {
|
||||||
|
/^([0-9a-f]+) (.*)$/;
|
||||||
|
[$1 => $2];
|
||||||
|
} split(/\n/, $ARGV[2])) {
|
||||||
|
if (!exists $not_in_next{$elem->[0]}) {
|
||||||
|
if ($msg) {
|
||||||
|
print STDERR $msg;
|
||||||
|
undef $msg;
|
||||||
|
}
|
||||||
|
print STDERR " $elem->[1]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$topic" "$not_in_next" "$not_in_master"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
<<\DOC_END
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
This sample hook safeguards topic branches that have been
|
||||||
|
published from being rewound.
|
||||||
|
|
||||||
|
The workflow assumed here is:
|
||||||
|
|
||||||
|
* Once a topic branch forks from "master", "master" is never
|
||||||
|
merged into it again (either directly or indirectly).
|
||||||
|
|
||||||
|
* Once a topic branch is fully cooked and merged into "master",
|
||||||
|
it is deleted. If you need to build on top of it to correct
|
||||||
|
earlier mistakes, a new topic branch is created by forking at
|
||||||
|
the tip of the "master". This is not strictly necessary, but
|
||||||
|
it makes it easier to keep your history simple.
|
||||||
|
|
||||||
|
* Whenever you need to test or publish your changes to topic
|
||||||
|
branches, merge them into "next" branch.
|
||||||
|
|
||||||
|
The script, being an example, hardcodes the publish branch name
|
||||||
|
to be "next", but it is trivial to make it configurable via
|
||||||
|
$GIT_DIR/config mechanism.
|
||||||
|
|
||||||
|
With this workflow, you would want to know:
|
||||||
|
|
||||||
|
(1) ... if a topic branch has ever been merged to "next". Young
|
||||||
|
topic branches can have stupid mistakes you would rather
|
||||||
|
clean up before publishing, and things that have not been
|
||||||
|
merged into other branches can be easily rebased without
|
||||||
|
affecting other people. But once it is published, you would
|
||||||
|
not want to rewind it.
|
||||||
|
|
||||||
|
(2) ... if a topic branch has been fully merged to "master".
|
||||||
|
Then you can delete it. More importantly, you should not
|
||||||
|
build on top of it -- other people may already want to
|
||||||
|
change things related to the topic as patches against your
|
||||||
|
"master", so if you need further changes, it is better to
|
||||||
|
fork the topic (perhaps with the same name) afresh from the
|
||||||
|
tip of "master".
|
||||||
|
|
||||||
|
Let's look at this example:
|
||||||
|
|
||||||
|
o---o---o---o---o---o---o---o---o---o "next"
|
||||||
|
/ / / /
|
||||||
|
/ a---a---b A / /
|
||||||
|
/ / / /
|
||||||
|
/ / c---c---c---c B /
|
||||||
|
/ / / \ /
|
||||||
|
/ / / b---b C \ /
|
||||||
|
/ / / / \ /
|
||||||
|
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||||
|
|
||||||
|
|
||||||
|
A, B and C are topic branches.
|
||||||
|
|
||||||
|
* A has one fix since it was merged up to "next".
|
||||||
|
|
||||||
|
* B has finished. It has been fully merged up to "master" and "next",
|
||||||
|
and is ready to be deleted.
|
||||||
|
|
||||||
|
* C has not merged to "next" at all.
|
||||||
|
|
||||||
|
We would want to allow C to be rebased, refuse A, and encourage
|
||||||
|
B to be deleted.
|
||||||
|
|
||||||
|
To compute (1):
|
||||||
|
|
||||||
|
git rev-list ^master ^topic next
|
||||||
|
git rev-list ^master next
|
||||||
|
|
||||||
|
if these match, topic has not merged in next at all.
|
||||||
|
|
||||||
|
To compute (2):
|
||||||
|
|
||||||
|
git rev-list master..topic
|
||||||
|
|
||||||
|
if this is empty, it is fully merged to "master".
|
||||||
|
|
||||||
|
DOC_END
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to prepare the commit log message.
|
||||||
|
# Called by "git commit" with the name of the file that has the
|
||||||
|
# commit message, followed by the description of the commit
|
||||||
|
# message's source. The hook's purpose is to edit the commit
|
||||||
|
# message file. If the hook fails with a non-zero status,
|
||||||
|
# the commit is aborted.
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||||
|
|
||||||
|
# This hook includes three examples. The first comments out the
|
||||||
|
# "Conflicts:" part of a merge commit.
|
||||||
|
#
|
||||||
|
# The second includes the output of "git diff --name-status -r"
|
||||||
|
# into the message, just before the "git status" output. It is
|
||||||
|
# commented because it doesn't cope with --amend or with squashed
|
||||||
|
# commits.
|
||||||
|
#
|
||||||
|
# The third example adds a Signed-off-by line to the message, that can
|
||||||
|
# still be edited. This is rarely a good idea.
|
||||||
|
|
||||||
|
case "$2,$3" in
|
||||||
|
merge,)
|
||||||
|
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
|
||||||
|
|
||||||
|
# ,|template,)
|
||||||
|
# /usr/bin/perl -i.bak -pe '
|
||||||
|
# print "\n" . `git diff --cached --name-status -r`
|
||||||
|
# if /^#/ && $first++ == 0' "$1" ;;
|
||||||
|
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||||
|
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
|
@ -0,0 +1,128 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# An example hook script to blocks unannotated tags from entering.
|
||||||
|
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||||
|
#
|
||||||
|
# To enable this hook, rename this file to "update".
|
||||||
|
#
|
||||||
|
# Config
|
||||||
|
# ------
|
||||||
|
# hooks.allowunannotated
|
||||||
|
# This boolean sets whether unannotated tags will be allowed into the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.allowdeletetag
|
||||||
|
# This boolean sets whether deleting tags will be allowed in the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.allowmodifytag
|
||||||
|
# This boolean sets whether a tag may be modified after creation. By default
|
||||||
|
# it won't be.
|
||||||
|
# hooks.allowdeletebranch
|
||||||
|
# This boolean sets whether deleting branches will be allowed in the
|
||||||
|
# repository. By default they won't be.
|
||||||
|
# hooks.denycreatebranch
|
||||||
|
# This boolean sets whether remotely creating branches will be denied
|
||||||
|
# in the repository. By default this is allowed.
|
||||||
|
#
|
||||||
|
|
||||||
|
# --- Command line
|
||||||
|
refname="$1"
|
||||||
|
oldrev="$2"
|
||||||
|
newrev="$3"
|
||||||
|
|
||||||
|
# --- Safety check
|
||||||
|
if [ -z "$GIT_DIR" ]; then
|
||||||
|
echo "Don't run this script from the command line." >&2
|
||||||
|
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||||
|
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||||
|
echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Config
|
||||||
|
allowunannotated=$(git config --bool hooks.allowunannotated)
|
||||||
|
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
|
||||||
|
denycreatebranch=$(git config --bool hooks.denycreatebranch)
|
||||||
|
allowdeletetag=$(git config --bool hooks.allowdeletetag)
|
||||||
|
allowmodifytag=$(git config --bool hooks.allowmodifytag)
|
||||||
|
|
||||||
|
# check for no description
|
||||||
|
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||||
|
case "$projectdesc" in
|
||||||
|
"Unnamed repository"* | "")
|
||||||
|
echo "*** Project description file hasn't been set" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Check types
|
||||||
|
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||||
|
zero="0000000000000000000000000000000000000000"
|
||||||
|
if [ "$newrev" = "$zero" ]; then
|
||||||
|
newrev_type=delete
|
||||||
|
else
|
||||||
|
newrev_type=$(git cat-file -t $newrev)
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$refname","$newrev_type" in
|
||||||
|
refs/tags/*,commit)
|
||||||
|
# un-annotated tag
|
||||||
|
short_refname=${refname##refs/tags/}
|
||||||
|
if [ "$allowunannotated" != "true" ]; then
|
||||||
|
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||||
|
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/tags/*,delete)
|
||||||
|
# delete tag
|
||||||
|
if [ "$allowdeletetag" != "true" ]; then
|
||||||
|
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/tags/*,tag)
|
||||||
|
# annotated tag
|
||||||
|
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||||
|
then
|
||||||
|
echo "*** Tag '$refname' already exists." >&2
|
||||||
|
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/heads/*,commit)
|
||||||
|
# branch
|
||||||
|
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||||
|
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/heads/*,delete)
|
||||||
|
# delete branch
|
||||||
|
if [ "$allowdeletebranch" != "true" ]; then
|
||||||
|
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
refs/remotes/*,commit)
|
||||||
|
# tracking branch
|
||||||
|
;;
|
||||||
|
refs/remotes/*,delete)
|
||||||
|
# delete tracking branch
|
||||||
|
if [ "$allowdeletebranch" != "true" ]; then
|
||||||
|
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Anything else (is there anything else?)
|
||||||
|
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Finished
|
||||||
|
exit 0
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
|
@ -0,0 +1,2 @@
|
||||||
|
0000000000000000000000000000000000000000 bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander <avm@avm-work.(none)> 1325695134 +0600 clone: from git://github.com/tpope/vim-ragtag.git
|
||||||
|
bdc8b580b5b583aeb43efb19aac2ab8ce5566dff bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander <avm@avm-work.(none)> 1325695134 +0600 checkout: moving from master to bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
|
|
@ -0,0 +1 @@
|
||||||
|
0000000000000000000000000000000000000000 bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander <avm@avm-work.(none)> 1325695134 +0600 clone: from git://github.com/tpope/vim-ragtag.git
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
# pack-refs with: peeled
|
||||||
|
30aefeac002db3ec08ff278bd76290645469611e refs/tags/v2.0
|
||||||
|
^644c62ad7bc7d9a4a5f19f5e8c41ef910782178b
|
||||||
|
235e4467107467feacc50553bbeda15e9bf99f57 refs/tags/v1.11
|
||||||
|
bdc8b580b5b583aeb43efb19aac2ab8ce5566dff refs/remotes/origin/master
|
|
@ -0,0 +1 @@
|
||||||
|
bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
|
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/remotes/origin/master
|
Loading…
Reference in New Issue