Commit 4bbe2b74a85793c06679851706633cbee87ff8ca

Authored by Dmitriy Zaporozhets
2 parents 7af16bbb dfeef6c2

Merge branch 'feature/merge_request_serialize'

app/controllers/blame_controller.rb
@@ -8,6 +8,7 @@ class BlameController < ProjectResourceController @@ -8,6 +8,7 @@ class BlameController < ProjectResourceController
8 before_filter :require_non_empty_project 8 before_filter :require_non_empty_project
9 9
10 def show 10 def show
  11 + @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
11 @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path) 12 @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
12 end 13 end
13 end 14 end
app/controllers/blob_controller.rb
@@ -8,15 +8,6 @@ class BlobController < ProjectResourceController @@ -8,15 +8,6 @@ class BlobController < ProjectResourceController
8 before_filter :require_non_empty_project 8 before_filter :require_non_empty_project
9 9
10 def show 10 def show
11 - if @tree.is_blob?  
12 - send_data(  
13 - @tree.data,  
14 - type: @tree.mime_type,  
15 - disposition: 'inline',  
16 - filename: @tree.name  
17 - )  
18 - else  
19 - not_found!  
20 - end 11 + @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
21 end 12 end
22 end 13 end
app/controllers/raw_controller.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +# Controller for viewing a file's raw
  2 +class RawController < ProjectResourceController
  3 + include ExtractsPath
  4 +
  5 + # Authorize
  6 + before_filter :authorize_read_project!
  7 + before_filter :authorize_code_access!
  8 + before_filter :require_non_empty_project
  9 +
  10 + def show
  11 + @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
  12 +
  13 + if @blob.exists?
  14 + send_data(
  15 + @blob.data,
  16 + type: @blob.mime_type,
  17 + disposition: 'inline',
  18 + filename: @blob.name
  19 + )
  20 + else
  21 + not_found!
  22 + end
  23 + end
  24 +end
  25 +
app/controllers/refs_controller.rb
@@ -30,7 +30,7 @@ class RefsController &lt; ProjectResourceController @@ -30,7 +30,7 @@ class RefsController &lt; ProjectResourceController
30 end 30 end
31 31
32 def logs_tree 32 def logs_tree
33 - contents = @tree.contents 33 + contents = @tree.entries
34 @logs = contents.map do |content| 34 @logs = contents.map do |content|
35 file = params[:path] ? File.join(params[:path], content.name) : content.name 35 file = params[:path] ? File.join(params[:path], content.name) : content.name
36 last_commit = @repo.commits(@commit.id, file, 1).last 36 last_commit = @repo.commits(@commit.id, file, 1).last
@@ -48,7 +48,7 @@ class RefsController &lt; ProjectResourceController @@ -48,7 +48,7 @@ class RefsController &lt; ProjectResourceController
48 48
49 @repo = project.repository 49 @repo = project.repository
50 @commit = @repo.commit(@ref) 50 @commit = @repo.commit(@ref)
51 - @tree = Tree.new(@commit.tree, @ref, params[:path]) 51 + @tree = Tree.new(@repo, @commit.id, @ref, params[:path])
52 @hex_path = Digest::SHA1.hexdigest(params[:path] || "") 52 @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
53 53
54 if params[:path] 54 if params[:path]
app/helpers/tree_helper.rb
@@ -3,9 +3,9 @@ module TreeHelper @@ -3,9 +3,9 @@ module TreeHelper
3 # their corresponding partials 3 # their corresponding partials
4 # 4 #
5 # contents - A Grit::Tree object for the current tree 5 # contents - A Grit::Tree object for the current tree
6 - def render_tree(contents) 6 + def render_tree(tree)
7 # Render Folders before Files/Submodules 7 # Render Folders before Files/Submodules
8 - folders, files = contents.partition { |v| v.kind_of?(Grit::Tree) } 8 + folders, files = tree.trees, tree.blobs
9 9
10 tree = "" 10 tree = ""
11 11
@@ -18,7 +18,7 @@ module TreeHelper @@ -18,7 +18,7 @@ module TreeHelper
18 render partial: 'tree/submodule_item', object: f 18 render partial: 'tree/submodule_item', object: f
19 else 19 else
20 # Object is a Blob 20 # Object is a Blob
21 - render partial: 'tree/tree_item', object: f, locals: {type: 'file'} 21 + render partial: 'tree/blob_item', object: f, locals: {type: 'file'}
22 end 22 end
23 23
24 tree += html if html.present? 24 tree += html if html.present?
@@ -91,5 +91,4 @@ module TreeHelper @@ -91,5 +91,4 @@ module TreeHelper
91 file = File.join(tree.path, "..") 91 file = File.join(tree.path, "..")
92 tree_join(tree.ref, file) 92 tree_join(tree.ref, file)
93 end 93 end
94 -  
95 end 94 end
app/models/merge_request.rb
@@ -152,17 +152,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -152,17 +152,7 @@ class MergeRequest &lt; ActiveRecord::Base
152 end 152 end
153 153
154 def commits 154 def commits
155 - if st_commits.present?  
156 - # check if merge request commits are valid  
157 - if st_commits.first.respond_to?(:short_id)  
158 - st_commits  
159 - else  
160 - # if commits are invalid - simply reload it from repo  
161 - reloaded_commits  
162 - end  
163 - else  
164 - []  
165 - end 155 + load_commits(st_commits || [])
166 end 156 end
167 157
168 def probably_merged? 158 def probably_merged?
@@ -172,13 +162,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -172,13 +162,7 @@ class MergeRequest &lt; ActiveRecord::Base
172 162
173 def reloaded_commits 163 def reloaded_commits
174 if opened? && unmerged_commits.any? 164 if opened? && unmerged_commits.any?
175 - # we need to reset st_commits field first  
176 - # in order to prevent internal rails comparison  
177 - self.st_commits = []  
178 - save  
179 -  
180 - # Then we can safely write unmerged commits  
181 - self.st_commits = unmerged_commits 165 + self.st_commits = dump_commits(unmerged_commits)
182 save 166 save
183 end 167 end
184 commits 168 commits
@@ -228,4 +212,14 @@ class MergeRequest &lt; ActiveRecord::Base @@ -228,4 +212,14 @@ class MergeRequest &lt; ActiveRecord::Base
228 def last_commit_short_sha 212 def last_commit_short_sha
229 @last_commit_short_sha ||= last_commit.sha[0..10] 213 @last_commit_short_sha ||= last_commit.sha[0..10]
230 end 214 end
  215 +
  216 + private
  217 +
  218 + def dump_commits(commits)
  219 + commits.map(&:to_hash)
  220 + end
  221 +
  222 + def load_commits(array)
  223 + array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash)) }
  224 + end
