Commit 35d0de8f367e949c3bab50506916ca87eeb5d5ab

Authored by Steven Verbeek
2 parents 2a7cd2f1 cadf12c6

merging upstream changes

Showing 49 changed files with 511 additions and 168 deletions   Show diff stats
app/assets/javascripts/application.js
... ... @@ -11,6 +11,7 @@
11 11 //= require jquery.tagify
12 12 //= require jquery.cookie
13 13 //= require jquery.endless-scroll
  14 +//= require jquery.highlight
14 15 //= require bootstrap-modal
15 16 //= require modernizr
16 17 //= require chosen
... ...
app/assets/javascripts/merge_requests.js
... ... @@ -11,7 +11,7 @@ var MergeRequest = {
11 11  
12 12 $(".tabs a.merge-notes-tab").live("click", function(e) {
13 13 $(".merge-request-diffs").hide();
14   - $(".merge-request-notes").show();
  14 + $(".merge_request_notes").show();
15 15 e.preventDefault();
16 16 });
17 17  
... ... @@ -19,7 +19,7 @@ var MergeRequest = {
19 19 if(!MergeRequest.diffs_loaded) {
20 20 MergeRequest.loadDiff();
21 21 }
22   - $(".merge-request-notes").hide();
  22 + $(".merge_request_notes").hide();
23 23 $(".merge-request-diffs").show();
24 24 e.preventDefault();
25 25 });
... ... @@ -33,7 +33,7 @@ var MergeRequest = {
33 33 url: $(".merge-diffs-tab").attr("data-url"),
34 34 complete: function(){
35 35 MergeRequest.diffs_loaded = true;
36   - $(".merge-request-notes").hide();
  36 + $(".merge_request_notes").hide();
37 37 $(".dashboard-loader").hide()},
38 38 dataType: "script"});
39 39 }
... ...
app/assets/stylesheets/common.scss
... ... @@ -3,7 +3,7 @@ a {
3 3 color: $link_color;
4 4 &:hover {
5 5 text-decoration:none;
6   - color: $style_color;
  6 + color: $blue_link;
7 7 }
8 8  
9 9 &.btn {
... ... @@ -11,6 +11,18 @@ a {
11 11 }
12 12 }
13 13  
  14 +.btn {
  15 + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f1f1f1), color-stop(25%, #f1f1f1), to(#e6e6e6));
  16 + background-image: -webkit-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
  17 + background-image: -moz-linear-gradient(top, #f1f1f1, #f1f1f1 25%, #e6e6e6);
  18 + background-image: -ms-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
  19 + background-image: -o-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
  20 + background-image: linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
  21 +
  22 + &:hover {
  23 + }
  24 +}
  25 +
14 26 a:focus {
15 27 outline: none;
16 28 }
... ... @@ -29,6 +41,29 @@ a:focus {
29 41  
30 42 .label {
31 43 background-color: #474D57;
  44 +
  45 + &.pushed {
  46 + background-color: $link_color;
  47 + }
  48 +}
  49 +
  50 +.pretty_label {
  51 + @include round-borders-all(4px);
  52 + padding:2px 4px;
  53 + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
  54 + background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
  55 + background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
  56 + background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
  57 + color: #777;
  58 + border: 1px solid #DEDFE1;
  59 +
  60 + &.branch {
  61 + border:none;
  62 + font-size:13px;
  63 + background: #474D57;
  64 + color:#fff;
  65 + font-family: monospace;
  66 + }
32 67 }
33 68  
34 69 .tabs > li > a, .pills > li > a {
... ... @@ -807,12 +842,19 @@ p.time {
807 842 width:840px;
808 843 margin:auto;
809 844  
810   - .wll {
811   - padding:5px;
812   - margin-top:5px;
  845 + .dash_project_item {
  846 + margin-bottom:10px;
813 847 border:none;
814 848 &:hover {
815 849 background:none;
  850 +
  851 + h4 {
  852 + color:#2FA0BB;
  853 + .arrow {
  854 + background:#2FA0BB;
  855 + color:#fff;
  856 + }
  857 + }
816 858 }
817 859  
818 860 h4 {
... ... @@ -887,7 +929,7 @@ p.time {
887 929 }
888 930 a:last-child h4 { border:none; }
889 931  
890   - a.active {
  932 + a:hover {
891 933 h4 {
892 934 color:#111;
893 935 border-right:4px solid $styled_border_color;
... ... @@ -974,3 +1016,32 @@ p.time {
974 1016 }
975 1017 }
976 1018 }
  1019 +
  1020 +.highlight_word {
  1021 + background:#EEDC94;
  1022 +}
  1023 +
  1024 +.status_info {
  1025 + font-size:14px;
  1026 + padding:5px 15px;
  1027 + line-height:24px;
  1028 + width:60px;
  1029 + text-align:center;
  1030 + float:left;
  1031 + margin-right:20px;
  1032 +}
  1033 +
  1034 +.merge_request_status_holder {
  1035 + margin-bottom:20px;
  1036 +}
  1037 +
  1038 +.arrow{
  1039 + float: right;
  1040 + background: #E3E5EA;
  1041 + padding: 10px;
  1042 + border-radius: 5px;
  1043 + text-shadow: none;
  1044 + color: #999;
  1045 + line-height: 16px;
  1046 + font-weight:bold;
  1047 +}
... ...
app/assets/stylesheets/main.scss
... ... @@ -15,7 +15,7 @@ $app_padding:20px;
15 15 $bg_color: #FFF;
16 16 $styled_border_color: #2FA0BB;
17 17 $color: "#4BB8D2";
18   -$blue_link: "#2fa0bb";
  18 +$blue_link: #2fa0bb;
19 19  
20 20  
21 21 /** Style colors **/
... ...
app/assets/stylesheets/ui_basic.scss
... ... @@ -17,7 +17,7 @@
17 17 color: $link_color;
18 18 &:hover {
19 19 text-decoration:none;
20   - color: $style_color;
  20 + color: $blue_link;
21 21 }
22 22 }
23 23  
... ...
app/controllers/dashboard_controller.rb
... ... @@ -18,7 +18,7 @@ class DashboardController < ApplicationController
18 18 # Get authored or assigned open merge requests
19 19 def merge_requests
20 20 @projects = current_user.projects.all
21   - @merge_requests = MergeRequest.where("author_id = :id or assignee_id = :id", :id => current_user.id).opened.order("created_at DESC").limit(40)
  21 + @merge_requests = current_user.cared_merge_requests.order("created_at DESC").limit(40)
22 22 end
23 23  
24 24 # Get only assigned issues
... ...
app/controllers/merge_requests_controller.rb
... ... @@ -41,13 +41,9 @@ class MergeRequestsController < ApplicationController
41 41  
42 42 @note = @project.notes.new(:noteable => @merge_request)
43 43  
44   - @commits = @project.repo.
45   - commits_between(@merge_request.target_branch, @merge_request.source_branch).
46   - map {|c| Commit.new(c)}.
47   - sort_by(&:created_at).
48   - reverse
49   -
50   - render_full_content
  44 + # Get commits from repository
  45 + # or from cache if already merged
  46 + @commits = @merge_request.commits
51 47  
52 48 respond_to do |format|
53 49 format.html
... ... @@ -76,6 +72,7 @@ class MergeRequestsController < ApplicationController
76 72  
77 73 respond_to do |format|
78 74 if @merge_request.save
  75 + @merge_request.reload_code
79 76 format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' }
80 77 format.json { render json: @merge_request, status: :created, location: @merge_request }
81 78 else
... ... @@ -88,6 +85,7 @@ class MergeRequestsController < ApplicationController
88 85 def update
89 86 respond_to do |format|
90 87 if @merge_request.update_attributes(params[:merge_request].merge(:author_id_of_changes => current_user.id))
  88 + @merge_request.reload_code
91 89 format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' }
92 90 format.json { head :ok }
93 91 else
... ...
app/controllers/projects_controller.rb
... ... @@ -13,6 +13,7 @@ class ProjectsController < ApplicationController
13 13 def index
14 14 @projects = current_user.projects
15 15 @projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse
  16 + @events = Event.where(:project_id => @projects.map(&:id)).recent.limit(40)
16 17 end
17 18  
18 19 def new
... ... @@ -78,7 +79,6 @@ class ProjectsController < ApplicationController
78 79 render "projects/empty"
79 80 end
80 81 end
81   - format.js
82 82 end
83 83 end
84 84  
... ...
app/controllers/search_controller.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +class SearchController < ApplicationController
  2 + def show
  3 + query = params[:search]
  4 + if query.blank?
  5 + @projects = []
  6 + @merge_requests = []
  7 + else
  8 + @projects = Project.search(query).limit(10)
  9 + @merge_requests = MergeRequest.search(query).limit(10)
  10 + end
  11 + end
  12 +end
... ...
app/models/event.rb
... ... @@ -7,6 +7,7 @@ class Event &lt; ActiveRecord::Base
7 7 Reopened = 4
8 8 Pushed = 5
9 9 Commented = 6
  10 + Merged = 7
10 11  
11 12 belongs_to :project
12 13 belongs_to :target, :polymorphic => true
... ...
app/models/merge_request.rb
  1 +require File.join(Rails.root, "app/models/commit")
  2 +
1 3 class MergeRequest < ActiveRecord::Base
2 4 belongs_to :project
3 5 belongs_to :author, :class_name => "User"
4 6 belongs_to :assignee, :class_name => "User"
5 7 has_many :notes, :as => :noteable, :dependent => :destroy
6 8  
  9 + serialize :st_commits
  10 + serialize :st_diffs
  11 +
7 12 attr_protected :author, :author_id, :project, :project_id
8 13 attr_accessor :author_id_of_changes
9 14  
... ... @@ -32,6 +37,13 @@ class MergeRequest &lt; ActiveRecord::Base
32 37 scope :closed, where(:closed => true)
33 38 scope :assigned, lambda { |u| where(:assignee_id => u.id)}
34 39  
  40 + def self.search query
  41 + where("title like :query", :query => "%#{query}%")
  42 + end
  43 +
  44 + def self.find_all_by_branch(branch_name)
  45 + where("source_branch like :branch or target_branch like :branch", :branch => branch_name)
  46 + end
35 47  
36 48 def validate_branches
37 49 if target_branch == source_branch
... ... @@ -39,23 +51,99 @@ class MergeRequest &lt; ActiveRecord::Base
39 51 end
40 52 end
41 53  
  54 + def reload_code
  55 + self.reloaded_commits
  56 + self.reloaded_diffs
  57 + end
  58 +
42 59 def new?
43 60 today? && created_at == updated_at
44 61 end
45 62  
46 63 def diffs
  64 + st_diffs || []
  65 + end
  66 +
  67 + def reloaded_diffs
  68 + if open? && unmerged_diffs.any?
  69 + self.st_diffs = unmerged_diffs
  70 + save
  71 + end
  72 + diffs
  73 + end
  74 +
  75 + def unmerged_diffs
47 76 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
48 77 diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
49 78 end
50 79  
51 80 def last_commit
52   - project.commit(source_branch)
  81 + commits.first
  82 + end
  83 +
  84 + def merged?
  85 + merged && merge_event
  86 + end
  87 +
  88 + def merge_event
  89 + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Merged).last
  90 + end
  91 +
  92 + def closed_event
  93 + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Closed).last
53 94 end
54 95  
  96 +
55 97 # Return the number of +1 comments (upvotes)
56 98 def upvotes
57 99 notes.select(&:upvote?).size
58 100 end
  101 +
  102 + def commits
  103 + st_commits || []
  104 + end
  105 +
  106 + def probably_merged?
  107 + unmerged_commits.empty? &&
  108 + commits.any? && open?
  109 + end
  110 +
  111 + def open?
  112 + !closed
  113 + end
  114 +
  115 + def mark_as_merged!
  116 + self.merged = true
  117 + self.closed = true
  118 + save
  119 + end
  120 +
  121 + def reloaded_commits
  122 + if open? && unmerged_commits.any?
  123 + self.st_commits = unmerged_commits
  124 + save
  125 + end
  126 + commits
  127 + end
  128 +
  129 + def unmerged_commits
  130 + self.project.repo.
  131 + commits_between(self.target_branch, self.source_branch).
  132 + map {|c| Commit.new(c)}.
  133 + sort_by(&:created_at).
  134 + reverse
  135 + end
  136 +
  137 + def merge!(user_id)
  138 + self.mark_as_merged!
  139 + Event.create(
  140 + :project => self.project,
  141 + :action => Event::Merged,
  142 + :target_id => self.id,
  143 + :target_type => "MergeRequest",
  144 + :author_id => user_id
  145 + )
  146 + end
59 147 end
60 148 # == Schema Information
61 149 #
... ...
app/models/project.rb
... ... @@ -54,6 +54,10 @@ class Project &lt; ActiveRecord::Base
54 54 UsersProject.access_roles
55 55 end
56 56  
  57 + def self.search query
  58 + where("name like :query or code like :query or path like :query", :query => "%#{query}%")
  59 + end
  60 +
57 61 def to_param
58 62 code
59 63 end
... ... @@ -73,6 +77,24 @@ class Project &lt; ActiveRecord::Base
73 77 )
74 78 end
75 79  
  80 + def update_merge_requests(oldrev, newrev, ref, author_key_id)
  81 + return true unless ref =~ /heads/
  82 + branch_name = ref.gsub("refs/heads/", "")
  83 + user = Key.find_by_identifier(author_key_id).user
  84 + c_ids = self.commits_between(oldrev, newrev).map(&:id)
  85 +
  86 + # Update code for merge requests
  87 + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
  88 + mrs.each { |merge_request| merge_request.reload_code }
  89 +
  90 + # Close merge requests
  91 + mrs = self.merge_requests.opened.where(:target_branch => branch_name).all
  92 + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
  93 + mrs.each { |merge_request| merge_request.merge!(user.id) }
  94 +
  95 + true
  96 + end
  97 +
76 98 def execute_web_hooks(oldrev, newrev, ref, author_key_id)
77 99 ref_parts = ref.split('/')
78 100  
... ...
app/models/user.rb
... ... @@ -86,6 +86,10 @@ class User &lt; ActiveRecord::Base
86 86 )
87 87 end
88 88 end
  89 +
  90 + def cared_merge_requests
  91 + MergeRequest.where("author_id = :id or assignee_id = :id", :id => self.id).opened
  92 + end
89 93 end
90 94 # == Schema Information
91 95 #
... ...
app/views/commits/show.html.haml
... ... @@ -12,8 +12,8 @@
12 12 = @commit.committer_name
13 13 %small= @commit.committed_date.stamp("Aug 21, 2011 9:23pm")
14 14  
15   -%hr
16   -%pre.commit_message
  15 +%br
  16 +%pre.commit_message.prettyprint
17 17 = commit_msg_with_link_to_issues(@project, @commit.safe_message)
18 18 .clear
19 19 %br
... ...
app/views/dashboard/_events_feed.html.haml
... ... @@ -1,2 +0,0 @@
1   -= render @events
2   -
app/views/dashboard/_issues_feed.html.haml
... ... @@ -3,8 +3,10 @@
3 3 = link_to [issue.project, issue] do
4 4 %p
5 5 %strong
6   - %span.label= issue.project.name
  6 + %span.pretty_label= issue.project.name
7 7 &ndash;
8 8 Issue #
9 9 = issue.id
10 10 = truncate issue.title, :length => 50
  11 + %span.right.cgray
  12 + = issue.updated_at.stamp("Aug 21, 2011")
... ...
app/views/dashboard/_merge_requests_feed.html.haml
... ... @@ -3,8 +3,9 @@
3 3 = link_to [merge_request.project, merge_request] do
4 4 %p
5 5 %strong
6   - %span.label= merge_request.project.name
  6 + %span.pretty_label= merge_request.project.name
7 7 &ndash;
8   - Merge Request #
9   - = merge_request.id
  8 + Merge Request ##{merge_request.id}
10 9 = truncate merge_request.title, :length => 50
  10 + %span.right.cgray
  11 + = merge_request.updated_at.stamp("Aug 21, 2011")
... ...
app/views/dashboard/_projects_feed.html.haml
1   -- @active_projects.first(5).each do |project|
2   - .wll
  1 +- projects.first(5).each do |project|
  2 + %div.dash_project_item
3 3 = link_to project do
4 4 %h4
5 5 %span.ico.project
... ... @@ -7,3 +7,5 @@
7 7 %small
8 8 last activity at
9 9 = project.last_activity_date.stamp("Aug 25, 2011")
  10 + %span.right.arrow
  11 + &rarr;
... ...
app/views/dashboard/index.html.haml
... ... @@ -20,7 +20,7 @@
20 20 .row
21 21 .dashboard_block
22 22 .row
23   - .span10= render "dashboard/projects_feed"
  23 + .span10= render "dashboard/projects_feed", :projects => @active_projects
24 24 .span4.right
25 25 - if current_user.can_create_project?
26 26 .alert-message.block-message.warning
... ... @@ -65,4 +65,4 @@
65 65  
66 66 %hr
67 67 .row
68   - .dashboard_block= render "dashboard/events_feed"
  68 + .dashboard_block= render @events
... ...
app/views/events/_event_changed_issue.html.haml
1 1 = image_tag gravatar_icon(event.author_email), :class => "avatar"
2 2 %strong #{event.author_name}
3   -- if event.closed?
4   - closed
5   -- else
6   - reopened
7   -issue
  3 +%span.label.important
  4 + - if event.closed?
  5 + closed
  6 + - else
  7 + reopened
  8 +&nbsp;issue
8 9 = link_to project_issue_path(event.project, event.issue) do
9 10 %strong= truncate event.issue_title
10 11 at
... ...
app/views/events/_event_changed_merge_request.html.haml
1 1 = image_tag gravatar_icon(event.author_email), :class => "avatar"
2 2 %strong #{event.author_name}
3   -- if event.closed?
4   - closed
5   -- else
6   - reopened
7   -merge request
  3 +%span.label.important
  4 + - if event.closed?
  5 + closed
  6 + - else
  7 + reopened
  8 +&nbsp;merge request
8 9 = link_to project_merge_request_path(event.project, event.merge_request) do
9 10 %strong= truncate event.merge_request_title
10 11 at
... ... @@ -12,7 +13,6 @@ at
12 13 %span.cgray
13 14 = time_ago_in_words(event.created_at)
14 15 ago.
15   -%br
16 16 %span.label= event.merge_request.source_branch
17 17 &rarr;
18 18 %span.label= event.merge_request.target_branch
... ...
app/views/events/_event_new_issue.html.haml
1 1 = image_tag gravatar_icon(event.author_email), :class => "avatar"
2 2 %strong #{event.author_name}
3   -created new issue
  3 +%span.label.success created
  4 +&nbsp;new issue
4 5 = link_to project_issue_path(event.project, event.issue) do
5 6 %strong= truncate event.issue_title
6 7 at
... ...
app/views/events/_event_new_merge_request.html.haml
1 1 = image_tag gravatar_icon(event.author_email), :class => "avatar"
2 2 %strong #{event.author_name}
3   -requested merge
  3 +%span.label.success requested
  4 +&nbsp;merge
4 5 = link_to project_merge_request_path(event.project, event.merge_request) do
5 6 %strong= truncate event.merge_request_title
6 7 at
... ... @@ -8,7 +9,6 @@ at
8 9 %span.cgray
9 10 = time_ago_in_words(event.created_at)
10 11 ago.
11   -%br
12 12 %span.label= event.merge_request.source_branch
13 13 &rarr;
14 14 %span.label= event.merge_request.target_branch
... ...
app/views/events/_event_push.html.haml
1 1 - if event.new_branch? || event.new_tag?
2 2 = image_tag gravatar_icon(event.author_email), :class => "avatar"
3 3 %strong #{event.author_name}
4   - pushed new
  4 + %span.label.pushed pushed
  5 + &nbsp;new
5 6 - if event.new_tag?
6 7 tag
7 8 = link_to project_commits_path(event.project, :ref => event.tag_name) do
... ... @@ -18,7 +19,8 @@
18 19 - else
19 20 = image_tag gravatar_icon(event.author_email), :class => "avatar"
20 21 %strong #{event.author_name}
21   - pushed to
  22 + %span.label.pushed pushed
  23 + &nbsp;to
22 24 = link_to project_commits_path(event.project, :ref => event.branch_name) do
23 25 %strong= event.branch_name
24 26 at
... ... @@ -30,10 +32,10 @@
30 32 = link_to compare_project_commits_path(event.project, :from => event.commits.first.prev_commit_id, :to => event.commits.last.id) do
31 33 Compare #{event.commits.first.commit.id[0..8]}...#{event.commits.last.id[0..8]}
32 34 - @project = event.project
33   - %ul.unstyled
34   - - if event.commits.size > 4
35   - = render event.commits[0..2]
36   - %li ... and #{event.commits.size - 3} more commits
  35 + %ul.unstyled.event_commits
  36 + - if event.commits.size > 3
  37 + = render event.commits[0...2]
  38 + %li ... and #{event.commits.size - 2} more commits
37 39 - else
38 40 = render event.commits
39 41  
... ...
app/views/layouts/_app_menu.html.haml
1 1 %nav.main_menu
2 2 = render "layouts/const_menu_links"
3 3 = link_to "Projects", projects_path, :class => "#{"current" if current_page?(projects_path)}"
4   - = link_to "Issues", dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide"
5   - = link_to "Requests", dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide"
  4 + = link_to dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide" do
  5 + Issues
  6 + %span.count= current_user.assigned_issues.opened.count
  7 + = link_to dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide" do
  8 + Requests
  9 + %span.count= current_user.cared_merge_requests.count
  10 + = link_to "Search", search_path, :class => "#{"current" if current_page?(search_path)}"
6 11 = link_to "Help", help_path, :class => "#{"current" if controller.controller_name == "help"}"
... ...
app/views/layouts/_head_panel.html.haml
... ... @@ -7,7 +7,9 @@
7 7 %h1
8 8 GITLAB
9 9 %h1.project_name= title
10   - .search= text_field_tag "search", nil, :placeholder => "Search", :class => "search-input"
  10 + .search
  11 + = form_tag search_path, :method => :get do |f|
  12 + = text_field_tag "search", nil, :placeholder => "Search", :class => "search-input"
11 13 - if current_user.is_admin?
12 14 = link_to admin_projects_path, :class => "admin_link", :title => "Admin area" do
13 15 = image_tag "admin.PNG", :width => 16
... ...
app/views/merge_requests/_commits.html.haml
... ... @@ -2,7 +2,9 @@
2 2 .ui-box
3 3 %h5 Commits
4 4 .merge-request-commits
5   - %ul.unstyled= render @commits
  5 + %ul.unstyled
  6 + - @commits.each do |commit|
  7 + = render "commits/commit", :commit => commit
6 8  
7 9 - else
8 10 %h5
... ...
app/views/merge_requests/_how_to_merge.html.haml
... ... @@ -15,7 +15,7 @@
15 15 :javascript
16 16 $(function(){
17 17 var modal = $('#modal_merge_info').modal({modal: true});
18   - $('.info_link').bind("click", function(){
  18 + $('.how_to_merge_link').bind("click", function(){
19 19 modal.show();
20 20 });
21 21 $('.modal-header .close').bind("click", function(){
... ...
app/views/merge_requests/_merge_request.html.haml
... ... @@ -6,7 +6,7 @@
6 6 = time_ago_in_words(merge_request.created_at)
7 7 ago
8 8 - if merge_request.notes.any?
9   - %span.label= pluralize merge_request.notes.count, 'note'
  9 + %span.pretty_label= pluralize merge_request.notes.count, 'note'
10 10 - if merge_request.upvotes > 0
11 11 %span.label.success= "+#{merge_request.upvotes}"
12 12 .right
... ...
app/views/merge_requests/show.html.haml
1 1 %h3
2 2 = "Merge Request ##{@merge_request.id}:"
3 3 &nbsp;
4   - %span.label= @merge_request.source_branch
  4 + %span.pretty_label.branch= @merge_request.source_branch
5 5 &rarr;
6   - %span.label= @merge_request.target_branch
  6 + %span.pretty_label.branch= @merge_request.target_branch
7 7  
8 8 %small
9 9 created at
... ... @@ -11,12 +11,10 @@
11 11  
12 12 %span.right
13 13 - if can?(current_user, :modify_merge_request, @merge_request)
14   - - if @merge_request.closed
15   - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => false }, :status_only => true), :method => :put, :class => "btn"
16   - - else
  14 + - if @merge_request.open?
17 15 = 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"
18   - = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do
19   - Edit
  16 + = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do
  17 + Edit
20 18  
21 19 %br
22 20 - if @merge_request.upvotes > 0
... ... @@ -28,17 +26,27 @@
28 26  
29 27  
30 28 %hr
31   -- if @merge_request.closed
32   - .alert-message.error Closed
33   -- else
34   - .alert-message.success
35   - = link_to "#", :class => "info_link", :title => "How To Merge" do
36   - = image_tag "Info-UI.PNG"
37   - Open
  29 +.merge_request_status_holder
  30 + - if @merge_request.closed
  31 + %h5
  32 + .alert-message.error.status_info Closed
  33 + - if @merge_request.merged?
  34 + %span
  35 + Merged by #{@merge_request.merge_event.author_name}
  36 + %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.
  37 + - elsif @merge_request.closed_event
  38 + %span
  39 + Closed by #{@merge_request.closed_event.author_name}
  40 + %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
  41 + %br
  42 + - else
  43 + %h5
  44 + .alert-message.success.status_info Open
  45 + = link_to "How to merge", "#", :class => "vlink how_to_merge_link", :title => "How To Merge"
38 46  
39 47 = render "merge_requests/how_to_merge"
40 48  
41   -%div.well
  49 +%div.well.prettyprint
42 50 %div
43 51 %cite.cgray Created by
44 52 = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av"
... ...
app/views/profile/password.html.haml
1   -%h3 Password
2   -%hr
3   -= form_for @user, :url => profile_password_path, :method => :put do |f|
4   - .data
5   - .alert-message.block-message.warning
6   - %p After successfull password update you will be redirected to login page where you should login with new password
7   - -if @user.errors.any?
8   - .alert-message.block-message.error
9   - %ul
10   - - @user.errors.full_messages.each do |msg|
11   - %li= msg
  1 +.row
  2 + .span8
  3 + %h3 Password
  4 + %hr
  5 + = form_for @user, :url => profile_password_path, :method => :put do |f|
  6 + .data
  7 + .alert-message.block-message.warning
  8 + %p After successfull password update you will be redirected to login page where you should login with new password
  9 + -if @user.errors.any?
  10 + .alert-message.block-message.error
  11 + %ul
  12 + - @user.errors.full_messages.each do |msg|
  13 + %li= msg
  14 +
  15 + .clearfix
  16 + = f.label :password
  17 + .input= f.password_field :password
  18 + .clearfix
  19 + = f.label :password_confirmation
  20 + .input= f.password_field :password_confirmation
  21 + .actions
  22 + = f.submit 'Save', :class => "btn"
12 23  
13   - .clearfix
14   - = f.label :password
15   - .input= f.password_field :password
16   - .clearfix
17   - = f.label :password_confirmation
18   - .input= f.password_field :password_confirmation
19   - .actions
20   - = f.submit 'Save', :class => "btn"
21   -
22   -%h3
23   - Private token
24   - %span.cred.right
25   - keep it in secret!
26   -%hr
27   -= form_for @user, :url => profile_reset_private_token_path, :method => :put do |f|
28   - .data
29   - %p Private token used to access application resources without authentication.
30   - %p For example its required to access commits feed.
  24 + .span7.right
  25 + %h3
  26 + Private token
  27 + %span.cred.right
  28 + keep it in secret!
31 29 %hr
32   - %p.cgray
33   - - if current_user.private_token
34   - = text_field_tag "token", current_user.private_token
35   - - else
36   - You don`t have one yet. Click generate to fix it.
37   - .actions
38   - - if current_user.private_token
39   - = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn"
40   - - else
41   - = f.submit 'Generate', :class => "btn"
  30 + = form_for @user, :url => profile_reset_private_token_path, :method => :put do |f|
  31 + .data
  32 + .alert-message.block-message.warning
  33 + %p Private token used to access application resources without authentication.
  34 + %hr
  35 + %p * required for rss feed
  36 + %p.cgray
  37 + - if current_user.private_token
  38 + = text_field_tag "token", current_user.private_token
  39 + - else
  40 + You don`t have one yet. Click generate to fix it.
  41 + .actions
  42 + - if current_user.private_token
  43 + = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn"
  44 + - else
  45 + = f.submit 'Generate', :class => "btn"
42 46  
... ...
app/views/projects/_show.html.haml
1   -%h4.title
  1 +%h5.title
2 2 = @project.name
3 3 %br
4 4 %div
... ...
app/views/projects/empty.html.haml
... ... @@ -6,7 +6,7 @@
6 6 %li Visit profile &rarr; keys and add public key of every machine you want to use for work with gitlabhq.
7 7  
8 8 .alert-message.block-message.error
9   - %ul.alert_holder
  9 + %ul.unstyled.alert_holder
10 10 %li You should push repository to proceed.
11 11 %li After push you will be able to browse code, commits etc.
12 12  
... ...
app/views/projects/index.html.haml
1 1 - if @projects.any?
2 2 .row
3   - .span4
  3 + .span11
  4 + = render @events
  5 + .span5.right
4 6 %div.leftbar.ui-box
5 7 %h5
6 8 Projects
  9 + %small
  10 + (#{@projects.count})
7 11 - if current_user.can_create_project?
8 12 %span.right
9 13 = link_to new_project_path, :class => "btn very_small info" do
10 14 New Project
11 15 .content_list
12 16 - @projects.each do |project|
13   - = link_to project_path(project), :remote => true, :class => dom_class(project) do
  17 + = link_to project_path(project), :class => dom_class(project) do
14 18 %h4
15 19 %span.ico.project
16   - = truncate(project.name, :length => 22)
17   - .span12.right
18   - .show_holder.ui-box.padded
19   - .loading
  20 + = truncate(project.name, :length => 25)
  21 + %span.right
  22 + &rarr;
20 23  
21 24 - else
22 25 %h3 Nothing here
... ... @@ -31,20 +34,3 @@
31 34 New Project »
32 35 - else
33 36 If you will be added to project - it will be displayed here
34   -
35   -
36   -:javascript
37   - $(function(){
38   - $("a.project").live("ajax:before", function() {
39   - $(".show_holder").html("<div class='loading'>");
40   - $('a.project').removeClass("active");
41   - $(this).addClass("active");
42   - });
43   - $('a.project:first-child').trigger("click");
44   - });
45   -
46   -- if @projects.count == @limit
47   - :javascript
48   - $(function(){
49   - Pager.init(#{@limit});
50   - });
... ...
app/views/projects/show.html.haml
... ... @@ -21,9 +21,13 @@
21 21 = text_field_tag :project_clone, @project.url_to_repo, :class => "xlarge one_click_select git_clone_url"
22 22  
23 23 - if @project.description.present?
24   - = markdown @project.description
  24 + .prettyprint= markdown @project.description
25 25 - unless @events.blank?
26   - %h5.cgray Recent Activity
  26 + %br
  27 + %h5.cgray
  28 + %span.ico.activities
  29 + Recent Activity
  30 + %hr
27 31 .content_list= render @events
28 32  
29 33  
... ...
app/views/projects/show.js.haml
... ... @@ -1,7 +0,0 @@
1   -- if @project.repo_exists? && @project.has_commits?
2   - :plain
3   - $(".show_holder").html("#{escape_javascript(render(:partial => 'projects/show'))}");
4   -- else
5   - :plain
6   - $(".show_holder").html("#{escape_javascript(render(:template => 'projects/empty'))}");
7   -
app/views/search/_result.html.haml 0 → 100644
No preview for this file type
app/views/search/show.html.haml 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 += form_tag search_path, :method => :get do |f|
  2 + .padded
  3 + = label_tag :search, "Looking for"
  4 + .input
  5 + = text_field_tag :search, params[:search],:placeholder => "issue 143", :class => "xxlarge"
  6 + = submit_tag 'Search', :class => "btn primary"
  7 +- if params[:search].present?
  8 + %br
  9 + %h3 Search results
  10 + %hr
  11 + .search_results
  12 + - if @projects.empty? && @merge_requests.empty?
  13 + %h3
  14 + %small Nothing here
  15 + - else
  16 + - if @projects.any?
  17 + - @projects.each do |project|
  18 + = link_to project do
  19 + %h4
  20 + %span.ico.project
  21 + = project.name
  22 + %small
  23 + last activity at
  24 + = project.last_activity_date.stamp("Aug 25, 2011")
  25 + - if @merge_requests.any?
  26 + - @merge_requests.each do |merge_request|
  27 + = link_to [merge_request.project, merge_request] do
  28 + %h5
  29 + Merge Request #
  30 + = merge_request.id
  31 + &ndash;
  32 + = truncate merge_request.title, :length => 50
  33 + %small
  34 + updated at
  35 + = merge_request.updated_at.stamp("Aug 25, 2011")
  36 + %strong
  37 + %span.label= merge_request.project.name
  38 + :javascript
  39 + $(function() {
  40 + $(".search_results").highlight("#{params[:search]}");
  41 + })
... ...
app/workers/post_receive.rb
... ... @@ -8,7 +8,13 @@ class PostReceive
8 8 # Ignore push from non-gitlab users
9 9 return false unless Key.find_by_identifier(author_key_id)
10 10  
  11 + # Create push event
11 12 project.observe_push(oldrev, newrev, ref, author_key_id)
  13 +
  14 + # Close merged MR
  15 + project.update_merge_requests(oldrev, newrev, ref, author_key_id)
  16 +
  17 + # Execute web hooks
12 18 project.execute_web_hooks(oldrev, newrev, ref, author_key_id)
13 19 end
14 20 end
... ...
config/routes.rb
1 1 Gitlab::Application.routes.draw do
2   -
  2 + get 'search' => "search#show"
3 3  
4 4 # Optionally, enable Resque here
5 5 require 'resque/server'
... ... @@ -40,6 +40,7 @@ Gitlab::Application.routes.draw do
40 40 get "dashboard", :to => "dashboard#index"
41 41 get "dashboard/issues", :to => "dashboard#issues"
42 42 get "dashboard/merge_requests", :to => "dashboard#merge_requests"
  43 + get "dashboard/activities", :to => "dashboard#activities"
43 44  
44 45 #get "profile/:id", :to => "profile#show"
45 46  
... ...
db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +class AddCommitsDiffStoreToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :st_commits, :text, :null => true
  4 + add_column :merge_requests, :st_diffs, :text, :null => true
  5 + end
  6 +end
... ...
db/migrate/20120315132931_add_merged_to_merge_request.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddMergedToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :merged, :boolean, :null => false, :default => false
  4 + end
  5 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(:version => 20120307095918) do
  14 +ActiveRecord::Schema.define(:version => 20120315132931) do
15 15  
16 16 create_table "events", :force => true do |t|
17 17 t.string "target_type"
... ... @@ -61,6 +61,9 @@ ActiveRecord::Schema.define(:version =&gt; 20120307095918) do
61 61 t.boolean "closed", :default => false, :null => false
62 62 t.datetime "created_at", :null => false
63 63 t.datetime "updated_at", :null => false
  64 + t.text "st_commits"
  65 + t.text "st_diffs"
  66 + t.boolean "merged", :default => false, :null => false
64 67 end
65 68  
66 69 add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
... ...
doc/installation.md
... ... @@ -209,7 +209,7 @@ Application can be started with next command:
209 209  
210 210 cd /home/gitlab/gitlab
211 211 sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb
212   - sudo -u gitlab unicorn_rails -c config/unicorn.rb -E production -D
  212 + sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D
213 213  
214 214 Edit /etc/nginx/nginx.conf. Add next code to **http** section:
215 215  
... ... @@ -256,33 +256,38 @@ Create init script in /etc/init.d/gitlab:
256 256 NAME=unicorn
257 257 DESC="Gitlab service"
258 258 PID=/home/gitlab/gitlab/tmp/pids/unicorn.pid
  259 + RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid
259 260  
260 261 case "$1" in
261 262 start)
262 263 CD_TO_APP_DIR="cd /home/gitlab/gitlab"
263 264 START_DAEMON_PROCESS="bundle exec unicorn_rails $DAEMON_OPTS"
  265 + START_RESQUE_PROCESS="./resque.sh"
264 266  
265 267 echo -n "Starting $DESC: "
266 268 if [ `whoami` = root ]; then
267   - sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS"
  269 + sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS"
268 270 else
269   - $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS
  271 + $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS
270 272 fi
271 273 echo "$NAME."
272 274 ;;
273 275 stop)
274 276 echo -n "Stopping $DESC: "
275 277 kill -QUIT `cat $PID`
  278 + kill -QUIT `cat $RESQUE_PID`
276 279 echo "$NAME."
277 280 ;;
278 281 restart)
279 282 echo -n "Restarting $DESC: "
280 283 kill -USR2 `cat $PID`
  284 + kill -USR2 `cat $RESQUE_PID`
281 285 echo "$NAME."
282 286 ;;
283 287 reload)
284 288 echo -n "Reloading $DESC configuration: "
285 289 kill -HUP `cat $PID`
  290 + kill -HUP `cat $RESQUE_PID`
286 291 echo "$NAME."
287 292 ;;
288 293 *)
... ...
resque.sh
1 1 mkdir tmp/pids
2   -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
  2 +nohup bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid & >> log/resque_worker.log 2>&1
... ...
spec/models/project_spec.rb
... ... @@ -160,6 +160,34 @@ describe Project do
160 160 end
161 161 end
162 162 end
  163 +
  164 + describe :update_merge_requests do
  165 + let(:project) { Factory :project }
  166 +
  167 + before do
  168 + @merge_request = Factory :merge_request,
  169 + :project => project,
  170 + :merged => false,
  171 + :closed => false
  172 + @key = Factory :key, :user_id => project.owner.id
  173 + end
  174 +
  175 + it "should close merge request if last commit from source branch was pushed to target branch" do
  176 + @merge_request.reloaded_commits
  177 + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
  178 + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.identifier)
  179 + @merge_request.reload
  180 + @merge_request.merged.should be_true
  181 + @merge_request.closed.should be_true
  182 + end
  183 +
  184 + it "should update merge request commits with new one if pushed to source branch" do
  185 + @merge_request.last_commit.should == nil
  186 + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.identifier)
  187 + @merge_request.reload
  188 + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
  189 + end
  190 + end
163 191 end
164 192 # == Schema Information
165 193 #
... ...
spec/requests/dashboard_spec.rb
1 1 require 'spec_helper'
2   -__END__
3   -# Disabled for now
4 2 describe "Dashboard" do
5 3 before do
6 4 @project = Factory :project
... ... @@ -22,19 +20,7 @@ describe &quot;Dashboard&quot; do
22 20 end
23 21  
24 22 it "should have projects panel" do
25   - within ".project-list" do
26   - page.should have_content(@project.name)
27   - end
  23 + page.should have_content(@project.name)
28 24 end
29   -
30   - # Temporary disabled cause of travis
31   - # TODO: fix or rewrite
32   - #it "should have news feed" do
33   - #within "#news-feed" do
34   - #page.should have_content("commit")
35   - #page.should have_content(@project.commit.author.name)
36   - #page.should have_content(@project.commit.safe_message)
37   - #end
38   - #end
39 25 end
40 26 end
... ...
spec/requests/merge_requests_spec.rb
... ... @@ -42,7 +42,7 @@ describe &quot;MergeRequests&quot; do
42 42  
43 43 it { should have_content(@merge_request.title[0..10]) }
44 44 it "Show page should inform user that merge request closed" do
45   - page.should have_content "Reopen"
  45 + page.should have_content "Closed"
46 46 end
47 47 end
48 48 end
... ...
vendor/assets/javascripts/jquery.highlight.js 0 → 100644
... ... @@ -0,0 +1,53 @@
  1 +/*
  2 +
  3 +highlight v3
  4 +
  5 +Highlights arbitrary terms.
  6 +
  7 +<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>
  8 +
  9 +MIT license.
  10 +
  11 +Johann Burkard
  12 +<http://johannburkard.de>
  13 +<mailto:jb@eaio.com>
  14 +
  15 +*/
  16 +
  17 +jQuery.fn.highlight = function(pat) {
  18 + function innerHighlight(node, pat) {
  19 + var skip = 0;
  20 + if (node.nodeType == 3) {
  21 + var pos = node.data.toUpperCase().indexOf(pat);
  22 + if (pos >= 0) {
  23 + var spannode = document.createElement('span');
  24 + spannode.className = 'highlight_word';
  25 + var middlebit = node.splitText(pos);
  26 + var endbit = middlebit.splitText(pat.length);
  27 + var middleclone = middlebit.cloneNode(true);
  28 + spannode.appendChild(middleclone);
  29 + middlebit.parentNode.replaceChild(spannode, middlebit);
  30 + skip = 1;
  31 + }
  32 + }
  33 + else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
  34 + for (var i = 0; i < node.childNodes.length; ++i) {
  35 + i += innerHighlight(node.childNodes[i], pat);
  36 + }
  37 + }
  38 + return skip;
  39 + }
  40 + return this.each(function() {
  41 + innerHighlight(this, pat.toUpperCase());
  42 + });
  43 +};
  44 +
  45 +jQuery.fn.removeHighlight = function() {
  46 + return this.find("span.highlight").each(function() {
  47 + this.parentNode.firstChild.nodeName;
  48 + with (this.parentNode) {
  49 + replaceChild(this.firstChild, this);
  50 + normalize();
  51 + }
  52 + }).end();
  53 +};
... ...