Commit 76a4cbe464a4472e82065c670d1190552db67cc6

Authored by Dmitriy Zaporozhets
2 parents 273741fb 8a6bf09a

Merge branch 'refactor/gitlab_git'

Showing 72 changed files with 918 additions and 836 deletions   Show diff stats
app/contexts/commit_load_context.rb
... ... @@ -12,7 +12,6 @@ class CommitLoadContext < BaseContext
12 12 commit = project.repository.commit(params[:id])
13 13  
14 14 if commit
15   - commit = CommitDecorator.decorate(commit)
16 15 line_notes = project.notes.for_commit_id(commit.id).inline
17 16  
18 17 result[:commit] = commit
... ...
app/controllers/blame_controller.rb
... ... @@ -8,7 +8,6 @@ class BlameController < ProjectResourceController
8 8 before_filter :require_non_empty_project
9 9  
10 10 def show
11   - @repo = @project.repo
12   - @blame = Grit::Blob.blame(@repo, @commit.id, @path)
  11 + @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
13 12 end
14 13 end
... ...
app/controllers/commits_controller.rb
... ... @@ -13,7 +13,6 @@ class CommitsController < ProjectResourceController
13 13 @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
14 14  
15 15 @commits = @repo.commits(@ref, @path, @limit, @offset)
16   - @commits = CommitDecorator.decorate_collection(@commits)
17 16  
18 17 respond_to do |format|
19 18 format.html # index.html.erb
... ...
app/controllers/compare_controller.rb
... ... @@ -8,15 +8,13 @@ class CompareController < ProjectResourceController
8 8 end
9 9  
10 10 def show
11   - result = Commit.compare(project, params[:from], params[:to])
  11 + compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to])
12 12  
13   - @commits = result[:commits]
14   - @commit = result[:commit]
15   - @diffs = result[:diffs]
16   - @refs_are_same = result[:same]
  13 + @commits = compare.commits
  14 + @commit = compare.commit
  15 + @diffs = compare.diffs
  16 + @refs_are_same = compare.same
17 17 @line_notes = []
18   -
19   - @commits = CommitDecorator.decorate_collection(@commits)
20 18 end
21 19  
22 20 def create
... ...
app/controllers/merge_requests_controller.rb
... ... @@ -94,12 +94,10 @@ class MergeRequestsController < ProjectResourceController
94 94  
95 95 def branch_from
96 96 @commit = @repository.commit(params[:ref])
97   - @commit = CommitDecorator.decorate(@commit)
98 97 end
99 98  
100 99 def branch_to
101 100 @commit = @repository.commit(params[:ref])
102   - @commit = CommitDecorator.decorate(@commit)
103 101 end
104 102  
105 103 def ci_status
... ... @@ -143,7 +141,6 @@ class MergeRequestsController < ProjectResourceController
143 141 # Get commits from repository
144 142 # or from cache if already merged
145 143 @commits = @merge_request.commits
146   - @commits = CommitDecorator.decorate_collection(@commits)
147 144  
148 145 @allowed_to_merge = allowed_to_merge?
149 146 @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
... ...
app/controllers/refs_controller.rb
... ... @@ -34,7 +34,6 @@ class RefsController < ProjectResourceController
34 34 @logs = contents.map do |content|
35 35 file = params[:path] ? File.join(params[:path], content.name) : content.name
36 36 last_commit = @repo.commits(@commit.id, file, 1).last
37   - last_commit = CommitDecorator.decorate(last_commit)
38 37 {
39 38 file_name: content.name,
40 39 commit: last_commit
... ... @@ -49,9 +48,7 @@ class RefsController < ProjectResourceController
49 48  
50 49 @repo = project.repository
51 50 @commit = @repo.commit(@ref)
52   - @commit = CommitDecorator.decorate(@commit)
53 51 @tree = Tree.new(@commit.tree, @ref, params[:path])
54   - @tree = TreeDecorator.new(@tree)
55 52 @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
56 53  
57 54 if params[:path]
... ...
app/decorators/commit_decorator.rb
... ... @@ -1,93 +0,0 @@
1   -class CommitDecorator < ApplicationDecorator
2   - decorates :commit
3   -
4   - # Returns a string describing the commit for use in a link title
5   - #
6   - # Example
7   - #
8   - # "Commit: Alex Denisov - Project git clone panel"
9   - def link_title
10   - "Commit: #{author_name} - #{title}"
11   - end
12   -
13   - # Returns the commits title.
14   - #
15   - # Usually, the commit title is the first line of the commit message.
16   - # In case this first line is longer than 80 characters, it is cut off
17   - # after 70 characters and ellipses (`&hellp;`) are appended.
18   - def title
19   - title = safe_message
20   -
21   - return no_commit_message if title.blank?
22   -
23   - title_end = title.index(/\n/)
24   - if (!title_end && title.length > 80) || (title_end && title_end > 80)
25   - title[0..69] << "&hellip;".html_safe
26   - else
27   - title.split(/\n/, 2).first
28   - end
29   - end
30   -
31   - # Returns the commits description
32   - #
33   - # cut off, ellipses (`&hellp;`) are prepended to the commit message.
34   - def description
35   - description = safe_message
36   -
37   - title_end = description.index(/\n/)
38   - if (!title_end && description.length > 80) || (title_end && title_end > 80)
39   - "&hellip;".html_safe << description[70..-1]
40   - else
41   - description.split(/\n/, 2)[1].try(:chomp)
42   - end
43   - end
44   -
45   - # Returns a link to the commit author. If the author has a matching user and
46   - # is a member of the current @project it will link to the team member page.
47   - # Otherwise it will link to the author email as specified in the commit.
48   - #
49   - # options:
50   - # avatar: true will prepend the avatar image
51   - # size: size of the avatar image in px
52   - def author_link(options = {})
53   - person_link(options.merge source: :author)
54   - end
55   -
56   - # Just like #author_link but for the committer.
57   - def committer_link(options = {})
58   - person_link(options.merge source: :committer)
59   - end
60   -
61   - protected
62   -
63   - def no_commit_message
64   - "--no commit message"
65   - end
66   -
67   - # Private: Returns a link to a person. If the person has a matching user and
68   - # is a member of the current @project it will link to the team member page.
69   - # Otherwise it will link to the person email as specified in the commit.
70   - #
71   - # options:
72   - # source: one of :author or :committer
73   - # avatar: true will prepend the avatar image
74   - # size: size of the avatar image in px
75   - def person_link(options = {})
76   - source_name = send "#{options[:source]}_name".to_sym
77   - source_email = send "#{options[:source]}_email".to_sym
78   - text = if options[:avatar]
79   - avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
80   - %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
81   - else
82   - source_name
83   - end
84   -
85   - user = User.where('name like ? or email like ?', source_name, source_email).first
86   -
87   - if user.nil?
88   - h.mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
89   - else
90   - h.link_to(text.html_safe, h.user_path(user), class: "commit-#{options[:source]}-link")
91   - end
92   - end
93   -end
app/decorators/tree_decorator.rb
... ... @@ -1,33 +0,0 @@
1   -class TreeDecorator < ApplicationDecorator
2   - decorates :tree
3   -
4   - def breadcrumbs(max_links = 2)
5   - if path
6   - part_path = ""
7   - parts = path.split("\/")
8   -
9   - yield('..', nil) if parts.count > max_links
10   -
11   - parts.each do |part|
12   - part_path = File.join(part_path, part) unless part_path.empty?
13   - part_path = part if part_path.empty?
14   -
15   - next unless parts.last(2).include?(part) if parts.count > max_links
16   - yield(part, h.tree_join(ref, part_path))
17   - end
18   - end
19   - end
20   -
21   - def up_dir?
22   - path.present?
23   - end
24   -
25   - def up_dir_path
26   - file = File.join(path, "..")
27   - h.tree_join(ref, file)
28   - end
29   -
30   - def readme
31   - @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
32   - end
33   -end
app/helpers/application_helper.rb
... ... @@ -96,7 +96,7 @@ module ApplicationHelper
96 96 ]
97 97  
98 98 project_nav = []
99   - if @project && @project.repository && @project.repository.root_ref
  99 + if @project && @project.repository.exists? && @project.repository.root_ref