231 end 225 end
app/models/tree.rb
1 class Tree 1 class Tree
2 - include Linguist::BlobHelper 2 + attr_accessor :raw
3 3
4 - attr_accessor :path, :tree, :ref  
5 -  
6 - delegate :contents, :basename, :name, :data, :mime_type,  
7 - :mode, :size, :text?, :colorize, to: :tree  
8 -  
9 - def initialize(raw_tree, ref = nil, path = nil)  
10 - @ref, @path = ref, path  
11 - @tree = if path.present?  
12 - raw_tree / path  
13 - else  
14 - raw_tree  
15 - end  
16 - end  
17 -  
18 - def is_blob?  
19 - tree.is_a?(Grit::Blob) 4 + def initialize(repository, sha, ref = nil, path = nil)
  5 + @raw = Gitlab::Git::Tree.new(repository, sha, ref, path)
20 end 6 end
21 7
22 - def invalid?  
23 - tree.nil? 8 + def method_missing(m, *args, &block)
  9 + @raw.send(m, *args, &block)
24 end 10 end
25 11
26 - def empty?  
27 - data.blank?  
28 - end  
29 -  
30 - def up_dir?  
31 - path.present?  
32 - end 12 + def respond_to?(method)
  13 + return true if @raw.respond_to?(method)
