Commit 9cd8f7b08296d73532c66f06581ff47dab9720a3

Authored by Dmitriy Zaporozhets
1 parent 2e54ac17

New Feature: Git Blame for file

app/assets/stylesheets/gitlab_bootstrap.scss
@@ -268,6 +268,13 @@ img.avatar { @@ -268,6 +268,13 @@ img.avatar {
268 -webkit-border-radius: 4px; 268 -webkit-border-radius: 4px;
269 -moz-border-radius: 4px; 269 -moz-border-radius: 4px;
270 border-radius: 4px; 270 border-radius: 4px;
  271 +
  272 + &.s16 {
  273 + width:16px;
  274 + }
  275 + &.s24 {
  276 + width:24px;
  277 + }
271 } 278 }
272 279
273 img.lil_av { 280 img.lil_av {
app/assets/stylesheets/tree.scss
@@ -53,6 +53,11 @@ @@ -53,6 +53,11 @@
53 padding: 9px 10px; 53 padding: 9px 10px;
54 height:18px; 54 height:18px;
55 55
  56 + .options {
  57 + float:right;
  58 + margin-top: -5px;
  59 + }
  60 +
56 .file_name { 61 .file_name {
57 color:$style_color; 62 color:$style_color;
58 font-size:14px; 63 font-size:14px;
@@ -220,3 +225,27 @@ @@ -220,3 +225,27 @@
220 margin:0; 225 margin:0;
221 } 226 }
222 } 227 }
  228 +
  229 +.blame_file {
  230 + .view_file_content {
  231 + tr {
  232 + border-bottom: 1px solid #eee;
  233 + }
  234 + td {
  235 + padding:5px;
  236 + }
  237 + .author,
  238 + .commit {
  239 + background:#f5f5f5;
  240 + vertical-align:top;
  241 + }
  242 + .lines {
  243 + pre {
  244 + padding:0;
  245 + margin:0;
  246 + background:none;
  247 + border:none;
  248 + }
  249 + }
  250 + }
  251 +}
app/controllers/refs_controller.rb
@@ -8,7 +8,7 @@ class RefsController < ApplicationController @@ -8,7 +8,7 @@ class RefsController < ApplicationController
8 before_filter :require_non_empty_project 8 before_filter :require_non_empty_project
9 9
10 before_filter :ref 10 before_filter :ref
11 - before_filter :define_tree_vars, :only => [:tree, :blob] 11 + before_filter :define_tree_vars, :only => [:tree, :blob, :blame]
12 before_filter :render_full_content 12 before_filter :render_full_content
13 13
14 layout "project" 14 layout "project"
@@ -62,6 +62,10 @@ class RefsController < ApplicationController @@ -62,6 +62,10 @@ class RefsController < ApplicationController
62 return render_404 62 return render_404
63 end 63 end
64 64
  65 + def blame
  66 + @blame = Grit::Blob.blame(@repo, @commit.id, params[:path])
  67 + end
  68 +
65 protected 69 protected
66 70
67 def define_tree_vars 71 def define_tree_vars
app/views/refs/_head.html.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +%ul.nav.nav-tabs
  2 + %li
  3 + = form_tag switch_project_refs_path(@project), :method => :get, :class => "project-refs-form", :remote => true do
  4 + = select_tag "ref", grouped_options_refs, :onchange => "$(this.form).trigger('submit');", :class => "project-refs-select"
  5 + = hidden_field_tag :destination, "tree"
  6 + = hidden_field_tag :path, params[:path]
  7 + %li{:class => "#{'active' if (controller.controller_name == "refs") }"}
  8 + = link_to tree_project_ref_path(@project, @ref) do
  9 + Code
  10 +
app/views/refs/_tree_file.html.haml
1 -:css  
2 .view_file 1 .view_file
3 .view_file_header 2 .view_file_header
4 %i.icon-file 3 %i.icon-file
5 %span.file_name 4 %span.file_name
6 = name 5 = name
7 %small #{file.mode} 6 %small #{file.mode}
8 - %span.right  
9 - = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "right", :target => "_blank"  
10 - = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "right", :style => "margin-right:10px;" 7 + %span.options
  8 + = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank"
  9 + = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small"
  10 + = link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