100 100 project_nav = [
101 101 { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
102 102 { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
... ...
app/helpers/commits_helper.rb
1 1 module CommitsHelper
  2 + # Returns a link to the commit author. If the author has a matching user and
  3 + # is a member of the current @project it will link to the team member page.
  4 + # Otherwise it will link to the author email as specified in the commit.
  5 + #
  6 + # options:
  7 + # avatar: true will prepend the avatar image
  8 + # size: size of the avatar image in px
  9 + def commit_author_link(commit, options = {})
  10 + commit_person_link(commit, options.merge(source: :author))
  11 + end
  12 +
  13 + # Just like #author_link but for the committer.
  14 + def commit_committer_link(commit, options = {})
  15 + commit_person_link(commit, options.merge(source: :committer))
  16 + end
  17 +
2 18 def identification_type(line)
3 19 if line[0] == "+"
4 20 "new"
... ... @@ -93,9 +109,7 @@ module CommitsHelper
93 109 end
94 110  
95 111 def commit_to_html commit
96   - if commit.model
97   - escape_javascript(render 'commits/commit', commit: commit)
98   - end
  112 + escape_javascript(render 'commits/commit', commit: commit)
99 113 end
100 114  
101 115 def diff_line_content(line)
... ... @@ -105,4 +119,58 @@ module CommitsHelper
105 119 line
106 120 end
107 121 end
  122 +
  123 + # Breadcrumb links for a Project and, if applicable, a tree path
  124 + def commits_breadcrumbs
  125 + return unless @project && @ref
  126 +
  127 + # Add the root project link and the arrow icon
  128 + crumbs = content_tag(:li) do
  129 + content_tag(:span, nil, class: 'arrow') +
  130 + link_to(@project.name, project_commits_path(@project, @ref))
  131 + end
  132 +
  133 + if @path
  134 + parts = @path.split('/')
  135 +
  136 + parts.each_with_index do |part, i|
  137 + crumbs += content_tag(:span, '/', class: 'divider')
  138 + crumbs += content_tag(:li) do
  139 + # The text is just the individual part, but the link needs all the parts before it
  140 + link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
  141 + end
  142 + end
  143 + end
  144 +
  145 + crumbs.html_safe
  146 + end
  147 +
  148 + protected
  149 +
  150 + # Private: Returns a link to a person. If the person has a matching user and
  151 + # is a member of the current @project it will link to the team member page.
  152 + # Otherwise it will link to the person email as specified in the commit.
  153 + #
  154 + # options:
  155 + # source: one of :author or :committer
  156 + # avatar: true will prepend the avatar image
  157 + # size: size of the avatar image in px
  158 + def commit_person_link(commit, options = {})
  159 + source_name = commit.send "#{options[:source]}_name".to_sym
  160 + source_email = commit.send "#{options[:source]}_email".to_sym
  161 + text = if options[:avatar]
  162 + avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
  163 + %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
  164 + else
  165 + source_name
  166 + end
  167 +
  168 + user = User.where('name like ? or email like ?', source_name, source_email).first
  169 +
  170 + if user.nil?
  171 + mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
  172 + else
  173 + link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link")
  174 + end
  175 + end
108 176 end
... ...
app/helpers/tree_helper.rb
... ... @@ -70,28 +70,26 @@ module TreeHelper
70 70 end
71 71 end
72 72  
73   - # Breadcrumb links for a Project and, if applicable, a tree path
74   - def breadcrumbs
75   - return unless @project && @ref
76   -
77   - # Add the root project link and the arrow icon
78   - crumbs = content_tag(:li) do
79   - content_tag(:span, nil, class: 'arrow') +
80   - link_to(@project.name, project_commits_path(@project, @ref))
81   - end
  73 + def tree_breadcrumbs(tree, max_links = 2)
  74 + if tree.path
  75 + part_path = ""
  76 + parts = tree.path.split("\/")
  77 +
  78 + yield('..', nil) if parts.count > max_links
82 79  
83   - if @path
84   - parts = @path.split('/')
  80 + parts.each do |part|
  81 + part_path = File.join(part_path, part) unless part_path.empty?
  82 + part_path = part if part_path.empty?
85 83  
86   - parts.each_with_index do |part, i|
87   - crumbs += content_tag(:span, '/', class: 'divider')
88   - crumbs += content_tag(:li) do
89   - # The text is just the individual part, but the link needs all the parts before it
90   - link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
91   - end
  84 + next unless parts.last(2).include?(part) if parts.count > max_links
  85 + yield(part, tree_join(tree.ref, part_path))
92 86 end
93 87 end
  88 + end
94 89  
95   - crumbs.html_safe
  90 + def up_dir_path tree
  91 + file = File.join(tree.path, "..")
  92 + tree_join(tree.ref, file)
96 93 end
  94 +
97 95 end
... ...
app/mailers/emails/notes.rb
... ... @@ -3,7 +3,6 @@ module Emails
3 3 def note_commit_email(recipient_id, note_id)
4 4 @note = Note.find(note_id)
5 5 @commit = @note.noteable
6   - @commit = CommitDecorator.decorate(@commit)
7 6 @project = @note.project
8 7 mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
9 8 end
... ...
app/models/commit.rb
... ... @@ -8,174 +8,70 @@ class Commit
8 8 #
9 9 DIFF_SAFE_SIZE = 100
10 10  
11   - attr_accessor :commit, :head, :refs
12   -
13   - delegate :message, :authored_date, :committed_date, :parents, :sha,
14   - :date, :committer, :author, :diffs, :tree, :id, :stats,
15   - :to_patch, to: :commit
16   -
17   - class << self
18   - def find_or_first(repo, commit_id = nil, root_ref)
19   - commit = if commit_id
20   - repo.commit(commit_id)
21   - else
22   - repo.commits(root_ref).first
23   - end
24   -
25   - Commit.new(commit) if commit
26   - end
27   -
28   - def fresh_commits(repo, n = 10)
29   - commits = repo.heads.map do |h|
30   - repo.commits(h.name, n).map { |c| Commit.new(c, h) }
31   - end.flatten.uniq { |c| c.id }
32   -
33   - commits.sort! do |x, y|
34   - y.committed_date <=> x.committed_date
35   - end
36   -
37   - commits[0...n]
38   - end
39   -
40   - def commits_with_refs(repo, n = 20)
41   - commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
42   -
43   - commits.sort! do |x, y|
44   - y.committed_date <=> x.committed_date
45   - end
46   -
47   - commits[0..n]
48   - end
49   -
50   - def commits_since(repo, date)
51   - commits = repo.heads.map do |h|
52   - repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
53   - end.flatten.uniq { |c| c.id }
54   -
55   - commits.sort! do |x, y|
56   - y.committed_date <=> x.committed_date
57   - end
58   -
59   - commits
60   - end
61   -
62   - def commits(repo, ref, path = nil, limit = nil, offset = nil)
63   - if path
64   - repo.log(ref, path, max_count: limit, skip: offset)
65   - elsif limit && offset
66   - repo.commits(ref, limit, offset)
67   - else
68   - repo.commits(ref)
69   - end.map{ |c| Commit.new(c) }
70   - end
71   -
72   - def commits_between(repo, from, to)
73   - repo.commits_between(from, to).map { |c| Commit.new(c) }
74   - end
75   -
76   - def compare(project, from, to)
77   - result = {
78   - commits: [],
79   - diffs: [],
80   - commit: nil,
81   - same: false
82   - }
83   -
84   - return result unless from && to
85   -
86   - first = project.repository.commit(to.try(:strip))
87   - last = project.repository.commit(from.try(:strip))
88   -
89   - if first && last
90   - result[:same] = (first.id == last.id)
91   - result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
92   -
93   - # Dont load diff for 100+ commits
94   - result[:diffs] = if result[:commits].size > 100
95   - []
96   - else
97   - project.repo.diff(last.id, first.id) rescue []
98   - end
99   -
100   - result[:commit] = Commit.new(first)
101   - end
102   -
103   - result
104   - end
105   - end
106   -
107   - def initialize(raw_commit, head = nil)
108   - raise "Nil as raw commit passed" unless raw_commit
109   -
110   - @commit = raw_commit
111   - @head = head
112   - end
113   -
114   - def short_id(length = 10)
115   - id.to_s[0..length]
  11 + def self.decorate(commits)
  12 + commits.map { |c| self.new(c) }
116 13 end
117 14  
118   - def safe_message
119   - @safe_message ||= message
120   - end
121   -
122   - def created_at
123   - committed_date
124   - end
  15 + attr_accessor :raw
125 16  
126   - def author_email
127   - author.email
128   - end
  17 + def initialize(raw_commit)
  18 + raise "Nil as raw commit passed" unless raw_commit
129 19  
130   - def author_name
131   - author.name
  20 + @raw = raw_commit
132 21 end
133 22  
134   - # Was this commit committed by a different person than the original author?
135   - def different_committer?
136   - author_name != committer_name || author_email != committer_email
  23 + def id
  24 + @raw.id
137 25 end
138 26  
139   - def committer_name
140   - committer.name
  27 + # Returns a string describing the commit for use in a link title
  28 + #
  29 + # Example
  30 + #
  31 + # "Commit: Alex Denisov - Project git clone panel"
  32 + def link_title
  33 + "Commit: #{author_name} - #{title}"
141 34 end
142 35  
143   - def committer_email
144   - committer.email
  36 + # Returns the commits title.
  37 + #
  38 + # Usually, the commit title is the first line of the commit message.
  39 + # In case this first line is longer than 80 characters, it is cut off
  40 + # after 70 characters and ellipses (`&hellp;`) are appended.
  41 + def title
  42 + title = safe_message
  43 +
  44 + return no_commit_message if title.blank?
  45 +
  46 + title_end = title.index(/\n/)
  47 + if (!title_end && title.length > 80) || (title_end && title_end > 80)
  48 + title[0..69] << "&hellip;".html_safe
  49 + else
  50 + title.split(/\n/, 2).first
  51 + end
145 52 end
146 53  
147   - def prev_commit
148   - @prev_commit ||= if parents.present?
149   - Commit.new(parents.first)
150   - else
151   - nil
152   - end
  54 + # Returns the commits description
  55 + #
  56 + # cut off, ellipses (`&hellp;`) are prepended to the commit message.
  57 + def description
  58 + description = safe_message
  59 +
  60 + title_end = description.index(/\n/)
  61 + if (!title_end && description.length > 80) || (title_end && title_end > 80)
  62 + "&hellip;".html_safe << description[70..-1]
  63 + else
  64 + description.split(/\n/, 2)[1].try(:chomp)
  65 + end
153 66 end
154 67  
155   - def prev_commit_id
156   - prev_commit.try :id
  68 + def method_missing(m, *args, &block)
  69 + @raw.send(m, *args, &block)
157 70 end
158 71  
159   - # Shows the diff between the commit's parent and the commit.
160   - #
161   - # Cuts out the header and stats from #to_patch and returns only the diff.
162   - def to_diff
163   - # see Grit::Commit#show
164   - patch = to_patch
165   -
166   - # discard lines before the diff
167   - lines = patch.split("\n")
168   - while !lines.first.start_with?("diff --git") do
169   - lines.shift
170   - end
171   - lines.pop if lines.last =~ /^[\d.]+$/ # Git version
172   - lines.pop if lines.last == "-- " # end of diff
173   - lines.join("\n")
174   - end
  72 + def respond_to?(method)
  73 + return true if @raw.respond_to?(method)
175 74  
176   - def has_zero_stats?
177   - stats.total.zero?
178   - rescue
179   - true
  75 + super
180 76 end
181 77 end
... ...
app/models/gollum_wiki.rb
... ... @@ -50,7 +50,7 @@ class GollumWiki
50 50 # Returns the last 30 Commit objects across the entire
51 51 # repository.
52 52 def recent_history
53   - Commit.fresh_commits(wiki.repo, 30)
  53 + Gitlab::Git::Commit.fresh_commits(wiki.repo, 30)
54 54 end
55 55  
56 56 # Finds a page within the repository based on a tile
... ... @@ -90,13 +90,17 @@ class GollumWiki
90 90 private
91 91  
92 92 def create_repo!
93   - if gitlab_shell.add_repository(path_with_namespace)
  93 + if init_repo(path_with_namespace)
94 94 Gollum::Wiki.new(path_to_repo)
95 95 else
96 96 raise CouldNotCreateWikiError
97 97 end
98 98 end
99 99  
  100 + def init_repo(path_with_namespace)
  101 + gitlab_shell.add_repository(path_with_namespace)
  102 + end
  103 +
100 104 def commit_details(action, message = nil, title = nil)
101 105 commit_message = message || default_message(action, title)
102 106  
... ... @@ -114,5 +118,4 @@ class GollumWiki
114 118 def path_to_repo
115 119 @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
116 120 end
117   -
118 121 end
... ...
app/models/merge_request.rb
... ... @@ -152,7 +152,17 @@ class MergeRequest &lt; ActiveRecord::Base
152 152 end
153 153  
154 154 def commits
155   - st_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
156 166 end
157 167  
158 168 def probably_merged?
... ... @@ -169,9 +179,8 @@ class MergeRequest &lt; ActiveRecord::Base
169 179 end
170 180  
171 181 def unmerged_commits
172   - self.project.repo.
  182 + self.project.repository.
173 183 commits_between(self.target_branch, self.source_branch).
174   - map {|c| Commit.new(c)}.
175 184 sort_by(&:created_at).
176 185 reverse
177 186 end
... ...
app/models/network/commit.rb
... ... @@ -8,7 +8,7 @@ module Network
8 8 attr_accessor :time, :spaces, :parent_spaces
9 9  
10 10 def initialize(raw_commit, refs)
11   - @commit = ::Commit.new(raw_commit)
  11 + @commit = Gitlab::Git::Commit.new(raw_commit)
12 12 @time = -1
13 13 @spaces = []
14 14 @parent_spaces = []
... ...
app/models/project.rb
... ... @@ -141,13 +141,7 @@ class Project &lt; ActiveRecord::Base
141 141 end
142 142  
143 143 def repository
144   - if path
145   - @repository ||= Repository.new(path_with_namespace, default_branch)
146   - else
147   - nil
148   - end
149   - rescue Grit::NoSuchPathError
150   - nil
  144 + @repository ||= Repository.new(path_with_namespace, default_branch)
151 145 end
152 146  
153 147 def saved?
... ... @@ -332,14 +326,14 @@ class Project &lt; ActiveRecord::Base
332 326 end
333 327  
334 328 def valid_repo?
335   - repo
  329 + repository.exists?
336 330 rescue
337 331 errors.add(:path, "Invalid repository path")
338 332 false
339 333 end
340 334  
341 335 def empty_repo?
342   - !repository || repository.empty?
  336 + !repository.exists? || repository.empty?
343 337 end
344 338  
345 339 def ensure_satellite_exists
... ... @@ -363,7 +357,7 @@ class Project &lt; ActiveRecord::Base
363 357 end
364 358  
365 359 def repo_exists?
366   - @repo_exists ||= (repository && repository.branches.present?)
  360 + @repo_exists ||= repository.exists?
367 361 rescue
368 362 @repo_exists = false
369 363 end
... ...
app/models/repository.rb
1 1 class Repository
2   - include Gitlab::Popen
  2 + attr_accessor :raw_repository
3 3  
4   - # Repository directory name with namespace direcotry
5   - # Examples:
6   - # gitlab/gitolite
7   - # diaspora
8   - #
9   - attr_accessor :path_with_namespace
10   -
11   - # Grit repo object
12   - attr_accessor :repo
13   -
14   - # Default branch in the repository
15   - attr_accessor :root_ref
16   -
17   - def initialize(path_with_namespace, root_ref = 'master')
18   - @root_ref = root_ref || "master"
19   - @path_with_namespace = path_with_namespace
20   -
21   - # Init grit repo object
22   - repo
23   - end
24   -
25   - def raw
26   - repo
27   - end
28   -
29   - def path_to_repo
30   - @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
31   - end
32   -
33   - def repo
34   - @repo ||= Grit::Repo.new(path_to_repo)
35   - end
36   -
37   - def commit(commit_id = nil)
38   - Commit.find_or_first(repo, commit_id, root_ref)
  4 + def initialize(path_with_namespace, default_branch)
  5 + @raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
  6 + rescue Gitlab::Git::Repository::NoRepository
  7 + nil
39 8 end
40 9  
41   - def fresh_commits(n = 10)
42   - Commit.fresh_commits(repo, n)
  10 + def exists?
  11 + raw_repository
43 12 end
44 13  
45   - def commits_with_refs(n = 20)
46   - Commit.commits_with_refs(repo, n)
  14 + def empty?
  15 + raw_repository.empty?
47 16 end
48 17  
49   - def commits_since(date)
50   - Commit.commits_since(repo, date)
  18 + def commit(id = nil)
  19 + commit = raw_repository.commit(id)
  20 + commit = Commit.new(commit) if commit
  21 + commit
51 22 end
52 23  
53 24 def commits(ref, path = nil, limit = nil, offset = nil)
54   - Commit.commits(repo, ref, path, limit, offset)
55   - end
56   -
57   - def last_commit_for(ref, path = nil)
58   - commits(ref, path, 1).first
59   - end
60   -
61   - def commits_between(from, to)
62   - Commit.commits_between(repo, from, to)
  25 + commits = raw_repository.commits(ref, path, limit, offset)
  26 + commits = Commit.decorate(commits) if commits.present?
  27 + commits
63 28 end
64 29  
65   - # Returns an Array of branch names
66   - # sorted by name ASC
67   - def branch_names
68   - branches.map(&:name)
  30 + def commits_between(target, source)
  31 + commits = raw_repository.commits_between(target, source)
  32 + commits = Commit.decorate(commits) if commits.present?
  33 + commits
69 34 end
70 35  
71   - # Returns an Array of Branches
72   - def branches
73   - repo.branches.sort_by(&:name)
  36 + def method_missing(m, *args, &block)
  37 + raw_repository.send(m, *args, &block)
74 38 end
75 39  
76   - # Returns an Array of tag names
77   - def tag_names
78   - repo.tags.collect(&:name).sort.reverse
79   - end
80   -
81   - # Returns an Array of Tags
82   - def tags
83   - repo.tags.sort_by(&:name).reverse
84   - end
85   -
86   - # Returns an Array of branch and tag names
87   - def ref_names
88   - [branch_names + tag_names].flatten
89   - end
90   -
91   - def heads
92   - @heads ||= repo.heads
93   - end
94   -
95   - def tree(fcommit, path = nil)
96   - fcommit = commit if fcommit == :head
97   - tree = fcommit.tree
98   - path ? (tree / path) : tree
99   - end
100   -
101   - def has_commits?
102   - !!commit
103   - rescue Grit::NoSuchPathError
104   - false
105   - end
106   -
107   - def empty?
108   - !has_commits?
109   - end
110   -
111   - # Discovers the default branch based on the repository's available branches
112   - #
113   - # - If no branches are present, returns nil
114   - # - If one branch is present, returns its name
115   - # - If two or more branches are present, returns the one that has a name
116   - # matching root_ref (default_branch or 'master' if default_branch is nil)
117   - def discover_default_branch
118   - if branch_names.length == 0
119   - nil
120   - elsif branch_names.length == 1
121   - branch_names.first
122   - else
123   - branch_names.select { |v| v == root_ref }.first
124   - end
125   - end
126   -
127   - # Archive Project to .tar.gz
128   - #
129   - # Already packed repo archives stored at
130   - # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
131   - #
132   - def archive_repo(ref)
133   - ref = ref || self.root_ref
134   - commit = self.commit(ref)
135   - return nil unless commit
136   -
137   - # Build file path
138   - file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
139   - storage_path = Rails.root.join("tmp", "repositories")
140   - file_path = File.join(storage_path, self.path_with_namespace, file_name)
141   -
142   - # Put files into a directory before archiving
143   - prefix = File.basename(self.path_with_namespace) + "/"
144   -
145   - # Create file if not exists
146   - unless File.exists?(file_path)
147   - FileUtils.mkdir_p File.dirname(file_path)
148   - file = self.repo.archive_to_file(ref, prefix, file_path)
149   - end
150   -
151   - file_path
152   - end
153   -
154   - # Return repo size in megabytes
155   - # Cached in redis
156   - def size
157   - Rails.cache.fetch(cache_key(:size)) do
158   - size = popen('du -s', path_to_repo).first.strip.to_i
159   - (size.to_f / 1024).round(2)
160   - end
161   - end
162   -
163   - def expire_cache
164   - Rails.cache.delete(cache_key(:size))
165   - end
  40 + def respond_to?(method)
  41 + return true if raw_repository.respond_to?(method)
166 42  
167   - def cache_key(type)
168   - "#{type}:#{path_with_namespace}"
  43 + super
169 44 end
170 45 end
... ...
app/models/tree.rb
... ... @@ -26,4 +26,12 @@ class Tree
26 26 def empty?
27 27 data.blank?
28 28 end
  29 +
  30 + def up_dir?
  31 + path.present?
  32 + end
  33 +
  34 + def readme
  35 + @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
  36 + end
29 37 end
... ...
app/models/wiki_page.rb
... ... @@ -79,14 +79,14 @@ class WikiPage
79 79 def version
80 80 return nil unless persisted?
81 81  
82   - @version ||= Commit.new(@page.version)
  82 + @version ||= Commit.new(Gitlab::Git::Commit.new(@page.version))
83 83 end
84 84  
85 85 # Returns an array of Gitlab Commit instances.
86 86 def versions
87 87 return [] unless persisted?
88 88  
89   - @page.versions.map { |v| Commit.new(v) }
  89 + @page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) }
90 90 end
91 91  
92 92 # Returns the Date that this latest version was
... ...
app/views/admin/projects/show.html.haml
... ... @@ -46,18 +46,21 @@
46 46 %span.light ssh:
47 47 %strong
48 48 = link_to @project.ssh_url_to_repo
49   - %li
50   - %span.light fs:
51   - %strong
52   - = @repository.path_to_repo
  49 + - if @project.repository.exists?
  50 + %li
  51 + %span.light fs:
  52 + %strong
  53 + = @repository.path_to_repo
53 54  
54   - %li
55   - %span.light last commit:
56   - %strong
57   - - if @repository
  55 + %li
  56 + %span.light last commit:
  57 + %strong
58 58 = last_commit(@project)
59   - - else
60   - never
  59 + - else
  60 + %li
  61 + %span.light repository:
  62 + %strong.cred
  63 + does not exist
61 64  
62 65 %li
63 66 %span.light access:
... ...
app/views/blame/show.html.haml
... ... @@ -6,7 +6,7 @@
6 6 %i.icon-angle-right
7 7 = link_to project_tree_path(@project, @ref) do
8 8 = @project.name
9   - - @tree.breadcrumbs(6) do |link|
  9 + - tree_breadcrumbs(@tree, 6) do |link|
10 10 \/
11 11 %li= link
12 12 .clear
... ... @@ -22,13 +22,13 @@
22 22 %table
23 23 - current_line = 1
24 24 - @blame.each do |commit, lines|
25   - - commit = CommitDecorator.decorate(Commit.new(commit))
  25 + - commit = Commit.new(commit)
26 26 %tr
27 27 %td.blame-commit
28 28 %span.commit
29 29 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
30 30 &nbsp;
31   - = commit.author_link avatar: true, size: 16
  31 + = commit_author_link(commit, avatar: true, size: 16)
32 32 &nbsp;
33 33 = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
34 34 %td.lines.blame-numbers
... ...
app/views/commit/_commit_box.html.haml
... ... @@ -24,14 +24,14 @@
24 24 .row
25 25 .span5
26 26 .author
27   - = @commit.author_link avatar: true, size: 32
  27 + = commit_author_link(@commit, avatar: true, size: 32)
28 28 authored
29 29 %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
30 30 #{time_ago_in_words(@commit.authored_date)} ago
31 31 - if @commit.different_committer?
32 32 .committer
33 33 &rarr;
34   - = @commit.committer_link
  34 + = commit_committer_link(@commit)
35 35 committed
36 36 %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
37 37 #{time_ago_in_words(@commit.committed_date)} ago
... ...
app/views/commits/_commit.html.haml
... ... @@ -4,7 +4,7 @@
4 4 %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
5 5 %p
6 6 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
7   - = commit.author_link avatar: true, size: 24
  7 + = commit_author_link(commit, avatar: true, size: 24)
8 8 &nbsp;
9 9 = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
10 10  
... ...
app/views/commits/show.html.haml
... ... @@ -2,7 +2,7 @@
2 2  
3 3 - if @path.present?
4 4 %ul.breadcrumb
5   - = breadcrumbs
  5 + = commits_breadcrumbs
6 6  
7 7 %div{id: dom_id(@project)}
8 8 #commits-list= render "commits"
... ...
app/views/compare/show.html.haml
... ... @@ -16,7 +16,7 @@
16 16 %div.ui-box
17 17 %h5.title
18 18 Commits (#{@commits.count})
19   - %ul.well-list= render @commits
  19 + %ul.well-list= render Commit.decorate(@commits)
20 20  
21 21 - unless @diffs.empty?
22 22 %h4 Diff
... ...
app/views/events/_commit.html.haml
1   -- commit = CommitDecorator.decorate(commit)
2 1 %li.commit
3 2 %p
4 3 = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
... ...
app/views/projects/_clone_panel.html.haml
... ... @@ -4,7 +4,7 @@
4 4 .form-horizontal= render "shared/clone_panel"
5 5 .span4.pull-right
6 6 .pull-right
7   - - unless @project.empty_repo?
  7 + - if @project.empty_repo?
8 8 - if can? current_user, :download_code, @project
9 9 = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do
10 10 %i.icon-download-alt
... ...
app/views/repositories/_branch.html.haml
1   -- commit = Commit.new(branch.commit)
2   -- commit = CommitDecorator.decorate(commit)
  1 +- commit = Commit.new(Gitlab::Git::Commit.new(branch.commit))
3 2 %tr
4 3 %td
5 4 = link_to project_commits_path(@project, branch.name) do
... ...
app/views/repositories/_feed.html.haml
1 1 - commit = update
2   -- commit = CommitDecorator.new(commit)
3 2 %tr
4 3 %td
5 4 = link_to project_commits_path(@project, commit.head.name) do
... ...
app/views/repositories/tags.html.haml
... ... @@ -7,8 +7,7 @@
7 7 %th Last commit
8 8 %th
9 9 - @tags.each do |tag|
10   - - commit = Commit.new(tag.commit)
11   - - commit = CommitDecorator.decorate(commit)
  10 + - commit = Commit.new(Gitlab::Git::Commit.new(tag.commit))
12 11 %tr
13 12 %td
14 13 %strong
... ...
app/views/tree/_tree.html.haml
... ... @@ -3,7 +3,7 @@
3 3 %i.icon-angle-right
4 4 = link_to project_tree_path(@project, @ref) do
5 5 = @project.path
6   - - tree.breadcrumbs(6) do |title, path|
  6 + - tree_breadcrumbs(tree, 6) do |title, path|
7 7 \/
8 8 %li
9 9 - if path
... ... @@ -27,7 +27,7 @@
27 27 %tr.tree-item
28 28 %td.tree-item-file-name
29 29 = image_tag "file_empty.png", size: '16x16'
30   - = link_to "..", project_tree_path(@project, tree.up_dir_path)
  30 + = link_to "..", project_tree_path(@project, up_dir_path(tree))
31 31 %td
32 32 %td
33 33 %td
... ...
app/views/tree/_tree_commit_column.html.haml
1   -%span.tree_author= commit.author_link avatar: true
  1 +%span.tree_author= commit_author_link(commit, avatar: true)
2 2 = link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link"
... ...
app/views/wikis/history.html.haml
... ... @@ -14,12 +14,13 @@
14 14 %th Format
15 15 %tbody
16 16 - @wiki.versions.each do |version|
17   - - commit = CommitDecorator.new(version)
  17 + - commit = version
18 18 %tr
19 19 %td
20 20 = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do
21 21 = commit.short_id
22   - %td= commit.author_link avatar: true, size: 24
  22 + %td
  23 + = commit_author_link(commit, avatar: true, size: 24)
23 24 %td
24 25 = commit.title
25 26 %td
... ...
app/views/wikis/pages.html.haml
... ... @@ -21,5 +21,5 @@
21 21 = wiki_page.created_at.to_s(:short) do
22 22 (#{time_ago_in_words(wiki_page.created_at)}
23 23 ago)
24   - - commit = CommitDecorator.decorate(wiki_page.version)
25   - %td= commit.author_link avatar: true, size: 24
  24 + %td
  25 + = commit_author_link(wiki_page.version, avatar: true, size: 24)
... ...
app/views/wikis/show.html.haml
... ... @@ -13,5 +13,4 @@
13 13 = preserve do
14 14 = render_wiki_content(@wiki)
15 15  
16   -- commit = CommitDecorator.new(@wiki.version)
17   -%p.time Last edited by #{commit.author_link(avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago
  16 +%p.time Last edited by #{commit_author_link(@wiki.version, avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago
... ...
features/steps/project/project_browse_commits.rb
... ... @@ -15,7 +15,7 @@ class ProjectBrowseCommits &lt; Spinach::FeatureSteps
15 15 end
16 16  
17 17 Then 'I see commits atom feed' do
18   - commit = CommitDecorator.decorate(@project.repository.commit)
  18 + commit = @project.repository.commit
19 19 page.response_headers['Content-Type'].should have_content("application/atom+xml")
20 20 page.body.should have_selector("title", :text => "Recent commits to #{@project.name}")
21 21 page.body.should have_selector("author email", :text => commit.author_email)
... ...
features/steps/shared/project.rb
... ... @@ -3,14 +3,14 @@ module SharedProject
3 3  
4 4 # Create a project without caring about what it's called
5 5 And "I own a project" do
6   - @project = create(:project)
  6 + @project = create(:project_with_code)
7 7 @project.team << [@user, :master]
8 8 end
9 9  
10 10 # Create a specific project called "Shop"
11 11 And 'I own project "Shop"' do
12 12 @project = Project.find_by_name "Shop"
13   - @project ||= create(:project, name: "Shop")
  13 + @project ||= create(:project_with_code, name: "Shop")
14 14 @project.team << [@user, :master]
15 15 end
16 16  
... ...
features/steps/userteams/userteams.rb
... ... @@ -132,7 +132,7 @@ class Userteams &lt; Spinach::FeatureSteps
132 132 team = UserTeam.last
133 133 team.projects.each do |project|
134 134 team.members.each do |member|
135   - 3.times { project.merge_requests << create(:merge_request, assignee: member) }
  135 + 3.times { create(:merge_request, assignee: member, project: project) }
136 136 end
137 137 end
138 138 end
... ... @@ -157,7 +157,7 @@ class Userteams &lt; Spinach::FeatureSteps
157 157 team = UserTeam.last
158 158 team.projects.each do |project|
159 159 team.members.each do |member|
160   - 3.times { project.merge_requests << create(:merge_request, assignee: member) }
  160 + 3.times { create(:merge_request, assignee: member, project: project) }
161 161 end
162 162 end
163 163 end
... ...
features/support/env.rb
... ... @@ -14,7 +14,7 @@ require &#39;spinach/capybara&#39;
14 14 require 'sidekiq/testing/inline'
15 15  
16 16  
17   -%w(stubbed_repository valid_commit select2_helper).each do |f|
  17 +%w(valid_commit select2_helper test_env).each do |f|
18 18 require Rails.root.join('spec', 'support', f)
19 19 end
20 20  
... ... @@ -35,13 +35,8 @@ Capybara.default_wait_time = 10
35 35 DatabaseCleaner.strategy = :truncation
36 36  
37 37 Spinach.hooks.before_scenario do
38   - # Use tmp dir for FS manipulations
39   - Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
40   - Gitlab::Shell.any_instance.stub(:add_repository) do |path|
41   - create_temp_repo("#{Rails.root}/tmp/test-git-base-path/#{path}.git")
42   - end
43   - FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
44   - FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
  38 + TestEnv.init
  39 +
45 40 DatabaseCleaner.start
46 41 end
47 42  
... ... @@ -54,9 +49,3 @@ Spinach.hooks.before_run do
54 49  
55 50 include FactoryGirl::Syntax::Methods
56 51 end
57   -
58   -def create_temp_repo(path)
59   - FileUtils.mkdir_p path
60   - command = "git init --quiet --bare #{path};"
61   - system(command)
62   -end
... ...
lib/api/projects.rb
... ... @@ -372,7 +372,7 @@ module Gitlab
372 372 ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
373 373  
374 374 commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
375   - present CommitDecorator.decorate(commits), with: Entities::RepoCommit
  375 + present commits, with: Entities::RepoCommit
376 376 end
377 377  
378 378 # Get a project snippets
... ...
lib/extracts_path.rb
... ... @@ -85,8 +85,8 @@ module ExtractsPath
85 85 # - @id - A string representing the joined ref and path
86 86 # - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA)
87 87 # - @path - A string representing the filesystem path
88   - # - @commit - A CommitDecorator representing the commit from the given ref
89   - # - @tree - A TreeDecorator representing the tree at the given ref/path
  88 + # - @commit - A Commit representing the commit from the given ref
  89 + # - @tree - A Tree representing the tree at the given ref/path
90 90 #
91 91 # If the :id parameter appears to be requesting a specific response format,
92 92 # that will be handled as well.
... ... @@ -100,11 +100,9 @@ module ExtractsPath
100 100  
101 101 # It is used "@project.repository.commits(@ref, @path, 1, 0)",
102 102 # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name.
103   - commits = @project.repository.commits(@ref, @path, 1, 0)
104   - @commit = CommitDecorator.decorate(commits.first)
  103 + @commit = @project.repository.commits(@ref, @path, 1, 0).first
105 104  
106 105 @tree = Tree.new(@commit.tree, @ref, @path)
107   - @tree = TreeDecorator.new(@tree)
108 106  
109 107 raise InvalidPathError if @tree.invalid?
110 108 rescue RuntimeError, NoMethodError, InvalidPathError
... ...
lib/gitlab/git/blame.rb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +module Gitlab
  2 + module Git
  3 + class Blame
  4 +
  5 + attr_accessor :repository, :sha, :path
  6 +
  7 + def initialize(repository, sha, path)
  8 + @repository, @sha, @path = repository, sha, path
  9 +
  10 + end
  11 +
  12 + def each
  13 + raw_blame = Grit::Blob.blame(repository.repo, sha, path)
  14 +
  15 + raw_blame.each do |commit, lines|
  16 + commit = Gitlab::Git::Commit.new(commit)
  17 + yield(commit, lines)
  18 + end
  19 + end
  20 + end
  21 + end
  22 +end
... ...
lib/gitlab/git/commit.rb 0 → 100644
... ... @@ -0,0 +1,153 @@
  1 +# Gitlab::Git::Commit is a wrapper around native Grit::Commit object
  2 +# We dont want to use grit objects inside app/
  3 +# It helps us easily migrate to rugged in future
  4 +module Gitlab
  5 + module Git
  6 + class Commit
  7 + attr_accessor :raw_commit, :head, :refs
  8 +
  9 + delegate :message, :authored_date, :committed_date, :parents, :sha,
  10 + :date, :committer, :author, :diffs, :tree, :id, :stats, :to_patch,
  11 + to: :raw_commit
  12 +
  13 + class << self
  14 + def find_or_first(repo, commit_id = nil, root_ref)
  15 + commit = if commit_id
  16 + repo.commit(commit_id)
  17 + else
  18 + repo.commits(root_ref).first
  19 + end
  20 +
  21 + Commit.new(commit) if commit
  22 + end
  23 +
  24 + def fresh_commits(repo, n = 10)
  25 + commits = repo.heads.map do |h|
  26 + repo.commits(h.name, n).map { |c| Commit.new(c, h) }
  27 + end.flatten.uniq { |c| c.id }
  28 +
  29 + commits.sort! do |x, y|
  30 + y.committed_date <=> x.committed_date
  31 + end
  32 +
  33 + commits[0...n]
  34 + end
  35 +
  36 + def commits_with_refs(repo, n = 20)
  37 + commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
  38 +
  39 + commits.sort! do |x, y|
  40 + y.committed_date <=> x.committed_date
  41 + end
  42 +
  43 + commits[0..n]
  44 + end
  45 +
  46 + def commits_since(repo, date)
  47 + commits = repo.heads.map do |h|
  48 + repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
  49 + end.flatten.uniq { |c| c.id }
  50 +
  51 + commits.sort! do |x, y|
  52 + y.committed_date <=> x.committed_date
  53 + end
  54 +
  55 + commits
  56 + end
  57 +
  58 + def commits(repo, ref, path = nil, limit = nil, offset = nil)
  59 + if path
  60 + repo.log(ref, path, max_count: limit, skip: offset)
  61 + elsif limit && offset
  62 + repo.commits(ref, limit, offset)
  63 + else
  64 + repo.commits(ref)
  65 + end.map{ |c| Commit.new(c) }
  66 + end
  67 +
  68 + def commits_between(repo, from, to)
  69 + repo.commits_between(from, to).map { |c| Commit.new(c) }
  70 + end
  71 + end
  72 +
  73 + def initialize(raw_commit, head = nil)
  74 + raise "Nil as raw commit passed" unless raw_commit
  75 +
  76 + @raw_commit = raw_commit
  77 + @head = head
  78 + end
  79 +
  80 + def short_id(length = 10)
  81 + id.to_s[0..length]
  82 + end
  83 +
  84 + def safe_message
  85 + @safe_message ||= message
  86 + end
  87 +
  88 + def created_at
  89 + committed_date
  90 + end
  91 +
  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?
  101 + def different_committer?
  102 + author_name != committer_name || author_email != committer_email
  103 + end
  104 +
  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
  123 + end
  124 +
  125 + # Shows the diff between the commit's parent and the commit.
  126 + #
  127 + # Cuts out the header and stats from #to_patch and returns only the diff.
  128 + def to_diff
  129 + # see Grit::Commit#show
  130 + patch = to_patch
  131 +
  132 + # discard lines before the diff
  133 + lines = patch.split("\n")
  134 + while !lines.first.start_with?("diff --git") do
  135 + lines.shift
  136 + end
  137 + lines.pop if lines.last =~ /^[\d.]+$/ # Git version
  138 + lines.pop if lines.last == "-- " # end of diff
  139 + lines.join("\n")
  140 + end
  141 +
  142 + def has_zero_stats?
  143 + stats.total.zero?
  144 + rescue
  145 + true
  146 + end
  147 +
  148 + def no_commit_message
  149 + "--no commit message"
  150 + end
  151 + end
  152 + end
  153 +end
... ...
lib/gitlab/git/compare.rb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +module Gitlab
  2 + module Git
  3 + class Compare
  4 + attr_accessor :commits, :commit, :diffs, :same
  5 +
  6 + def initialize(repository, from, to)
  7 + @commits, @diffs = [], []
  8 + @commit = nil
  9 + @same = false
  10 +
  11 + return unless from && to
  12 +
  13 + first = repository.commit(to.try(:strip))
  14 + last = repository.commit(from.try(:strip))
  15 +
  16 + return unless first && last
  17 +
  18 + if first.id == last.id
  19 + @same = true
  20 + return
  21 + end
  22 +
  23 + @commit = Commit.new(first)
  24 +
  25 + @commits = repository.commits_between(last.id, first.id)
  26 + @commits = @commits.map { |c| Commit.new(c) }
  27 +
  28 + @diffs = if @commits.size > 100
  29 + []
  30 + else
  31 + repository.repo.diff(last.id, first.id) rescue []
  32 + end
  33 + end
  34 + end
  35 + end
  36 +end
  37 +
... ...
lib/gitlab/git/repository.rb 0 → 100644
... ... @@ -0,0 +1,185 @@
  1 +# Gitlab::Git::Gitlab::Git::Commit is a wrapper around native Grit::Repository object
  2 +# We dont want to use grit objects inside app/
  3 +# It helps us easily migrate to rugged in future
  4 +module Gitlab
  5 + module Git
  6 + class Repository
  7 + include Gitlab::Popen
  8 +
  9 + class NoRepository < StandardError; end
  10 +
  11 + # Repository directory name with namespace direcotry
  12 + # Examples:
  13 + # gitlab/gitolite
  14 + # diaspora
  15 + #
  16 + attr_accessor :path_with_namespace
  17 +
  18 + # Grit repo object
  19 + attr_accessor :repo
  20 +
  21 + # Default branch in the repository
  22 + attr_accessor :root_ref
  23 +
  24 + def initialize(path_with_namespace, root_ref = 'master')
  25 + @root_ref = root_ref || "master"
  26 + @path_with_namespace = path_with_namespace
  27 +
  28 + # Init grit repo object
  29 + repo
  30 + end
  31 +
  32 + def raw
  33 + repo
  34 + end
  35 +
  36 + def path_to_repo
  37 + @path_to_repo ||= File.join(repos_path, "#{path_with_namespace}.git")
  38 + end
  39 +
  40 + def repos_path
  41 + Gitlab.config.gitlab_shell.repos_path
  42 + end
  43 +
  44 + def repo
  45 + @repo ||= Grit::Repo.new(path_to_repo)
  46 + rescue Grit::NoSuchPathError
  47 + raise NoRepository.new('no repository for such path')
  48 + end
  49 +
  50 + def commit(commit_id = nil)
  51 + Gitlab::Git::Commit.find_or_first(repo, commit_id, root_ref)
  52 + end
  53 +
  54 + def fresh_commits(n = 10)
  55 + Gitlab::Git::Commit.fresh_commits(repo, n)
  56 + end
  57 +
  58 + def commits_with_refs(n = 20)
  59 + Gitlab::Git::Commit.commits_with_refs(repo, n)
  60 + end
  61 +
  62 + def commits_since(date)
  63 + Gitlab::Git::Commit.commits_since(repo, date)
  64 + end
  65 +
  66 + def commits(ref, path = nil, limit = nil, offset = nil)
  67 + Gitlab::Git::Commit.commits(repo, ref, path, limit, offset)
  68 + end
  69 +
  70 + def last_commit_for(ref, path = nil)
  71 + commits(ref, path, 1).first
  72 + end
  73 +
  74 + def commits_between(from, to)
  75 + Gitlab::Git::Commit.commits_between(repo, from, to)
  76 + end
  77 +
  78 + # Returns an Array of branch names
  79 + # sorted by name ASC
  80 + def branch_names
  81 + branches.map(&:name)
  82 + end
  83 +
  84 + # Returns an Array of Branches
  85 + def branches
  86 + repo.branches.sort_by(&:name)
  87 + end
  88 +
  89 + # Returns an Array of tag names
  90 + def tag_names
  91 + repo.tags.collect(&:name).sort.reverse
  92 + end
  93 +
  94 + # Returns an Array of Tags
  95 + def tags
  96 + repo.tags.sort_by(&:name).reverse
  97 + end
  98 +
  99 + # Returns an Array of branch and tag names
  100 + def ref_names
  101 + [branch_names + tag_names].flatten
  102 + end
  103 +
  104 + def heads
  105 + @heads ||= repo.heads
  106 + end
  107 +
  108 + def tree(fcommit, path = nil)
  109 + fcommit = commit if fcommit == :head
  110 + tree = fcommit.tree
  111 + path ? (tree / path) : tree
  112 + end
  113 +
  114 + def has_commits?
  115 + !!commit
  116 + rescue Grit::NoSuchPathError
  117 + false
  118 + end
  119 +
  120 + def empty?
  121 + !has_commits?
  122 + end
  123 +
  124 + # Discovers the default branch based on the repository's available branches
  125 + #
  126 + # - If no branches are present, returns nil
  127 + # - If one branch is present, returns its name
  128 + # - If two or more branches are present, returns the one that has a name
  129 + # matching root_ref (default_branch or 'master' if default_branch is nil)
  130 + def discover_default_branch
  131 + if branch_names.length == 0
  132 + nil
  133 + elsif branch_names.length == 1
  134 + branch_names.first
  135 + else
  136 + branch_names.select { |v| v == root_ref }.first
  137 + end
  138 + end
  139 +
  140 + # Archive Project to .tar.gz
  141 + #
  142 + # Already packed repo archives stored at
  143 + # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
  144 + #
  145 + def archive_repo(ref)
  146 + ref = ref || self.root_ref
  147 + commit = self.commit(ref)
  148 + return nil unless commit
  149 +
  150 + # Build file path
  151 + file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
  152 + storage_path = Rails.root.join("tmp", "repositories")
  153 + file_path = File.join(storage_path, self.path_with_namespace, file_name)
  154 +
  155 + # Put files into a directory before archiving
  156 + prefix = File.basename(self.path_with_namespace) + "/"
  157 +
  158 + # Create file if not exists
  159 + unless File.exists?(file_path)
  160 + FileUtils.mkdir_p File.dirname(file_path)
  161 + file = self.repo.archive_to_file(ref, prefix, file_path)
  162 + end
  163 +
  164 + file_path
  165 + end
  166 +
  167 + # Return repo size in megabytes
  168 + # Cached in redis
  169 + def size
  170 + Rails.cache.fetch(cache_key(:size)) do
  171 + size = popen('du -s', path_to_repo).first.strip.to_i
  172 + (size.to_f / 1024).round(2)
  173 + end
  174 + end
  175 +
  176 + def expire_cache
  177 + Rails.cache.delete(cache_key(:size))
  178 + end
  179 +
  180 + def cache_key(type)
  181 + "#{type}:#{path_with_namespace}"
  182 + end
  183 + end
  184 + end
  185 +end
... ...
lib/gitlab/markdown.rb
... ... @@ -187,7 +187,7 @@ module Gitlab
187 187  
188 188 def reference_commit(identifier)
189 189 if @project.valid_repo? && commit = @project.repository.commit(identifier)
190   - link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: CommitDecorator.new(commit).link_title, class: "gfm gfm-commit #{html_options[:class]}"))
  190 + link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}"))