33 14
34 - def readme  
35 - @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i } 15 + super
36 end 16 end
37 end 17 end
app/services/git_push_service.rb
@@ -104,7 +104,7 @@ class GitPushService @@ -104,7 +104,7 @@ class GitPushService
104 data[:commits] << { 104 data[:commits] << {
105 id: commit.id, 105 id: commit.id,
106 message: commit.safe_message, 106 message: commit.safe_message,
107 - timestamp: commit.date.xmlschema, 107 + timestamp: commit.committed_date.xmlschema,
108 url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}", 108 url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}",
109 author: { 109 author: {
110 name: commit.author_name, 110 name: commit.author_name,
app/views/blame/show.html.haml
@@ -15,9 +15,9 @@ @@ -15,9 +15,9 @@
15 .file_title 15 .file_title
16 %i.icon-file 16 %i.icon-file
17 %span.file_name 17 %span.file_name
18 - = @tree.name  
19 - %small= number_to_human_size @tree.size  
20 - %span.options= render "tree/blob_actions" 18 + = @blob.name
  19 + %small= number_to_human_size @blob.size
  20 + %span.options= render "blob/actions"
21 .file_content.blame 21 .file_content.blame
22 %table 22 %table
23 - current_line = 1 23 - current_line = 1
app/views/blob/_actions.html.haml 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +.btn-group.tree-btn-group
  2 + -# only show edit link for text files
  3 + - if @blob.text?
  4 + = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit?
  5 + = link_to "raw", project_raw_path(@project, @id), class: "btn btn-tiny", target: "_blank"
  6 + -# only show normal/blame view links for text files
  7 + - if @blob.text?
  8 + - if current_page? project_blame_path(@project, @id)
  9 + = link_to "normal view", project_blob_path(@project, @id), class: "btn btn-tiny"
  10 + - else
  11 + = link_to "blame", project_blame_path(@project, @id), class: "btn btn-tiny"
  12 + = link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny"
app/views/blob/_blob.html.haml 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +%ul.breadcrumb
  2 + %li
  3 + %i.icon-angle-right
  4 + = link_to project_tree_path(@project, @ref) do
  5 + = @project.path
  6 + - tree_breadcrumbs(@tree, 6) do |title, path|
  7 + \/
  8 + %li
  9 + - if path
  10 + = link_to truncate(title, length: 40), project_tree_path(@project, path)
  11 + - else
  12 + = link_to title, '#'
  13 +
  14 +%div#tree-content-holder.tree-content-holder
  15 + .file_holder
  16 + .file_title
  17 + %i.icon-file
  18 + %span.file_name
  19 + = blob.name
  20 + %small= number_to_human_size blob.size
  21 + %span.options= render "actions"
  22 + - if blob.text?
  23 + = render "text", blob: blob
  24 + - elsif blob.image?
  25 + = render "image", blob: blob
  26 + - else
  27 + = render "download", blob: blob
app/views/blob/_download.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +.file_content.blob_file
  2 + %center
  3 + = link_to project_blob_path(@project, @id) do
  4 + %div.padded
  5 + %h4
  6 + %i.icon-download-alt
  7 + %br
  8 + Download (#{number_to_human_size blob.size})
app/views/blob/_image.html.haml 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +.file_content.image_file
  2 + %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}
app/views/blob/_text.html.haml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +- if gitlab_markdown?(blob.name)
  2 + .file_content.wiki
  3 + = preserve do
  4 + = markdown(blob.data)
  5 +- elsif markup?(blob.name)
  6 + .file_content.wiki
  7 + = raw GitHub::Markup.render(blob.name, blob.data)
  8 +- else
  9 + .file_content.code
  10 + - unless blob.empty?
  11 + %div{class: user_color_scheme_class}
  12 + = raw blob.colorize(formatter: :gitlab)
  13 + - else
  14 + %p.nothing_here_message Empty file
app/views/blob/show.html.haml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +%div.tree-ref-holder
  2 + = render 'shared/ref_switcher', destination: 'tree', path: @path
  3 +%div#tree-holder.tree-holder
  4 + = render 'blob', blob: @blob
app/views/blob/show.js.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +:plain
  2 + // Load Files list
  3 + $("#tree-holder").html("#{escape_javascript(render(partial: "blob", locals: {blob: @blob}))}");
  4 + $("#tree-content-holder").show("slide", { direction: "right" }, 400);
  5 + $('.project-refs-form #path').val("#{@path}");
  6 +
  7 + // Load last commit log for each file in tree
  8 + $('#tree-slider').waitForImages(function() {
  9 + ajaxGet('#{@logs_path}');
  10 + });
app/views/commits/_diffs.html.haml
@@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
16 - unless @suppress_diff 16 - unless @suppress_diff
17 - diffs.each_with_index do |diff, i| 17 - diffs.each_with_index do |diff, i|
18 - next if diff.diff.empty? 18 - next if diff.diff.empty?
19 - - file = (@commit.tree / diff.new_path)  
20 - - file = (@commit.prev_commit.tree / diff.old_path) unless file 19 + - file = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, diff.new_path)
  20 + - file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) unless file.exists?
21 - next unless file 21 - next unless file
22 .file{id: "diff-#{i}"} 22 .file{id: "diff-#{i}"}
23 .header 23 .header
24 - if diff.deleted_file 24 - if diff.deleted_file
25 %span= diff.old_path 25 %span= diff.old_path
26 26
27 - - if @commit.prev_commit  
28 - = link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do 27 + - if @commit.parent_ids.present?
  28 + = link_to project_tree_path(@project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
29 View file @ 29 View file @
30 %span.commit-short-id= @commit.short_id(6) 30 %span.commit-short-id= @commit.short_id(6)
31 - else 31 - else
@@ -43,7 +43,7 @@ @@ -43,7 +43,7 @@
43 - if file.text? 43 - if file.text?
44 = render "commits/text_file", diff: diff, index: i 44 = render "commits/text_file", diff: diff, index: i
45 - elsif file.image? 45 - elsif file.image?
46 - - old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil? 46 + - old_file = Gitlab::Git::Blob.new(@repository, @commit.parent_id, @ref, diff.old_path) if @commit.parent_id
47 = render "commits/image", diff: diff, old_file: old_file, file: file, index: i 47 = render "commits/image", diff: diff, old_file: old_file, file: file, index: i
48 - else 48 - else
49 %p.nothing_here_message No preview for this file type 49 %p.nothing_here_message No preview for this file type
app/views/graph/show.json.erb
@@ -7,9 +7,9 @@ @@ -7,9 +7,9 @@
7 { 7 {
8 parents: parents_zip_spaces(c.parents(@graph.map), c.parent_spaces), 8 parents: parents_zip_spaces(c.parents(@graph.map), c.parent_spaces),
9 author: { 9 author: {
10 - name: c.author.name,  
11 - email: c.author.email,  
12 - icon: gravatar_icon(c.author.email, 20) 10 + name: c.author_name,
  11 + email: c.author_email,
  12 + icon: gravatar_icon(c.author_email, 20)
13 }, 13 },
14 time: c.time, 14 time: c.time,
15 space: c.spaces.first, 15 space: c.spaces.first,
app/views/tree/_blob.html.haml
@@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
1 -.file_holder  
2 - .file_title  
3 - %i.icon-file  
4 - %span.file_name  
5 - = blob.name  
6 - %small= number_to_human_size blob.size  
7 - %span.options= render "tree/blob_actions"  
8 - - if blob.text?  
9 - = render "tree/blob/text", blob: blob  
10 - - elsif blob.image?  
11 - = render "tree/blob/image", blob: blob  
12 - - else  
13 - = render "tree/blob/download", blob: blob  
app/views/tree/_blob_actions.html.haml
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -.btn-group.tree-btn-group  
2 - -# only show edit link for text files  
3 - - if @tree.text?  
4 - = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-tiny", disabled: !allowed_tree_edit?  
5 - = link_to "raw", project_blob_path(@project, @id), class: "btn btn-tiny", target: "_blank"  
6 - -# only show normal/blame view links for text files  
7 - - if @tree.text?  
8 - - if current_page? project_blame_path(@project, @id)  
9 - = link_to "normal view", project_tree_path(@project, @id), class: "btn btn-tiny"  
10 - - else  
11 - = link_to "blame", project_blame_path(@project, @id), class: "btn btn-tiny"  
12 - = link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny"  
app/views/tree/_blob_item.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
  2 + %td.tree-item-file-name
  3 + = tree_icon(type)
  4 + %strong= link_to truncate(blob_item.name, length: 40), project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
  5 + %td.tree_time_ago.cgray
  6 + %span.log_loading.hide
  7 + Loading commit data...
  8 + = image_tag "ajax_loader_tree.gif", width: 14
  9 + %td.tree_commit{ colspan: 2 }
app/views/tree/_tree.html.haml
@@ -12,36 +12,32 @@ @@ -12,36 +12,32 @@
12 = link_to title, '#' 12 = link_to title, '#'
13 13
14 %div#tree-content-holder.tree-content-holder 14 %div#tree-content-holder.tree-content-holder
15 - - if tree.is_blob?  
16 - = render "tree/blob", blob: tree  
17 - - else  
18 - %table#tree-slider{class: "table_#{@hex_path} tree-table" }  
19 - %thead  
20 - %tr  
21 - %th Name  
22 - %th Last Update  
23 - %th Last Commit  
24 - %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right" 15 + %table#tree-slider{class: "table_#{@hex_path} tree-table" }
  16 + %thead
  17 + %tr
  18 + %th Name
  19 + %th Last Update
  20 + %th Last Commit
  21 + %th= link_to "history", project_commits_path(@project, @id), class: "btn btn-tiny pull-right"
25 22
26 - - if tree.up_dir?  
27 - %tr.tree-item  
28 - %td.tree-item-file-name  
29 - = image_tag "file_empty.png", size: '16x16'  
30 - = link_to "..", project_tree_path(@project, up_dir_path(tree))  
31 - %td  
32 - %td  
33 - %td 23 + - if tree.up_dir?
  24 + %tr.tree-item
  25 + %td.tree-item-file-name
  26 + = image_tag "file_empty.png", size: '16x16'
  27 + = link_to "..", project_tree_path(@project, up_dir_path(tree))
  28 + %td
  29 + %td
  30 + %td
34 31
35 - = render_tree(tree.contents) 32 + = render_tree(tree)
36 33
37 - - if tree.readme  
38 - = render "tree/readme", readme: tree.readme 34 + - if tree.readme
  35 + = render "tree/readme", readme: tree.readme
39 36
40 %div.tree_progress 37 %div.tree_progress
41 38
42 -- unless tree.is_blob?  
43 - :javascript  
44 - // Load last commit log for each file in tree  
45 - $(window).load(function(){  
46 - ajaxGet('#{@logs_path}');  
47 - }); 39 +:javascript
  40 + // Load last commit log for each file in tree
  41 + $(window).load(function(){
  42 + ajaxGet('#{@logs_path}');
  43 + });
app/views/tree/blob/_download.html.haml
@@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
1 -.file_content.blob_file  
2 - %center  
3 - = link_to project_blob_path(@project, @id) do  
4 - %div.padded  
5 - %h4  
6 - %i.icon-download-alt  
7 - %br  
8 - Download (#{number_to_human_size blob.size})  
app/views/tree/blob/_image.html.haml
@@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
1 -.file_content.image_file  
2 - %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}  
app/views/tree/blob/_text.html.haml
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -- if gitlab_markdown?(blob.name)  
2 - .file_content.wiki  
3 - = preserve do  
4 - = markdown(blob.data)  
5 -- elsif markup?(blob.name)  
6 - .file_content.wiki  
7 - = raw GitHub::Markup.render(blob.name, blob.data)  
8 -- else  
9 - .file_content.code  
10 - - unless blob.empty?  
11 - %div{class: user_color_scheme_class}  
12 - = raw blob.colorize(formatter: :gitlab)  
13 - - else  
14 - %p.nothing_here_message Empty file  
app/workers/post_receive.rb
@@ -23,7 +23,7 @@ class PostReceive @@ -23,7 +23,7 @@ class PostReceive
23 23
24 user = if identifier.blank? 24 user = if identifier.blank?
25 # Local push from gitlab 25 # Local push from gitlab
26 - email = project.repository.commit(newrev).author.email rescue nil 26 + email = project.repository.commit(newrev).author_email rescue nil
27 User.find_by_email(email) if email 27 User.find_by_email(email) if email
28 28
29 elsif identifier =~ /\Auser-\d+\Z/ 29 elsif identifier =~ /\Auser-\d+\Z/
config/routes.rb
@@ -170,6 +170,7 @@ Gitlab::Application.routes.draw do @@ -170,6 +170,7 @@ Gitlab::Application.routes.draw do
170 end 170 end
171 171
172 resources :blob, only: [:show], constraints: {id: /.+/} 172 resources :blob, only: [:show], constraints: {id: /.+/}
  173 + resources :raw, only: [:show], constraints: {id: /.+/}
173 resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } 174 resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
174 resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' 175 resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
175 resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} 176 resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
features/steps/shared/paths.rb
@@ -205,7 +205,7 @@ module SharedPaths @@ -205,7 +205,7 @@ module SharedPaths
205 end 205 end
206 206
207 Given 'I visit blob file from repo' do 207 Given 'I visit blob file from repo' do
208 - visit project_tree_path(@project, File.join(ValidCommit::ID, ValidCommit::BLOB_FILE_PATH)) 208 + visit project_blob_path(@project, File.join(ValidCommit::ID, ValidCommit::BLOB_FILE_PATH))
209 end 209 end
210 210
211 Given 'I visit project source page for "8470d70"' do 211 Given 'I visit project source page for "8470d70"' do
lib/api/projects.rb
@@ -493,14 +493,16 @@ module Gitlab @@ -493,14 +493,16 @@ module Gitlab
493 493
494 ref = params[:sha] 494 ref = params[:sha]
495 495
496 - commit = user_project.repository.commit ref 496 + repo = user_project.repository
  497 +
  498 + commit = repo.commit(ref)
497 not_found! "Commit" unless commit 499 not_found! "Commit" unless commit
498 500
499 - tree = Tree.new commit.tree, ref, params[:filepath]  
500 - not_found! "File" unless tree.try(:tree) 501 + blob = Gitlab::Git::Blob.new(repo, commit.id, ref, params[:filepath])
  502 + not_found! "File" unless blob.exists?
501 503
502 - content_type tree.mime_type  
503 - present tree.data 504 + content_type blob.mime_type
  505 + present blob.data
504 end 506 end
505 507
506 # Get a specific project's keys 508 # Get a specific project's keys
lib/extracts_path.rb
@@ -102,9 +102,9 @@ module ExtractsPath @@ -102,9 +102,9 @@ module ExtractsPath
102 # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name. 102 # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name.
103 @commit = @project.repository.commits(@ref, @path, 1, 0).first 103 @commit = @project.repository.commits(@ref, @path, 1, 0).first
104 104
105 - @tree = Tree.new(@commit.tree, @ref, @path) 105 + @tree = Tree.new(@project.repository, @commit.id, @ref, @path)
106 106
107 - raise InvalidPathError if @tree.invalid? 107 + raise InvalidPathError unless @tree.exists?
108 rescue RuntimeError, NoMethodError, InvalidPathError 108 rescue RuntimeError, NoMethodError, InvalidPathError
109 not_found! 109 not_found!
110 end 110 end
lib/gitlab/git/blob.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +module Gitlab
  2 + module Git
  3 + class Blob
  4 + include Linguist::BlobHelper
  5 +
  6 + attr_accessor :raw_blob
  7 +
  8 + delegate :name, to: :raw_blob
  9 +
  10 + def initialize(repository, sha, ref, path)
  11 + @repository, @sha, @ref = repository, sha, ref
  12 +
  13 + @commit = @repository.commit(sha)
  14 + @raw_blob = @repository.tree(@commit, path)
  15 + end
  16 +
  17 + def data
  18 + if raw_blob
  19 + raw_blob.data
  20 + else
  21 + nil
  22 + end
  23 + end
  24 +
  25 + def exists?
  26 + raw_blob
  27 + end
  28 +
  29 + def empty?
  30 + data.blank?
  31 + end
  32 +
  33 + def mode
  34 + raw_blob.mode
  35 + end
  36 +
  37 + def size
  38 + raw_blob.size
  39 + end
  40 + end
  41 + end
  42 +end
lib/gitlab/git/commit.rb
@@ -4,13 +4,19 @@ @@ -4,13 +4,19 @@
4 module Gitlab 4 module Gitlab
5 module Git 5 module Git
6 class Commit 6 class Commit
7 - attr_accessor :raw_commit, :head, :refs 7 + attr_accessor :raw_commit, :head, :refs,
  8 + :id, :authored_date, :committed_date, :message,
  9 + :author_name, :author_email, :parent_ids,
  10 + :committer_name, :committer_email
8 11
9 - delegate :message, :authored_date, :committed_date, :parents, :sha,  
10 - :date, :committer, :author, :diffs, :tree, :id, :stats, :to_patch, 12 + delegate :parents, :diffs, :tree, :stats, :to_patch,
11 to: :raw_commit 13 to: :raw_commit
12 14
13 class << self 15 class << self
  16 + def serialize_keys
  17 + %w(id authored_date committed_date author_name author_email committer_name committer_email message parent_ids)
  18 + end
  19 +
14 def find_or_first(repo, commit_id = nil, root_ref) 20 def find_or_first(repo, commit_id = nil, root_ref)
15 commit = if commit_id 21 commit = if commit_id
16 repo.commit(commit_id) 22 repo.commit(commit_id)
@@ -73,10 +79,19 @@ module Gitlab @@ -73,10 +79,19 @@ module Gitlab
73 def initialize(raw_commit, head = nil) 79 def initialize(raw_commit, head = nil)
74 raise "Nil as raw commit passed" unless raw_commit 80 raise "Nil as raw commit passed" unless raw_commit
75 81
76 - @raw_commit = raw_commit 82 + if raw_commit.is_a?(Hash)
  83 + init_from_hash(raw_commit)
  84 + else
  85 + init_from_grit(raw_commit)
  86 + end
  87 +
77 @head = head 88 @head = head
78 end 89 end
79 90
  91 + def sha
  92 + id
  93 + end
  94 +
80 def short_id(length = 10) 95 def short_id(length = 10)
81 id.to_s[0..length] 96 id.to_s[0..length]
82 end 97 end
@@ -89,37 +104,13 @@ module Gitlab @@ -89,37 +104,13 @@ module Gitlab
89 committed_date 104 committed_date
90 end 105 end
91 106
92 - def author_email  
93 - author.email  
94 - end  
95 -  
96 - def author_name  
97 - author.name  
98 - end  
99 -  
100 # Was this commit committed by a different person than the original author? 107 # Was this commit committed by a different person than the original author?
101 def different_committer? 108 def different_committer?
102 author_name != committer_name || author_email != committer_email 109 author_name != committer_name || author_email != committer_email
103 end 110 end
104 111
105 - def committer_name  
106 - committer.name  
107 - end  
108 -  
109 - def committer_email  
110 - committer.email  
111 - end  
112 -  
113 - def prev_commit  
114 - @prev_commit ||= if parents.present?  
115 - Commit.new(parents.first)  
116 - else  
117 - nil  
118 - end  
119 - end  
120 -  
121 - def prev_commit_id  
122 - prev_commit.try :id 112 + def parent_id
  113 + parent_ids.first
123 end 114 end
124 115
125 # Shows the diff between the commit's parent and the commit. 116 # Shows the diff between the commit's parent and the commit.
@@ -148,6 +139,43 @@ module Gitlab @@ -148,6 +139,43 @@ module Gitlab
148 def no_commit_message 139 def no_commit_message
149 "--no commit message" 140 "--no commit message"
150 end 141 end
  142 +
  143 + def to_hash
  144 + hash = {}
  145 +
  146 + keys = Commit.serialize_keys
  147 +
  148 + keys.each do |key|
  149 + hash[key] = send(key)
  150 + end
  151 +
  152 + hash
  153 + end
  154 +
  155 + def date
  156 + committed_date
  157 + end
  158 +
  159 + private
  160 +
  161 + def init_from_grit(grit)
  162 + @raw_commit = grit
  163 + @id = grit.id
  164 + @message = grit.message
  165 + @authored_date = grit.authored_date
  166 + @committed_date = grit.committed_date
  167 + @author_name = grit.author.name
  168 + @author_email = grit.author.email
  169 + @committer_name = grit.committer.name
  170 + @committer_email = grit.committer.email
  171 + @parent_ids = grit.parents.map(&:id)
  172 + end
  173 +
  174 + def init_from_hash(hash)
  175 + Commit.serialize_keys.each do |key|
  176 + send(:"#{key}=", hash[key])
  177 + end
  178 + end
151 end 179 end
152 end 180 end
153 end 181 end
lib/gitlab/git/compare.rb
@@ -20,10 +20,8 @@ module Gitlab @@ -20,10 +20,8 @@ module Gitlab
20 return 20 return
21 end 21 end
22 22
23 - @commit = Commit.new(first)  
24 - 23 + @commit = first
25 @commits = repository.commits_between(last.id, first.id) 24 @commits = repository.commits_between(last.id, first.id)
26 - @commits = @commits.map { |c| Commit.new(c) }  
27 25
28 @diffs = if @commits.size > 100 26 @diffs = if @commits.size > 100
29 [] 27 []
lib/gitlab/git/tree.rb 0 → 100644
@@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
  1 +module Gitlab
  2 + module Git
  3 + class Tree
  4 + attr_accessor :repository, :sha, :path, :ref, :raw_tree, :id
  5 +
  6 + def initialize(repository, sha, ref = nil, path = nil)
  7 + @repository, @sha, @ref, @path = repository, sha, ref, path
  8 +
  9 + @path = nil if @path.blank?
  10 +
  11 + # Load tree from repository
  12 + @commit = @repository.commit(@sha)
  13 + @raw_tree = @repository.tree(@commit, @path)
  14 + end
  15 +
  16 + def exists?
  17 + raw_tree
  18 + end
  19 +
  20 + def empty?
  21 + data.blank?
  22 + end
  23 +
  24 + def trees
  25 + entries.select { |t| t.is_a?(Grit::Tree) }
  26 + end
  27 +
  28 + def blobs
  29 + entries.select { |t| t.is_a?(Grit::Blob) }
  30 + end
  31 +
  32 + def is_blob?
  33 + raw_tree.is_a?(Grit::Blob)
  34 + end
  35 +
  36 + def up_dir?
  37 + path.present?
  38 + end
  39 +
  40 + def readme
  41 + @readme ||= entries.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
  42 + end
  43 +
  44 + protected
  45 +
  46 + def entries
  47 + raw_tree.contents
  48 + end
  49 + end
  50 + end
  51 +end
  52 +
spec/controllers/blob_controller_spec.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +require 'spec_helper'
  2 +
  3 +describe BlobController do
  4 + let(:project) { create(:project_with_code) }
  5 + let(:user) { create(:user) }
  6 +
  7 + before do
  8 + sign_in(user)
  9 +
  10 + project.team << [user, :master]
  11 +
  12 + project.stub(:branches).and_return(['master', 'foo/bar/baz'])
  13 + project.stub(:tags).and_return(['v1.0.0', 'v2.0.0'])
  14 + controller.instance_variable_set(:@project, project)
  15 + end
  16 +
  17 + describe "GET show" do
  18 + render_views
  19 +
  20 + before { get :show, project_id: project.code, id: id }
  21 +
  22 + context "valid branch, valid file" do
  23 + let(:id) { 'master/README.md' }
  24 + it { should respond_with(:success) }
  25 + end
  26 +
  27 + context "valid branch, invalid file" do
  28 + let(:id) { 'master/invalid-path.rb' }
  29 + it { should respond_with(:not_found) }
  30 + end
  31 +
  32 + context "invalid branch, valid file" do
  33 + let(:id) { 'invalid-branch/README.md' }
  34 + it { should respond_with(:not_found) }
  35 + end
  36 + end
  37 +end
spec/controllers/tree_controller_spec.rb
@@ -26,17 +26,17 @@ describe TreeController do @@ -26,17 +26,17 @@ describe TreeController do
26 end 26 end
27 27
28 context "valid branch, valid path" do 28 context "valid branch, valid path" do
29 - let(:id) { 'master/README.md' } 29 + let(:id) { 'master/app/' }
30 it { should respond_with(:success) } 30 it { should respond_with(:success) }
31 end 31 end
32 32
33 context "valid branch, invalid path" do 33 context "valid branch, invalid path" do
34 - let(:id) { 'master/invalid-path.rb' } 34 + let(:id) { 'master/invalid-path/' }
35 it { should respond_with(:not_found) } 35 it { should respond_with(:not_found) }
36 end 36 end
37 37
38 context "invalid branch, valid path" do 38 context "invalid branch, valid path" do
39 - let(:id) { 'invalid-branch/README.md' } 39 + let(:id) { 'invalid-branch/app/' }
40 it { should respond_with(:not_found) } 40 it { should respond_with(:not_found) }
41 end 41 end
42 end 42 end
spec/factories.rb
@@ -86,9 +86,11 @@ FactoryGirl.define do @@ -86,9 +86,11 @@ FactoryGirl.define do
86 target_branch "master" # pretend bcf03b5d~3 86 target_branch "master" # pretend bcf03b5d~3
87 source_branch "stable" # pretend bcf03b5d 87 source_branch "stable" # pretend bcf03b5d
88 st_commits do 88 st_commits do
89 - [Commit.new(project.repository.commit('bcf03b5d')),  
90 - Commit.new(project.repository.commit('bcf03b5d~1')),  
91 - Commit.new(project.repository.commit('bcf03b5d~2'))] 89 + [
  90 + project.repository.commit('bcf03b5d').to_hash,
  91 + project.repository.commit('bcf03b5d~1').to_hash,
  92 + project.repository.commit('bcf03b5d~2').to_hash
  93 + ]
92 end 94 end
93 st_diffs do 95 st_diffs do
94 project.repo.diff("bcf03b5d~3", "bcf03b5d") 96 project.repo.diff("bcf03b5d~3", "bcf03b5d")
spec/lib/git/commit_spec.rb
@@ -20,6 +20,8 @@ describe Gitlab::Git::Commit do @@ -20,6 +20,8 @@ describe Gitlab::Git::Commit do
20 author: @author, 20 author: @author,
21 committer: @committer, 21 committer: @committer,
22 committed_date: Date.yesterday, 22 committed_date: Date.yesterday,
  23 + authored_date: Date.yesterday,
  24 + parents: [],
23 message: 'Refactoring specs' 25 message: 'Refactoring specs'
24 ) 26 )
25 27
spec/models/commit_spec.rb
@@ -38,10 +38,10 @@ describe Commit do @@ -38,10 +38,10 @@ describe Commit do
38 it { should respond_to(:message) } 38 it { should respond_to(:message) }
39 it { should respond_to(:authored_date) } 39 it { should respond_to(:authored_date) }
40 it { should respond_to(:committed_date) } 40 it { should respond_to(:committed_date) }
  41 + it { should respond_to(:committer_email) }
  42 + it { should respond_to(:author_email) }
41 it { should respond_to(:parents) } 43 it { should respond_to(:parents) }
42 it { should respond_to(:date) } 44 it { should respond_to(:date) }
43 - it { should respond_to(:committer) }  
44 - it { should respond_to(:author) }  
45 it { should respond_to(:diffs) } 45 it { should respond_to(:diffs) }
46 it { should respond_to(:tree) } 46 it { should respond_to(:tree) }
47 it { should respond_to(:id) } 47 it { should respond_to(:id) }
spec/support/matchers.rb
@@ -3,7 +3,7 @@ RSpec::Matchers.define :be_valid_commit do @@ -3,7 +3,7 @@ RSpec::Matchers.define :be_valid_commit do
3 actual != nil 3 actual != nil
4 actual.id == ValidCommit::ID 4 actual.id == ValidCommit::ID
5 actual.message == ValidCommit::MESSAGE 5 actual.message == ValidCommit::MESSAGE
6 - actual.author.name == ValidCommit::AUTHOR_FULL_NAME 6 + actual.author_name == ValidCommit::AUTHOR_FULL_NAME
7 end 7 end
8 end 8 end
9 9