11 - if file.text? 11 - if file.text?
12 - if name =~ /\.(md|markdown)$/i 12 - if name =~ /\.(md|markdown)$/i
13 #tree-readme-holder 13 #tree-readme-holder
app/views/refs/blame.html.haml 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 += render "head"
  2 +
  3 +#tree-holder
  4 + %ul.breadcrumb
  5 + %li
  6 + %span.arrow
  7 + = link_to tree_project_ref_path(@project, @ref, :path => nil) do
  8 + = @project.name
  9 + - @tree.breadcrumbs(6) do |link|
  10 + \/
  11 + %li= link
  12 + .clear
  13 +
  14 + .view_file.blame_file
  15 + .view_file_header
  16 + %i.icon-file
  17 + %span.file_name
  18 + = @tree.name
  19 + %small blame
  20 + %span.options
  21 + = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank"
  22 + = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small"
  23 + = link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
  24 + .view_file_content
  25 + %table
  26 + - @blame.each do |commit, lines|
  27 + - commit = Commit.new(commit)
  28 + %tr
  29 + %td.author
  30 + = image_tag gravatar_icon(commit.author_email, 16)
  31 + = commit.author_name
  32 + %td.commit
  33 +  
  34 + = link_to project_commit_path(@project, :id => commit.id) do
  35 + %code= commit.id.to_s[0..10]
  36 + %span.row_title= truncate(commit.safe_message, :length => 30) rescue "--broken encoding"
  37 + %td.lines
  38 + = preserve do
  39 + %pre
  40 + - lines.each do |line|
  41 + = line
  42 +
  43 +:javascript
  44 + $(function(){
  45 + $('.project-refs-select').chosen();
  46 + });
app/views/refs/tree.html.haml
1 -%ul.nav.nav-tabs  
2 - %li  
3 - = form_tag switch_project_refs_path(@project), :method => :get, :class => "project-refs-form", :remote => true do  
4 - = select_tag "ref", grouped_options_refs, :onchange => "$(this.form).trigger('submit');", :class => "project-refs-select"  
5 - = hidden_field_tag :destination, "tree"  
6 - = hidden_field_tag :path, params[:path]  
7 - %li{:class => "#{'active' if (controller.controller_name == "refs") }"}  
8 - = link_to tree_project_ref_path(@project, @ref) do  
9 - Code  
10 - 1 += render "head"
11 #tree-holder= render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree} 2 #tree-holder= render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}
12 3
13 :javascript 4 :javascript
config/routes.rb
@@ -94,6 +94,14 @@ Gitlab::Application.routes.draw do @@ -94,6 +94,14 @@ Gitlab::Application.routes.draw do
94 :id => /[a-zA-Z.0-9\/_\-]+/, 94 :id => /[a-zA-Z.0-9\/_\-]+/,
95 :path => /.*/ 95 :path => /.*/
96 } 96 }
  97 +
  98 + # blame
  99 + get "blame/:path" => "refs#blame",
  100 + :as => :blame_file,
  101 + :constraints => {
  102 + :id => /[a-zA-Z.0-9\/_\-]+/,
  103 + :path => /.*/
  104 + }
97 end 105 end
98 end 106 end
99 107
spec/requests/file_blame_spec.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Blame file" do
  4 + before { login_as :user }
  5 +
  6 + describe "GET /:projectname/:commit/blob/Gemfile" do
  7 + before do
  8 + @project = Factory :project
  9 + @project.add_access(@user, :read)
  10 +
  11 + visit tree_project_ref_path(@project, @project.root_ref, :path => "Gemfile")
  12 + click_link "blame"
  13 + end
  14 +
  15 + it "should be correct path" do
  16 + current_path.should == blame_file_project_ref_path(@project, @project.root_ref, :path => "Gemfile")
  17 + end
  18 +
  19 + it "should contain file view" do
  20 + page.should have_content("rubygems.org")
  21 + page.should have_content("Dmitriy Zaporozhets")
  22 + page.should have_content("bc3735004cb Moving to rails 3.2")
  23 + end
  24 + end
  25 +end