191 191 end
192 192 end
193 193 end
... ...
lib/tasks/cache.rake 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +namespace :cache do
  2 + desc "GITLAB | Clear redis cache"
  3 + task :clear => :environment do
  4 + Rails.cache.clear
  5 + end
  6 +end
... ...
spec/controllers/commit_controller_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe CommitController do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project_with_code) }
5 5 let(:user) { create(:user) }
6 6 let(:commit) { project.repository.last_commit_for("master") }
7 7  
... ...
spec/controllers/commits_controller_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe CommitsController do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project_with_code) }
5 5 let(:user) { create(:user) }
6 6  
7 7 before do
... ...
spec/controllers/merge_requests_controller_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe MergeRequestsController do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project_with_code) }
5 5 let(:user) { create(:user) }
6 6 let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") }
7 7  
... ...
spec/controllers/tree_controller_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe TreeController do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project_with_code) }
5 5 let(:user) { create(:user) }
6 6  
7 7 before do
... ...
spec/factories.rb
... ... @@ -34,6 +34,10 @@ FactoryGirl.define do
34 34 issues_tracker_id { "project_name_in_redmine" }
35 35 end
36 36  
  37 + factory :project_with_code, parent: :project do
  38 + path { 'gitlabhq' }
  39 + end
  40 +
