diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b3142feb494..85fb8e785ac 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -11,6 +11,7 @@ //= require jquery.tagify //= require jquery.cookie //= require jquery.endless-scroll +//= require jquery.highlight //= require bootstrap-modal //= require modernizr //= require chosen diff --git a/app/assets/javascripts/merge_requests.js b/app/assets/javascripts/merge_requests.js index 1c262afc641..96bc1ccbe0b 100644 --- a/app/assets/javascripts/merge_requests.js +++ b/app/assets/javascripts/merge_requests.js @@ -11,7 +11,7 @@ var MergeRequest = { $(".tabs a.merge-notes-tab").live("click", function(e) { $(".merge-request-diffs").hide(); - $(".merge-request-notes").show(); + $(".merge_request_notes").show(); e.preventDefault(); }); @@ -19,7 +19,7 @@ var MergeRequest = { if(!MergeRequest.diffs_loaded) { MergeRequest.loadDiff(); } - $(".merge-request-notes").hide(); + $(".merge_request_notes").hide(); $(".merge-request-diffs").show(); e.preventDefault(); }); @@ -33,7 +33,7 @@ var MergeRequest = { url: $(".merge-diffs-tab").attr("data-url"), complete: function(){ MergeRequest.diffs_loaded = true; - $(".merge-request-notes").hide(); + $(".merge_request_notes").hide(); $(".dashboard-loader").hide()}, dataType: "script"}); } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 810e747cb2c..0edd63d3b47 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -3,7 +3,7 @@ a { color: $link_color; &:hover { text-decoration:none; - color: $style_color; + color: $blue_link; } &.btn { @@ -11,6 +11,18 @@ a { } } +.btn { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f1f1f1), color-stop(25%, #f1f1f1), to(#e6e6e6)); + background-image: -webkit-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); + background-image: -moz-linear-gradient(top, #f1f1f1, #f1f1f1 25%, #e6e6e6); + background-image: -ms-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); + background-image: -o-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); + background-image: linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); + + &:hover { + } +} + a:focus { outline: none; } @@ -29,6 +41,29 @@ a:focus { .label { background-color: #474D57; + + &.pushed { + background-color: $link_color; + } +} + +.pretty_label { + @include round-borders-all(4px); + padding:2px 4px; + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8)); + background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8); + background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8); + background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8); + color: #777; + border: 1px solid #DEDFE1; + + &.branch { + border:none; + font-size:13px; + background: #474D57; + color:#fff; + font-family: monospace; + } } .tabs > li > a, .pills > li > a { @@ -807,12 +842,19 @@ p.time { width:840px; margin:auto; - .wll { - padding:5px; - margin-top:5px; + .dash_project_item { + margin-bottom:10px; border:none; &:hover { background:none; + + h4 { + color:#2FA0BB; + .arrow { + background:#2FA0BB; + color:#fff; + } + } } h4 { @@ -887,7 +929,7 @@ p.time { } a:last-child h4 { border:none; } - a.active { + a:hover { h4 { color:#111; border-right:4px solid $styled_border_color; @@ -974,3 +1016,32 @@ p.time { } } } + +.highlight_word { + background:#EEDC94; +} + +.status_info { + font-size:14px; + padding:5px 15px; + line-height:24px; + width:60px; + text-align:center; + float:left; + margin-right:20px; +} + +.merge_request_status_holder { + margin-bottom:20px; +} + +.arrow{ + float: right; + background: #E3E5EA; + padding: 10px; + border-radius: 5px; + text-shadow: none; + color: #999; + line-height: 16px; + font-weight:bold; +} diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 5881880982a..10388bcf223 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -15,7 +15,7 @@ $app_padding:20px; $bg_color: #FFF; $styled_border_color: #2FA0BB; $color: "#4BB8D2"; -$blue_link: "#2fa0bb"; +$blue_link: #2fa0bb; /** Style colors **/ diff --git a/app/assets/stylesheets/ui_basic.scss b/app/assets/stylesheets/ui_basic.scss index 64347525099..a1e6133d4f3 100644 --- a/app/assets/stylesheets/ui_basic.scss +++ b/app/assets/stylesheets/ui_basic.scss @@ -17,7 +17,7 @@ color: $link_color; &:hover { text-decoration:none; - color: $style_color; + color: $blue_link; } } diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 8fa4129812e..f6b09e23430 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -18,7 +18,7 @@ class DashboardController < ApplicationController # Get authored or assigned open merge requests def merge_requests @projects = current_user.projects.all - @merge_requests = MergeRequest.where("author_id = :id or assignee_id = :id", :id => current_user.id).opened.order("created_at DESC").limit(40) + @merge_requests = current_user.cared_merge_requests.order("created_at DESC").limit(40) end # Get only assigned issues diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index d1d19efc98c..3a3143cb15f 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -41,13 +41,9 @@ class MergeRequestsController < ApplicationController @note = @project.notes.new(:noteable => @merge_request) - @commits = @project.repo. - commits_between(@merge_request.target_branch, @merge_request.source_branch). - map {|c| Commit.new(c)}. - sort_by(&:created_at). - reverse - - render_full_content + # Get commits from repository + # or from cache if already merged + @commits = @merge_request.commits respond_to do |format| format.html @@ -76,6 +72,7 @@ class MergeRequestsController < ApplicationController respond_to do |format| if @merge_request.save + @merge_request.reload_code format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' } format.json { render json: @merge_request, status: :created, location: @merge_request } else @@ -88,6 +85,7 @@ class MergeRequestsController < ApplicationController def update respond_to do |format| if @merge_request.update_attributes(params[:merge_request].merge(:author_id_of_changes => current_user.id)) + @merge_request.reload_code format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' } format.json { head :ok } else diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 90372cf5c8b..459beec9a73 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -13,6 +13,7 @@ class ProjectsController < ApplicationController def index @projects = current_user.projects @projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse + @events = Event.where(:project_id => @projects.map(&:id)).recent.limit(40) end def new @@ -78,7 +79,6 @@ class ProjectsController < ApplicationController render "projects/empty" end end - format.js end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb new file mode 100644 index 00000000000..8a452fe0914 --- /dev/null +++ b/app/controllers/search_controller.rb @@ -0,0 +1,12 @@ +class SearchController < ApplicationController + def show + query = params[:search] + if query.blank? + @projects = [] + @merge_requests = [] + else + @projects = Project.search(query).limit(10) + @merge_requests = MergeRequest.search(query).limit(10) + end + end +end diff --git a/app/models/event.rb b/app/models/event.rb index 8ab127c02a0..c8af9363133 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -7,6 +7,7 @@ class Event < ActiveRecord::Base Reopened = 4 Pushed = 5 Commented = 6 + Merged = 7 belongs_to :project belongs_to :target, :polymorphic => true diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 9bbbcd472aa..edf073d4e88 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,9 +1,14 @@ +require File.join(Rails.root, "app/models/commit") + class MergeRequest < ActiveRecord::Base belongs_to :project belongs_to :author, :class_name => "User" belongs_to :assignee, :class_name => "User" has_many :notes, :as => :noteable, :dependent => :destroy + serialize :st_commits + serialize :st_diffs + attr_protected :author, :author_id, :project, :project_id attr_accessor :author_id_of_changes @@ -32,6 +37,13 @@ class MergeRequest < ActiveRecord::Base scope :closed, where(:closed => true) scope :assigned, lambda { |u| where(:assignee_id => u.id)} + def self.search query + where("title like :query", :query => "%#{query}%") + end + + def self.find_all_by_branch(branch_name) + where("source_branch like :branch or target_branch like :branch", :branch => branch_name) + end def validate_branches if target_branch == source_branch @@ -39,23 +51,99 @@ class MergeRequest < ActiveRecord::Base end end + def reload_code + self.reloaded_commits + self.reloaded_diffs + end + def new? today? && created_at == updated_at end def diffs + st_diffs || [] + end + + def reloaded_diffs + if open? && unmerged_diffs.any? + self.st_diffs = unmerged_diffs + save + end + diffs + end + + def unmerged_diffs commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] end def last_commit - project.commit(source_branch) + commits.first end + def merged? + merged && merge_event + end + + def merge_event + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Merged).last + end + + def closed_event + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Closed).last + end + + # Return the number of +1 comments (upvotes) def upvotes notes.select(&:upvote?).size end + + def commits + st_commits || [] + end + + def probably_merged? + unmerged_commits.empty? && + commits.any? && open? + end + + def open? + !closed + end + + def mark_as_merged! + self.merged = true + self.closed = true + save + end + + def reloaded_commits + if open? && unmerged_commits.any? + self.st_commits = unmerged_commits + save + end + commits + end + + def unmerged_commits + self.project.repo. + commits_between(self.target_branch, self.source_branch). + map {|c| Commit.new(c)}. + sort_by(&:created_at). + reverse + end + + def merge!(user_id) + self.mark_as_merged! + Event.create( + :project => self.project, + :action => Event::Merged, + :target_id => self.id, + :target_type => "MergeRequest", + :author_id => user_id + ) + end end # == Schema Information # diff --git a/app/models/project.rb b/app/models/project.rb index 2ba0c9077bc..8811176170e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -54,6 +54,10 @@ class Project < ActiveRecord::Base UsersProject.access_roles end + def self.search query + where("name like :query or code like :query or path like :query", :query => "%#{query}%") + end + def to_param code end @@ -73,6 +77,24 @@ class Project < ActiveRecord::Base ) end + def update_merge_requests(oldrev, newrev, ref, author_key_id) + return true unless ref =~ /heads/ + branch_name = ref.gsub("refs/heads/", "") + user = Key.find_by_identifier(author_key_id).user + c_ids = self.commits_between(oldrev, newrev).map(&:id) + + # Update code for merge requests + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all + mrs.each { |merge_request| merge_request.reload_code } + + # Close merge requests + mrs = self.merge_requests.opened.where(:target_branch => branch_name).all + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } + mrs.each { |merge_request| merge_request.merge!(user.id) } + + true + end + def execute_web_hooks(oldrev, newrev, ref, author_key_id) ref_parts = ref.split('/') diff --git a/app/models/user.rb b/app/models/user.rb index 660e5a21ad9..5caa7451bef 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,6 +86,10 @@ class User < ActiveRecord::Base ) end end + + def cared_merge_requests + MergeRequest.where("author_id = :id or assignee_id = :id", :id => self.id).opened + end end # == Schema Information # diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 9c1df54ea70..9e633072d2e 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -12,8 +12,8 @@ = @commit.committer_name %small= @commit.committed_date.stamp("Aug 21, 2011 9:23pm") -%hr -%pre.commit_message +%br +%pre.commit_message.prettyprint = commit_msg_with_link_to_issues(@project, @commit.safe_message) .clear %br diff --git a/app/views/dashboard/_events_feed.html.haml b/app/views/dashboard/_events_feed.html.haml deleted file mode 100644 index 976597531df..00000000000 --- a/app/views/dashboard/_events_feed.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -= render @events - diff --git a/app/views/dashboard/_issues_feed.html.haml b/app/views/dashboard/_issues_feed.html.haml index 5c0e05ef0a5..f867b3589cd 100644 --- a/app/views/dashboard/_issues_feed.html.haml +++ b/app/views/dashboard/_issues_feed.html.haml @@ -3,8 +3,10 @@ = link_to [issue.project, issue] do %p %strong - %span.label= issue.project.name + %span.pretty_label= issue.project.name – Issue # = issue.id = truncate issue.title, :length => 50 + %span.right.cgray + = issue.updated_at.stamp("Aug 21, 2011") diff --git a/app/views/dashboard/_merge_requests_feed.html.haml b/app/views/dashboard/_merge_requests_feed.html.haml index db1ebd32a63..7fb7998d537 100644 --- a/app/views/dashboard/_merge_requests_feed.html.haml +++ b/app/views/dashboard/_merge_requests_feed.html.haml @@ -3,8 +3,9 @@ = link_to [merge_request.project, merge_request] do %p %strong - %span.label= merge_request.project.name + %span.pretty_label= merge_request.project.name – - Merge Request # - = merge_request.id + Merge Request ##{merge_request.id} = truncate merge_request.title, :length => 50 + %span.right.cgray + = merge_request.updated_at.stamp("Aug 21, 2011") diff --git a/app/views/dashboard/_projects_feed.html.haml b/app/views/dashboard/_projects_feed.html.haml index c1abe556a88..61154147841 100644 --- a/app/views/dashboard/_projects_feed.html.haml +++ b/app/views/dashboard/_projects_feed.html.haml @@ -1,5 +1,5 @@ -- @active_projects.first(5).each do |project| - .wll +- projects.first(5).each do |project| + %div.dash_project_item = link_to project do %h4 %span.ico.project @@ -7,3 +7,5 @@ %small last activity at = project.last_activity_date.stamp("Aug 25, 2011") + %span.right.arrow + → diff --git a/app/views/dashboard/index.html.haml b/app/views/dashboard/index.html.haml index d3946f5b327..b20fafca75c 100644 --- a/app/views/dashboard/index.html.haml +++ b/app/views/dashboard/index.html.haml @@ -20,7 +20,7 @@ .row .dashboard_block .row - .span10= render "dashboard/projects_feed" + .span10= render "dashboard/projects_feed", :projects => @active_projects .span4.right - if current_user.can_create_project? .alert-message.block-message.warning @@ -65,4 +65,4 @@ %hr .row - .dashboard_block= render "dashboard/events_feed" + .dashboard_block= render @events diff --git a/app/views/events/_event_changed_issue.html.haml b/app/views/events/_event_changed_issue.html.haml index 65cfebbd76b..4f5d78af40e 100644 --- a/app/views/events/_event_changed_issue.html.haml +++ b/app/views/events/_event_changed_issue.html.haml @@ -1,10 +1,11 @@ = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -- if event.closed? - closed -- else - reopened -issue +%span.label.important + - if event.closed? + closed + - else + reopened + issue = link_to project_issue_path(event.project, event.issue) do %strong= truncate event.issue_title at diff --git a/app/views/events/_event_changed_merge_request.html.haml b/app/views/events/_event_changed_merge_request.html.haml index a55e609c2cf..ff91375e901 100644 --- a/app/views/events/_event_changed_merge_request.html.haml +++ b/app/views/events/_event_changed_merge_request.html.haml @@ -1,10 +1,11 @@ = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -- if event.closed? - closed -- else - reopened -merge request +%span.label.important + - if event.closed? + closed + - else + reopened + merge request = link_to project_merge_request_path(event.project, event.merge_request) do %strong= truncate event.merge_request_title at @@ -12,7 +13,6 @@ at %span.cgray = time_ago_in_words(event.created_at) ago. -%br %span.label= event.merge_request.source_branch → %span.label= event.merge_request.target_branch diff --git a/app/views/events/_event_new_issue.html.haml b/app/views/events/_event_new_issue.html.haml index 477ca48f746..3d38d844ca5 100644 --- a/app/views/events/_event_new_issue.html.haml +++ b/app/views/events/_event_new_issue.html.haml @@ -1,6 +1,7 @@ = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -created new issue +%span.label.success created + new issue = link_to project_issue_path(event.project, event.issue) do %strong= truncate event.issue_title at diff --git a/app/views/events/_event_new_merge_request.html.haml b/app/views/events/_event_new_merge_request.html.haml index e6895351106..e3d8ab2a62d 100644 --- a/app/views/events/_event_new_merge_request.html.haml +++ b/app/views/events/_event_new_merge_request.html.haml @@ -1,6 +1,7 @@ = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} -requested merge +%span.label.success requested + merge = link_to project_merge_request_path(event.project, event.merge_request) do %strong= truncate event.merge_request_title at @@ -8,7 +9,6 @@ at %span.cgray = time_ago_in_words(event.created_at) ago. -%br %span.label= event.merge_request.source_branch → %span.label= event.merge_request.target_branch diff --git a/app/views/events/_event_push.html.haml b/app/views/events/_event_push.html.haml index c59b482f5a5..f09aac29bfc 100644 --- a/app/views/events/_event_push.html.haml +++ b/app/views/events/_event_push.html.haml @@ -1,7 +1,8 @@ - if event.new_branch? || event.new_tag? = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} - pushed new + %span.label.pushed pushed +  new - if event.new_tag? tag = link_to project_commits_path(event.project, :ref => event.tag_name) do @@ -18,7 +19,8 @@ - else = image_tag gravatar_icon(event.author_email), :class => "avatar" %strong #{event.author_name} - pushed to + %span.label.pushed pushed +  to = link_to project_commits_path(event.project, :ref => event.branch_name) do %strong= event.branch_name at @@ -30,10 +32,10 @@ = link_to compare_project_commits_path(event.project, :from => event.commits.first.prev_commit_id, :to => event.commits.last.id) do Compare #{event.commits.first.commit.id[0..8]}...#{event.commits.last.id[0..8]} - @project = event.project - %ul.unstyled - - if event.commits.size > 4 - = render event.commits[0..2] - %li ... and #{event.commits.size - 3} more commits + %ul.unstyled.event_commits + - if event.commits.size > 3 + = render event.commits[0...2] + %li ... and #{event.commits.size - 2} more commits - else = render event.commits diff --git a/app/views/layouts/_app_menu.html.haml b/app/views/layouts/_app_menu.html.haml index 27d9b458bc9..b9249b525b0 100644 --- a/app/views/layouts/_app_menu.html.haml +++ b/app/views/layouts/_app_menu.html.haml @@ -1,6 +1,11 @@ %nav.main_menu = render "layouts/const_menu_links" = link_to "Projects", projects_path, :class => "#{"current" if current_page?(projects_path)}" - = link_to "Issues", dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide" - = link_to "Requests", dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide" + = link_to dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide" do + Issues + %span.count= current_user.assigned_issues.opened.count + = link_to dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide" do + Requests + %span.count= current_user.cared_merge_requests.count + = link_to "Search", search_path, :class => "#{"current" if current_page?(search_path)}" = link_to "Help", help_path, :class => "#{"current" if controller.controller_name == "help"}" diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 221bca559a9..88429a4857e 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -7,7 +7,9 @@ %h1 GITLAB %h1.project_name= title - .search= text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" + .search + = form_tag search_path, :method => :get do |f| + = text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" - if current_user.is_admin? = link_to admin_projects_path, :class => "admin_link", :title => "Admin area" do = image_tag "admin.PNG", :width => 16 diff --git a/app/views/merge_requests/_commits.html.haml b/app/views/merge_requests/_commits.html.haml index 7606e71e7c0..60cb21ba16e 100644 --- a/app/views/merge_requests/_commits.html.haml +++ b/app/views/merge_requests/_commits.html.haml @@ -2,7 +2,9 @@ .ui-box %h5 Commits .merge-request-commits - %ul.unstyled= render @commits + %ul.unstyled + - @commits.each do |commit| + = render "commits/commit", :commit => commit - else %h5 diff --git a/app/views/merge_requests/_how_to_merge.html.haml b/app/views/merge_requests/_how_to_merge.html.haml index 19caca3fc08..5cf5e5bcb9c 100644 --- a/app/views/merge_requests/_how_to_merge.html.haml +++ b/app/views/merge_requests/_how_to_merge.html.haml @@ -15,7 +15,7 @@ :javascript $(function(){ var modal = $('#modal_merge_info').modal({modal: true}); - $('.info_link').bind("click", function(){ + $('.how_to_merge_link').bind("click", function(){ modal.show(); }); $('.modal-header .close').bind("click", function(){ diff --git a/app/views/merge_requests/_merge_request.html.haml b/app/views/merge_requests/_merge_request.html.haml index 4b3470f98ad..9f556c06561 100644 --- a/app/views/merge_requests/_merge_request.html.haml +++ b/app/views/merge_requests/_merge_request.html.haml @@ -6,7 +6,7 @@ = time_ago_in_words(merge_request.created_at) ago - if merge_request.notes.any? - %span.label= pluralize merge_request.notes.count, 'note' + %span.pretty_label= pluralize merge_request.notes.count, 'note' - if merge_request.upvotes > 0 %span.label.success= "+#{merge_request.upvotes}" .right diff --git a/app/views/merge_requests/show.html.haml b/app/views/merge_requests/show.html.haml index 6b9ade455b2..e3ee5bc62c1 100644 --- a/app/views/merge_requests/show.html.haml +++ b/app/views/merge_requests/show.html.haml @@ -1,9 +1,9 @@ %h3 = "Merge Request ##{@merge_request.id}:"   - %span.label= @merge_request.source_branch + %span.pretty_label.branch= @merge_request.source_branch → - %span.label= @merge_request.target_branch + %span.pretty_label.branch= @merge_request.target_branch %small created at @@ -11,12 +11,10 @@ %span.right - if can?(current_user, :modify_merge_request, @merge_request) - - if @merge_request.closed - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => false }, :status_only => true), :method => :put, :class => "btn" - - else + - if @merge_request.open? = link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn", :title => "Close merge request" - = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do - Edit + = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do + Edit %br - if @merge_request.upvotes > 0 @@ -28,17 +26,27 @@ %hr -- if @merge_request.closed - .alert-message.error Closed -- else - .alert-message.success - = link_to "#", :class => "info_link", :title => "How To Merge" do - = image_tag "Info-UI.PNG" - Open +.merge_request_status_holder + - if @merge_request.closed + %h5 + .alert-message.error.status_info Closed + - if @merge_request.merged? + %span + Merged by #{@merge_request.merge_event.author_name} + %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. + - elsif @merge_request.closed_event + %span + Closed by #{@merge_request.closed_event.author_name} + %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. + %br + - else + %h5 + .alert-message.success.status_info Open + = link_to "How to merge", "#", :class => "vlink how_to_merge_link", :title => "How To Merge" = render "merge_requests/how_to_merge" -%div.well +%div.well.prettyprint %div %cite.cgray Created by = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av" diff --git a/app/views/profile/password.html.haml b/app/views/profile/password.html.haml index dc1f221435e..350d6801fd3 100644 --- a/app/views/profile/password.html.haml +++ b/app/views/profile/password.html.haml @@ -1,42 +1,46 @@ -%h3 Password -%hr -= form_for @user, :url => profile_password_path, :method => :put do |f| - .data - .alert-message.block-message.warning - %p After successfull password update you will be redirected to login page where you should login with new password - -if @user.errors.any? - .alert-message.block-message.error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg - - .clearfix - = f.label :password - .input= f.password_field :password - .clearfix - = f.label :password_confirmation - .input= f.password_field :password_confirmation - .actions - = f.submit 'Save', :class => "btn" - -%h3 - Private token - %span.cred.right - keep it in secret! -%hr -= form_for @user, :url => profile_reset_private_token_path, :method => :put do |f| - .data - %p Private token used to access application resources without authentication. - %p For example its required to access commits feed. +.row + .span8 + %h3 Password %hr - %p.cgray - - if current_user.private_token - = text_field_tag "token", current_user.private_token - - else - You don`t have one yet. Click generate to fix it. - .actions - - if current_user.private_token - = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn" - - else - = f.submit 'Generate', :class => "btn" + = form_for @user, :url => profile_password_path, :method => :put do |f| + .data + .alert-message.block-message.warning + %p After successfull password update you will be redirected to login page where you should login with new password + -if @user.errors.any? + .alert-message.block-message.error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation + .actions + = f.submit 'Save', :class => "btn" + + .span7.right + %h3 + Private token + %span.cred.right + keep it in secret! + %hr + = form_for @user, :url => profile_reset_private_token_path, :method => :put do |f| + .data + .alert-message.block-message.warning + %p Private token used to access application resources without authentication. + %hr + %p * required for rss feed + %p.cgray + - if current_user.private_token + = text_field_tag "token", current_user.private_token + - else + You don`t have one yet. Click generate to fix it. + .actions + - if current_user.private_token + = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn" + - else + = f.submit 'Generate', :class => "btn" diff --git a/app/views/projects/_show.html.haml b/app/views/projects/_show.html.haml index 5d30b75bb64..edb9c1cefe6 100644 --- a/app/views/projects/_show.html.haml +++ b/app/views/projects/_show.html.haml @@ -1,4 +1,4 @@ -%h4.title +%h5.title = @project.name %br %div diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 5088c2e7297..3fb81370853 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -6,7 +6,7 @@ %li Visit profile → keys and add public key of every machine you want to use for work with gitlabhq. .alert-message.block-message.error - %ul.alert_holder + %ul.unstyled.alert_holder %li You should push repository to proceed. %li After push you will be able to browse code, commits etc. diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index e163851fd46..fee6363de09 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -1,22 +1,25 @@ - if @projects.any? .row - .span4 + .span11 + = render @events + .span5.right %div.leftbar.ui-box %h5 Projects + %small + (#{@projects.count}) - if current_user.can_create_project? %span.right = link_to new_project_path, :class => "btn very_small info" do New Project .content_list - @projects.each do |project| - = link_to project_path(project), :remote => true, :class => dom_class(project) do + = link_to project_path(project), :class => dom_class(project) do %h4 %span.ico.project - = truncate(project.name, :length => 22) - .span12.right - .show_holder.ui-box.padded - .loading + = truncate(project.name, :length => 25) + %span.right + → - else %h3 Nothing here @@ -31,20 +34,3 @@ New Project ยป - else If you will be added to project - it will be displayed here - - -:javascript - $(function(){ - $("a.project").live("ajax:before", function() { - $(".show_holder").html("
"); - $('a.project').removeClass("active"); - $(this).addClass("active"); - }); - $('a.project:first-child').trigger("click"); - }); - -- if @projects.count == @limit - :javascript - $(function(){ - Pager.init(#{@limit}); - }); diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 89a3a4265e4..ae92b1ca7b3 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -21,9 +21,13 @@ = text_field_tag :project_clone, @project.url_to_repo, :class => "xlarge one_click_select git_clone_url" - if @project.description.present? - = markdown @project.description + .prettyprint= markdown @project.description - unless @events.blank? - %h5.cgray Recent Activity + %br + %h5.cgray + %span.ico.activities + Recent Activity + %hr .content_list= render @events diff --git a/app/views/projects/show.js.haml b/app/views/projects/show.js.haml deleted file mode 100644 index 132ebebf23f..00000000000 --- a/app/views/projects/show.js.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @project.repo_exists? && @project.has_commits? - :plain - $(".show_holder").html("#{escape_javascript(render(:partial => 'projects/show'))}"); -- else - :plain - $(".show_holder").html("#{escape_javascript(render(:template => 'projects/empty'))}"); - diff --git a/app/views/search/_result.html.haml b/app/views/search/_result.html.haml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/app/views/search/_result.html.haml @@ -0,0 +1 @@ + diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml new file mode 100644 index 00000000000..a524b7b4013 --- /dev/null +++ b/app/views/search/show.html.haml @@ -0,0 +1,41 @@ += form_tag search_path, :method => :get do |f| + .padded + = label_tag :search, "Looking for" + .input + = text_field_tag :search, params[:search],:placeholder => "issue 143", :class => "xxlarge" + = submit_tag 'Search', :class => "btn primary" +- if params[:search].present? + %br + %h3 Search results + %hr + .search_results + - if @projects.empty? && @merge_requests.empty? + %h3 + %small Nothing here + - else + - if @projects.any? + - @projects.each do |project| + = link_to project do + %h4 + %span.ico.project + = project.name + %small + last activity at + = project.last_activity_date.stamp("Aug 25, 2011") + - if @merge_requests.any? + - @merge_requests.each do |merge_request| + = link_to [merge_request.project, merge_request] do + %h5 + Merge Request # + = merge_request.id + – + = truncate merge_request.title, :length => 50 + %small + updated at + = merge_request.updated_at.stamp("Aug 25, 2011") + %strong + %span.label= merge_request.project.name + :javascript + $(function() { + $(".search_results").highlight("#{params[:search]}"); + }) diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index d74f10a1ce5..28216ec33da 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -8,7 +8,13 @@ class PostReceive # Ignore push from non-gitlab users return false unless Key.find_by_identifier(author_key_id) + # Create push event project.observe_push(oldrev, newrev, ref, author_key_id) + + # Close merged MR + project.update_merge_requests(oldrev, newrev, ref, author_key_id) + + # Execute web hooks project.execute_web_hooks(oldrev, newrev, ref, author_key_id) end end diff --git a/config/routes.rb b/config/routes.rb index 4cc3a4ff77b..8241b542794 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Gitlab::Application.routes.draw do - + get 'search' => "search#show" # Optionally, enable Resque here require 'resque/server' @@ -40,6 +40,7 @@ Gitlab::Application.routes.draw do get "dashboard", :to => "dashboard#index" get "dashboard/issues", :to => "dashboard#issues" get "dashboard/merge_requests", :to => "dashboard#merge_requests" + get "dashboard/activities", :to => "dashboard#activities" #get "profile/:id", :to => "profile#show" diff --git a/db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb b/db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb new file mode 100644 index 00000000000..2dc1dfb4b6e --- /dev/null +++ b/db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb @@ -0,0 +1,6 @@ +class AddCommitsDiffStoreToMergeRequest < ActiveRecord::Migration + def change + add_column :merge_requests, :st_commits, :text, :null => true + add_column :merge_requests, :st_diffs, :text, :null => true + end +end diff --git a/db/migrate/20120315132931_add_merged_to_merge_request.rb b/db/migrate/20120315132931_add_merged_to_merge_request.rb new file mode 100644 index 00000000000..2deb59e4420 --- /dev/null +++ b/db/migrate/20120315132931_add_merged_to_merge_request.rb @@ -0,0 +1,5 @@ +class AddMergedToMergeRequest < ActiveRecord::Migration + def change + add_column :merge_requests, :merged, :boolean, :null => false, :default => false + end +end diff --git a/db/schema.rb b/db/schema.rb index 991401b5793..a90b6df1700 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120307095918) do +ActiveRecord::Schema.define(:version => 20120315132931) do create_table "events", :force => true do |t| t.string "target_type" @@ -61,6 +61,9 @@ ActiveRecord::Schema.define(:version => 20120307095918) do t.boolean "closed", :default => false, :null => false t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.text "st_commits" + t.text "st_diffs" + t.boolean "merged", :default => false, :null => false end add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" diff --git a/doc/installation.md b/doc/installation.md index 95249abc47b..22205af95ec 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -209,7 +209,7 @@ Application can be started with next command: cd /home/gitlab/gitlab sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb - sudo -u gitlab unicorn_rails -c config/unicorn.rb -E production -D + sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D Edit /etc/nginx/nginx.conf. Add next code to **http** section: @@ -256,33 +256,38 @@ Create init script in /etc/init.d/gitlab: NAME=unicorn DESC="Gitlab service" PID=/home/gitlab/gitlab/tmp/pids/unicorn.pid + RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid case "$1" in start) CD_TO_APP_DIR="cd /home/gitlab/gitlab" START_DAEMON_PROCESS="bundle exec unicorn_rails $DAEMON_OPTS" + START_RESQUE_PROCESS="./resque.sh" echo -n "Starting $DESC: " if [ `whoami` = root ]; then - sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS" + sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS" else - $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS + $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS fi echo "$NAME." ;; stop) echo -n "Stopping $DESC: " kill -QUIT `cat $PID` + kill -QUIT `cat $RESQUE_PID` echo "$NAME." ;; restart) echo -n "Restarting $DESC: " kill -USR2 `cat $PID` + kill -USR2 `cat $RESQUE_PID` echo "$NAME." ;; reload) echo -n "Reloading $DESC configuration: " kill -HUP `cat $PID` + kill -HUP `cat $RESQUE_PID` echo "$NAME." ;; *) diff --git a/resque.sh b/resque.sh index 84a1066f087..d8b68d463f0 100755 --- a/resque.sh +++ b/resque.sh @@ -1,2 +1,2 @@ mkdir tmp/pids -nohup bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production PIDFILE=tmp/pids/resque_worker_QUEUE.pid & >> log/resque_worker_QUEUE.log 2>&1 +nohup bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid & >> log/resque_worker.log 2>&1 diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cb28a7eca38..1f8133e7ff7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -160,6 +160,34 @@ describe Project do end end end + + describe :update_merge_requests do + let(:project) { Factory :project } + + before do + @merge_request = Factory :merge_request, + :project => project, + :merged => false, + :closed => false + @key = Factory :key, :user_id => project.owner.id + end + + it "should close merge request if last commit from source branch was pushed to target branch" do + @merge_request.reloaded_commits + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.identifier) + @merge_request.reload + @merge_request.merged.should be_true + @merge_request.closed.should be_true + end + + it "should update merge request commits with new one if pushed to source branch" do + @merge_request.last_commit.should == nil + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.identifier) + @merge_request.reload + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" + end + end end # == Schema Information # diff --git a/spec/requests/dashboard_spec.rb b/spec/requests/dashboard_spec.rb index 98de046fee1..cfdbb8d4729 100644 --- a/spec/requests/dashboard_spec.rb +++ b/spec/requests/dashboard_spec.rb @@ -1,6 +1,4 @@ require 'spec_helper' -__END__ -# Disabled for now describe "Dashboard" do before do @project = Factory :project @@ -22,19 +20,7 @@ describe "Dashboard" do end it "should have projects panel" do - within ".project-list" do - page.should have_content(@project.name) - end + page.should have_content(@project.name) end - - # Temporary disabled cause of travis - # TODO: fix or rewrite - #it "should have news feed" do - #within "#news-feed" do - #page.should have_content("commit") - #page.should have_content(@project.commit.author.name) - #page.should have_content(@project.commit.safe_message) - #end - #end end end diff --git a/spec/requests/merge_requests_spec.rb b/spec/requests/merge_requests_spec.rb index f4e25cab856..f8b8725f56b 100644 --- a/spec/requests/merge_requests_spec.rb +++ b/spec/requests/merge_requests_spec.rb @@ -42,7 +42,7 @@ describe "MergeRequests" do it { should have_content(@merge_request.title[0..10]) } it "Show page should inform user that merge request closed" do - page.should have_content "Reopen" + page.should have_content "Closed" end end end diff --git a/vendor/assets/javascripts/jquery.highlight.js b/vendor/assets/javascripts/jquery.highlight.js new file mode 100644 index 00000000000..7a67cf99844 --- /dev/null +++ b/vendor/assets/javascripts/jquery.highlight.js @@ -0,0 +1,53 @@ +/* + +highlight v3 + +Highlights arbitrary terms. + + + +MIT license. + +Johann Burkard + + + +*/ + +jQuery.fn.highlight = function(pat) { + function innerHighlight(node, pat) { + var skip = 0; + if (node.nodeType == 3) { + var pos = node.data.toUpperCase().indexOf(pat); + if (pos >= 0) { + var spannode = document.createElement('span'); + spannode.className = 'highlight_word'; + var middlebit = node.splitText(pos); + var endbit = middlebit.splitText(pat.length); + var middleclone = middlebit.cloneNode(true); + spannode.appendChild(middleclone); + middlebit.parentNode.replaceChild(spannode, middlebit); + skip = 1; + } + } + else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { + for (var i = 0; i < node.childNodes.length; ++i) { + i += innerHighlight(node.childNodes[i], pat); + } + } + return skip; + } + return this.each(function() { + innerHighlight(this, pat.toUpperCase()); + }); +}; + +jQuery.fn.removeHighlight = function() { + return this.find("span.highlight").each(function() { + this.parentNode.firstChild.nodeName; + with (this.parentNode) { + replaceChild(this.firstChild, this); + normalize(); + } + }).end(); +};