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 268 -webkit-border-radius: 4px;
269 269 -moz-border-radius: 4px;
270 270 border-radius: 4px;
  271 +
  272 + &.s16 {
  273 + width:16px;
  274 + }
  275 + &.s24 {
  276 + width:24px;
  277 + }
271 278 }
272 279  
273 280 img.lil_av {
... ...
app/assets/stylesheets/tree.scss
... ... @@ -53,6 +53,11 @@
53 53 padding: 9px 10px;
54 54 height:18px;
55 55  
  56 + .options {
  57 + float:right;
  58 + margin-top: -5px;
  59 + }
  60 +
56 61 .file_name {
57 62 color:$style_color;
58 63 font-size:14px;
... ... @@ -220,3 +225,27 @@
220 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 8 before_filter :require_non_empty_project
9 9  
10 10 before_filter :ref
11   - before_filter :define_tree_vars, :only => [:tree, :blob]
  11 + before_filter :define_tree_vars, :only => [:tree, :blob, :blame]
12 12 before_filter :render_full_content
13 13  
14 14 layout "project"
... ... @@ -62,6 +62,10 @@ class RefsController < ApplicationController
62 62 return render_404
63 63 end
64 64  
  65 + def blame
  66 + @blame = Grit::Blob.blame(@repo, @commit.id, params[:path])
  67 + end
  68 +
65 69 protected
66 70  
67 71 def define_tree_vars
... ...
app/views/refs/_head.html.haml 0 → 100644
... ... @@ -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 1 .view_file
3 2 .view_file_header
4 3 %i.icon-file
5 4 %span.file_name
6 5 = name
7 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 11 - if file.text?
12 12 - if name =~ /\.(md|markdown)$/i
13 13 #tree-readme-holder
... ...
app/views/refs/blame.html.haml 0 → 100644
... ... @@ -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 2 #tree-holder= render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}
12 3  
13 4 :javascript
... ...
config/routes.rb
... ... @@ -94,6 +94,14 @@ Gitlab::Application.routes.draw do
94 94 :id => /[a-zA-Z.0-9\/_\-]+/,
95 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 105 end
98 106 end
99 107  
... ...
spec/requests/file_blame_spec.rb 0 → 100644
... ... @@ -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
... ...