37 41 factory :group do
38 42 sequence(:name) { |n| "group#{n}" }
39 43 path { name.downcase.gsub(/\s/, '_') }
... ... @@ -73,7 +77,7 @@ FactoryGirl.define do
73 77 factory :merge_request do
74 78 title
75 79 author
76   - project
  80 + project factory: :project_with_code
77 81 source_branch "master"
78 82 target_branch "stable"
79 83  
... ... @@ -82,9 +86,9 @@ FactoryGirl.define do
82 86 target_branch "master" # pretend bcf03b5d~3
83 87 source_branch "stable" # pretend bcf03b5d
84 88 st_commits do
85   - [Commit.new(project.repo.commit('bcf03b5d')),
86   - Commit.new(project.repo.commit('bcf03b5d~1')),
87   - Commit.new(project.repo.commit('bcf03b5d~2'))]
  89 + [Commit.new(project.repository.commit('bcf03b5d')),
  90 + Commit.new(project.repository.commit('bcf03b5d~1')),
  91 + Commit.new(project.repository.commit('bcf03b5d~2'))]
88 92 end
89 93 st_diffs do
90 94 project.repo.diff("bcf03b5d~3", "bcf03b5d")
... ... @@ -116,6 +120,7 @@ FactoryGirl.define do
116 120 factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
117 121  
118 122 trait :on_commit do
  123 + project factory: :project_with_code
119 124 commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
120 125 noteable_type "Commit"
121 126 end
... ... @@ -125,6 +130,7 @@ FactoryGirl.define do
125 130 end
126 131  
127 132 trait :on_merge_request do
  133 + project factory: :project_with_code
128 134 noteable_id 1
129 135 noteable_type "MergeRequest"
130 136 end
... ...
spec/features/gitlab_flavored_markdown_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe "Gitlab Flavored Markdown" do
4   - let(:project) { create(:project) }
  4 + let(:project) { create(:project_with_code) }
5 5 let(:issue) { create(:issue, project: project) }
6 6 let(:merge_request) { create(:merge_request, project: project) }
7 7 let(:fred) do
... ...
spec/features/notes_on_merge_requests_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe "On a merge request", js: true do
4   - let!(:project) { create(:project) }
  4 + let!(:project) { create(:project_with_code) }
5 5 let!(:merge_request) { create(:merge_request, project: project) }
6 6  
7 7 before do
... ... @@ -83,7 +83,7 @@ end
83 83  
84 84  
85 85 describe "On a merge request diff", js: true, focus: true do
86   - let!(:project) { create(:project) }
  86 + let!(:project) { create(:project_with_code) }
87 87 let!(:merge_request) { create(:merge_request_with_diffs, project: project) }
88 88  
89 89 before do
... ...
spec/features/profile_spec.rb
... ... @@ -12,8 +12,9 @@ describe &quot;Profile account page&quot; do
12 12 Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
13 13 visit account_profile_path
14 14 end
  15 +
15 16 it { page.should have_content("Remove account") }
16   -
  17 +
17 18 it "should delete the account", js: true do
18 19 expect { click_link "Delete account" }.to change {User.count}.by(-1)
19 20 current_path.should == new_user_session_path
... ... @@ -45,4 +46,4 @@ describe &quot;Profile account page&quot; do
45 46 current_path.should == account_profile_path
46 47 end
47 48 end
48   -end
49 49 \ No newline at end of file
  50 +end
... ...
spec/features/security/project_access_spec.rb
... ... @@ -14,7 +14,7 @@ describe &quot;Application access&quot; do
14 14 end
15 15  
16 16 describe "Project" do
17   - let(:project) { create(:project) }
  17 + let(:project) { create(:project_with_code) }
18 18  
19 19 let(:master) { create(:user) }
20 20 let(:guest) { create(:user) }
... ...
spec/helpers/gitlab_markdown_helper_spec.rb
... ... @@ -4,10 +4,10 @@ describe GitlabMarkdownHelper do
4 4 include ApplicationHelper
5 5 include IssuesHelper
6 6  
7   - let!(:project) { create(:project) }
  7 + let!(:project) { create(:project_with_code) }
8 8  
9 9 let(:user) { create(:user, username: 'gfm') }
10   - let(:commit) { CommitDecorator.decorate(project.repository.commit) }
  10 + let(:commit) { project.repository.commit }
11 11 let(:issue) { create(:issue, project: project) }
12 12 let(:merge_request) { create(:merge_request, project: project) }
13 13 let(:snippet) { create(:snippet, project: project) }
... ...
spec/lib/git/commit_spec.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +require "spec_helper"
  2 +
  3 +describe Gitlab::Git::Commit do
  4 + let(:commit) { create(:project_with_code).repository.commit }
  5 +
  6 + describe "Commit info" do
  7 + before do
  8 + @committer = double(
  9 + email: 'mike@smith.com',
  10 + name: 'Mike Smith'
  11 + )
  12 +
  13 + @author = double(
  14 + email: 'john@smith.com',
  15 + name: 'John Smith'
  16 + )
  17 +
  18 + @raw_commit = double(
  19 + id: "bcf03b5de6abcf03b5de6c",
  20 + author: @author,
  21 + committer: @committer,
  22 + committed_date: Date.yesterday,
  23 + message: 'Refactoring specs'
  24 + )
  25 +
  26 + @commit = Gitlab::Git::Commit.new(@raw_commit)
  27 + end
  28 +
  29 + it { @commit.short_id.should == "bcf03b5de6a" }
  30 + it { @commit.safe_message.should == @raw_commit.message }
  31 + it { @commit.created_at.should == @raw_commit.committed_date }
  32 + it { @commit.author_email.should == @author.email }
  33 + it { @commit.author_name.should == @author.name }
  34 + it { @commit.committer_name.should == @committer.name }
  35 + it { @commit.committer_email.should == @committer.email }
  36 + it { @commit.different_committer?.should be_true }
  37 + end
  38 +
  39 + describe "Class methods" do
  40 + subject { Gitlab::Git::Commit }
  41 +
  42 + it { should respond_to(:find_or_first) }
  43 + it { should respond_to(:fresh_commits) }
  44 + it { should respond_to(:commits_with_refs) }
  45 + it { should respond_to(:commits_since) }
  46 + it { should respond_to(:commits_between) }
  47 + it { should respond_to(:commits) }
  48 + end
  49 +end
... ...
spec/lib/git/repository_spec.rb 0 → 100644
... ... @@ -0,0 +1,104 @@
  1 +require "spec_helper"
  2 +
  3 +describe Gitlab::Git::Repository do
  4 + let(:repository) { Gitlab::Git::Repository.new('gitlabhq', 'master') }
  5 +
  6 + describe "Respond to" do
  7 + subject { repository }
  8 +
  9 + it { should respond_to(:repo) }
  10 + it { should respond_to(:tree) }
  11 + it { should respond_to(:root_ref) }
  12 + it { should respond_to(:tags) }
  13 + it { should respond_to(:commit) }
  14 + it { should respond_to(:commits) }
  15 + it { should respond_to(:commits_between) }
  16 + it { should respond_to(:commits_with_refs) }
  17 + it { should respond_to(:commits_since) }
  18 + it { should respond_to(:commits_between) }
  19 + end
  20 +
  21 +
  22 + describe "#discover_default_branch" do
  23 + let(:master) { 'master' }
  24 + let(:stable) { 'stable' }
  25 +
  26 + it "returns 'master' when master exists" do
  27 + repository.should_receive(:branch_names).at_least(:once).and_return([stable, master])
  28 + repository.discover_default_branch.should == 'master'
  29 + end
  30 +
  31 + it "returns non-master when master exists but default branch is set to something else" do
  32 + repository.root_ref = 'stable'
  33 + repository.should_receive(:branch_names).at_least(:once).and_return([stable, master])
  34 + repository.discover_default_branch.should == 'stable'
  35 + end
  36 +
  37 + it "returns a non-master branch when only one exists" do
  38 + repository.should_receive(:branch_names).at_least(:once).and_return([stable])
  39 + repository.discover_default_branch.should == 'stable'
  40 + end
  41 +
  42 + it "returns nil when no branch exists" do
  43 + repository.should_receive(:branch_names).at_least(:once).and_return([])
  44 + repository.discover_default_branch.should be_nil
  45 + end
  46 + end
  47 +
  48 + describe :commit do
  49 + it "should return first head commit if without params" do
  50 + repository.commit.id.should == repository.repo.commits.first.id
  51 + end
  52 +
  53 + it "should return valid commit" do
  54 + repository.commit(ValidCommit::ID).should be_valid_commit
  55 + end
  56 +
  57 + it "should return nil" do
  58 + repository.commit("+123_4532530XYZ").should be_nil
  59 + end
  60 + end
  61 +
  62 + describe :tree do
  63 + before do
  64 + @commit = repository.commit(ValidCommit::ID)
  65 + end
  66 +
  67 + it "should raise error w/o arguments" do
  68 + lambda { repository.tree }.should raise_error
  69 + end
  70 +
  71 + it "should return root tree for commit" do
  72 + tree = repository.tree(@commit)
  73 + tree.contents.size.should == ValidCommit::FILES_COUNT
  74 + tree.contents.map(&:name).should == ValidCommit::FILES
  75 + end
  76 +
  77 + it "should return root tree for commit with correct path" do
  78 + tree = repository.tree(@commit, ValidCommit::C_FILE_PATH)
  79 + tree.contents.map(&:name).should == ValidCommit::C_FILES
  80 + end
  81 +
  82 + it "should return root tree for commit with incorrect path" do
  83 + repository.tree(@commit, "invalid_path").should be_nil
  84 + end
  85 + end
  86 +
  87 + describe "fresh commits" do
  88 + it { repository.fresh_commits(3).count.should == 3 }
  89 + it { repository.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }
  90 + it { repository.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }
  91 + end
  92 +
  93 + describe "commits_between" do
  94 + subject do
  95 + commits = repository.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",
  96 + "8470d70da67355c9c009e4401746b1d5410af2e3")
  97 + commits.map { |c| c.id }
  98 + end
  99 +
  100 + it { should have(3).elements }
  101 + it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }
  102 + it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
  103 + end
  104 +end
... ...
spec/mailers/notify_spec.rb
... ... @@ -5,7 +5,7 @@ describe Notify do
5 5 include EmailSpec::Matchers
6 6  
7 7 let(:recipient) { create(:user, email: 'recipient@example.com') }
8   - let(:project) { create(:project) }
  8 + let(:project) { create(:project_with_code) }
9 9  
10 10 shared_examples 'a multiple recipients email' do
11 11 it 'is sent to the given recipient' do
... ... @@ -277,14 +277,7 @@ describe Notify do
277 277 end
278 278  
279 279 describe 'on a commit' do
280   - let(:commit) do
281   - mock(:commit).tap do |commit|
282   - commit.stub(:id).and_return('fauxsha1')
283   - commit.stub(:project).and_return(project)
284   - commit.stub(:short_id).and_return('fauxsha1')
285   - commit.stub(:safe_message).and_return('some message')
286   - end
287   - end
  280 + let(:commit) { project.repository.commit }
288 281  
289 282 before(:each) { note.stub(:noteable).and_return(commit) }
290 283  
... ... @@ -297,7 +290,7 @@ describe Notify do
297 290 end
298 291  
299 292 it 'contains a link to the commit' do
300   - should have_body_text /fauxsha1/
  293 + should have_body_text commit.short_id
301 294 end
302 295 end
303 296  
... ...
spec/models/commit_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe Commit do
4   - let(:commit) { create(:project).repository.commit }
  4 + let(:commit) { create(:project_with_code).repository.commit }
5 5  
6   - describe CommitDecorator do
7   - let(:decorator) { CommitDecorator.new(commit) }
8 6  
9   - describe '#title' do
10   - it "returns no_commit_message when safe_message is blank" do
11   - decorator.stub(:safe_message).and_return('')
12   - decorator.title.should == "--no commit message"
13   - end
14   -
15   - it "truncates a message without a newline at 70 characters" do
16   - message = commit.safe_message * 10
17   -
18   - decorator.stub(:safe_message).and_return(message)
19   - decorator.title.should == "#{message[0..69]}&hellip;"
20   - end
21   -
22   - it "truncates a message with a newline before 80 characters at the newline" do
23   - message = commit.safe_message.split(" ").first
24   -
25   - decorator.stub(:safe_message).and_return(message + "\n" + message)
26   - decorator.title.should == message
27   - end
28   -
29   - it "truncates a message with a newline after 80 characters at 70 characters" do
30   - message = (commit.safe_message * 10) + "\n"
31   -
32   - decorator.stub(:safe_message).and_return(message)
33   - decorator.title.should == "#{message[0..69]}&hellip;"
34   - end
  7 + describe '#title' do
  8 + it "returns no_commit_message when safe_message is blank" do
  9 + commit.stub(:safe_message).and_return('')
  10 + commit.title.should == "--no commit message"
35 11 end
36   - end
37 12  
38   - describe "Commit info" do
39   - before do
40   - @committer = double(
41   - email: 'mike@smith.com',
42   - name: 'Mike Smith'
43   - )
  13 + it "truncates a message without a newline at 70 characters" do
  14 + message = commit.safe_message * 10
44 15  
45   - @author = double(
46   - email: 'john@smith.com',
47   - name: 'John Smith'
48   - )
  16 + commit.stub(:safe_message).and_return(message)
  17 + commit.title.should == "#{message[0..69]}&hellip;"
  18 + end
49 19  
50   - @raw_commit = double(
51   - id: "bcf03b5de6abcf03b5de6c",
52   - author: @author,
53   - committer: @committer,
54   - committed_date: Date.yesterday,
55   - message: 'Refactoring specs'
56   - )
  20 + it "truncates a message with a newline before 80 characters at the newline" do
  21 + message = commit.safe_message.split(" ").first
57 22  
58   - @commit = Commit.new(@raw_commit)
  23 + commit.stub(:safe_message).and_return(message + "\n" + message)
  24 + commit.title.should == message
59 25 end
60 26  
61   - it { @commit.short_id.should == "bcf03b5de6a" }
62   - it { @commit.safe_message.should == @raw_commit.message }
63   - it { @commit.created_at.should == @raw_commit.committed_date }
64   - it { @commit.author_email.should == @author.email }
65   - it { @commit.author_name.should == @author.name }
66   - it { @commit.committer_name.should == @committer.name }
67   - it { @commit.committer_email.should == @committer.email }
68   - it { @commit.different_committer?.should be_true }
69   - end
  27 + it "truncates a message with a newline after 80 characters at 70 characters" do
  28 + message = (commit.safe_message * 10) + "\n"
70 29  
71   - describe "Class methods" do
72   - subject { Commit }
73   -
74   - it { should respond_to(:find_or_first) }
75   - it { should respond_to(:fresh_commits) }
76   - it { should respond_to(:commits_with_refs) }
77   - it { should respond_to(:commits_since) }
78   - it { should respond_to(:commits_between) }
79   - it { should respond_to(:commits) }
80   - it { should respond_to(:compare) }
  30 + commit.stub(:safe_message).and_return(message)
  31 + commit.title.should == "#{message[0..69]}&hellip;"
  32 + end
81 33 end
82 34  
83 35 describe "delegation" do
... ...
spec/models/gollum_wiki_spec.rb
... ... @@ -81,7 +81,7 @@ describe GollumWiki do
81 81 end
82 82  
83 83 it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
84   - Gitlab::Shell.any_instance.stub(:add_repository).and_return(false)
  84 + GollumWiki.any_instance.stub(:init_repo).and_return(false)
85 85 expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError)
86 86 end
87 87 end
... ...
spec/models/project_spec.rb
... ... @@ -119,7 +119,7 @@ describe Project do
119 119 end
120 120  
121 121 describe :update_merge_requests do
122   - let(:project) { create(:project) }
  122 + let(:project) { create(:project_with_code) }
123 123  
124 124 before do
125 125 @merge_request = create(:merge_request, project: project)
... ... @@ -189,10 +189,6 @@ describe Project do
189 189 it "should return valid repo" do
190 190 project.repository.should be_kind_of(Repository)
191 191 end
192   -
193   - it "should return nil" do
194   - Project.new(path: "empty").repository.should be_nil
195   - end
196 192 end
197 193  
198 194 describe :issue_exists? do
... ... @@ -249,7 +245,7 @@ describe Project do
249 245 end
250 246  
251 247 describe :open_branches do
252   - let(:project) { create(:project) }
  248 + let(:project) { create(:project_with_code) }
253 249  
254 250 before do
255 251 project.protected_branches.create(name: 'master')
... ...
spec/models/repository_spec.rb
... ... @@ -1,105 +0,0 @@
1   -require "spec_helper"
2   -
3   -describe Repository do
4   - let(:project) { create(:project) }
5   - let(:repository) { project.repository }
6   -
7   - describe "Respond to" do
8   - subject { repository }
9   -
10   - it { should respond_to(:repo) }
11   - it { should respond_to(:tree) }
12   - it { should respond_to(:root_ref) }
13   - it { should respond_to(:tags) }
14   - it { should respond_to(:commit) }
15   - it { should respond_to(:commits) }
16   - it { should respond_to(:commits_between) }
17   - it { should respond_to(:commits_with_refs) }
18   - it { should respond_to(:commits_since) }
19   - it { should respond_to(:commits_between) }
20   - end
21   -
22   -
23   - describe "#discover_default_branch" do
24   - let(:master) { 'master' }
25   - let(:stable) { 'stable' }
26   -
27   - it "returns 'master' when master exists" do
28   - repository.should_receive(:branch_names).at_least(:once).and_return([stable, master])
29   - repository.discover_default_branch.should == 'master'
30   - end
31   -
32   - it "returns non-master when master exists but default branch is set to something else" do
33   - repository.root_ref = 'stable'
34   - repository.should_receive(:branch_names).at_least(:once).and_return([stable, master])
35   - repository.discover_default_branch.should == 'stable'
36   - end
37   -
38   - it "returns a non-master branch when only one exists" do
39   - repository.should_receive(:branch_names).at_least(:once).and_return([stable])
40   - repository.discover_default_branch.should == 'stable'
41   - end
42   -
43   - it "returns nil when no branch exists" do
44   - repository.should_receive(:branch_names).at_least(:once).and_return([])
45   - repository.discover_default_branch.should be_nil
46   - end
47   - end
48   -
49   - describe :commit do
50   - it "should return first head commit if without params" do
51   - repository.commit.id.should == repository.repo.commits.first.id
52   - end
53   -
54   - it "should return valid commit" do
55   - repository.commit(ValidCommit::ID).should be_valid_commit
56   - end
57   -
58   - it "should return nil" do
59   - repository.commit("+123_4532530XYZ").should be_nil
60   - end
61   - end
62   -
63   - describe :tree do
64   - before do
65   - @commit = repository.commit(ValidCommit::ID)
66   - end
67   -
68   - it "should raise error w/o arguments" do
69   - lambda { repository.tree }.should raise_error
70   - end
71   -
72   - it "should return root tree for commit" do
73   - tree = repository.tree(@commit)
74   - tree.contents.size.should == ValidCommit::FILES_COUNT
75   - tree.contents.map(&:name).should == ValidCommit::FILES
76   - end
77   -
78   - it "should return root tree for commit with correct path" do
79   - tree = repository.tree(@commit, ValidCommit::C_FILE_PATH)
80   - tree.contents.map(&:name).should == ValidCommit::C_FILES
81   - end
82   -
83   - it "should return root tree for commit with incorrect path" do
84   - repository.tree(@commit, "invalid_path").should be_nil
85   - end
86   - end
87   -
88   - describe "fresh commits" do
89   - it { repository.fresh_commits(3).count.should == 3 }
90   - it { repository.fresh_commits.first.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" }
91   - it { repository.fresh_commits.last.id.should == "f403da73f5e62794a0447aca879360494b08f678" }
92   - end
93   -
94   - describe "commits_between" do
95   - subject do
96   - commits = repository.commits_between("3a4b4fb4cde7809f033822a171b9feae19d41fff",
97   - "8470d70da67355c9c009e4401746b1d5410af2e3")
98   - commits.map { |c| c.id }
99   - end
100   -
101   - it { should have(3).elements }
102   - it { should include("f0f14c8eaba69ebddd766498a9d0b0e79becd633") }
103   - it { should_not include("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
104   - end
105   -end
spec/requests/api/merge_requests_spec.rb
... ... @@ -4,7 +4,7 @@ describe Gitlab::API do
4 4 include ApiHelpers
5 5  
6 6 let(:user) { create(:user ) }
7   - let!(:project) { create(:project, namespace: user.namespace ) }
  7 + let!(:project) { create(:project_with_code, creator_id: user.id) }
8 8 let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") }
9 9 before { project.team << [user, :reporters] }
10 10  
... ...
spec/requests/api/projects_spec.rb
... ... @@ -7,7 +7,7 @@ describe Gitlab::API do
7 7 let(:user2) { create(:user) }
8 8 let(:user3) { create(:user) }
9 9 let(:admin) { create(:admin) }
10   - let!(:project) { create(:project, namespace: user.namespace ) }
  10 + let!(:project) { create(:project_with_code, creator_id: user.id) }
11 11 let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
12 12 let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') }
13 13 let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
... ...
spec/services/git_push_service_spec.rb
... ... @@ -2,7 +2,7 @@ require &#39;spec_helper&#39;
2 2  
3 3 describe GitPushService do
4 4 let (:user) { create :user }
5   - let (:project) { create :project }
  5 + let (:project) { create :project_with_code }
6 6 let (:service) { GitPushService.new }
7 7  
8 8 before do
... ...
spec/spec_helper.rb
... ... @@ -47,11 +47,7 @@ Spork.prefork do
47 47 config.use_transactional_fixtures = false
48 48  
49 49 config.before do
50   - # Use tmp dir for FS manipulations
51   - temp_repos_path = Rails.root.join('tmp', 'test-git-base-path')
52   - Gitlab.config.gitlab_shell.stub(repos_path: temp_repos_path)
53   - FileUtils.rm_rf temp_repos_path
54   - FileUtils.mkdir_p temp_repos_path
  50 + TestEnv.init
55 51 end
56 52 end
57 53 end
... ...
spec/support/stubbed_repository.rb
... ... @@ -1,75 +0,0 @@
1   -require "repository"
2   -require "project"
3   -require "merge_request"
4   -require "shell"
5   -
6   -# Stubs out all Git repository access done by models so that specs can run
7   -# against fake repositories without Grit complaining that they don't exist.
8   -class Project
9   - def repository
10   - if path == "empty" || !path
11   - nil
12   - else
13   - GitLabTestRepo.new(path_with_namespace)
14   - end
15   - end
16   -
17   - def satellite
18   - FakeSatellite.new
19   - end
20   -
21   - class FakeSatellite
22   - def exists?
23   - true
24   - end
25   -
26   - def destroy
27   - true
28   - end
29   -
30   - def create
31   - true
32   - end
33   - end
34   -end
35   -
36   -class MergeRequest
37   - def check_if_can_be_merged
38   - true
39   - end
40   -end
41   -
42   -class GitLabTestRepo < Repository
43   - def repo
44   - @repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq'))
45   - end
46   -
47   - # patch repo size (in mb)
48   - def size
49   - 12.45
50   - end
51   -end
52   -
53   -module Gitlab
54   - class Shell
55   - def add_repository name
56   - true
57   - end
58   -
59   - def mv_repository name, new_name
60   - true
61   - end
62   -
63   - def remove_repository name
64   - true
65   - end
66   -
67   - def add_key id, key
68   - true
69   - end
70   -
71   - def remove_key id, key
72   - true
73   - end
74   - end
75   -end
spec/support/test_env.rb 0 → 100644
... ... @@ -0,0 +1,63 @@
  1 +module TestEnv
  2 + extend self
  3 +
  4 + # Test environment
  5 + #
  6 + # all repositories and namespaces stored at
  7 + # RAILS_APP/tmp/test-git-base-path
  8 + #
  9 + # Next shell methods are stubbed and return true
  10 + # - mv_repository
  11 + # - remove_repository
  12 + # - add_key
  13 + # - remove_key
  14 + #
  15 + def init
  16 + # Use tmp dir for FS manipulations
  17 + repos_path = Rails.root.join('tmp', 'test-git-base-path')
  18 + Gitlab.config.gitlab_shell.stub(repos_path: repos_path)
  19 +
  20 + GollumWiki.any_instance.stub(:init_repo) do |path|
  21 + create_temp_repo(File.join(repos_path, "#{path}.git"))
  22 + end
  23 +
  24 + Gitlab::Shell.any_instance.stub(
  25 + add_repository: true,
  26 + mv_repository: true,
  27 + remove_repository: true,
  28 + add_key: true,
  29 + remove_key: true
  30 + )
  31 +
  32 + Gitlab::Satellite::Satellite.any_instance.stub(
  33 + exists?: true,
  34 + destroy: true,
  35 + create: true
  36 + )
  37 +
  38 + MergeRequest.any_instance.stub(
  39 + check_if_can_be_merged: true
  40 + )
  41 +
  42 + Repository.any_instance.stub(
  43 + size: 12.45
  44 + )
  45 +
  46 + # Remove tmp/test-git-base-path
  47 + FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
  48 +
  49 + # Recreate tmp/test-git-base-path
  50 + FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
  51 +
  52 + # Symlink tmp/repositories/gitlabhq to tmp/test-git-base-path/gitlabhq
  53 + seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq')
  54 + target_repo = File.join(repos_path, 'gitlabhq.git')
  55 + system("ln -s #{seed_repo} #{target_repo}")
  56 + end
  57 +
  58 + def create_temp_repo(path)
  59 + FileUtils.mkdir_p path
  60 + command = "git init --quiet --bare #{path};"
  61 + system(command)
  62 + end
  63 +end
... ...
spec/workers/post_receive_spec.rb
... ... @@ -9,7 +9,7 @@ describe PostReceive do
9 9 end
10 10  
11 11 context "web hook" do
12   - let(:project) { create(:project) }
  12 + let(:project) { create(:project_with_code) }
13 13 let(:key) { create(:key, user: project.owner) }
14 14 let(:key_id) { key.shell_id }
15 15  
... ...