Commit 76a4cbe4 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'refactor/gitlab_git'

parents 273741fb 8a6bf09a
...@@ -12,7 +12,6 @@ class CommitLoadContext < BaseContext ...@@ -12,7 +12,6 @@ class CommitLoadContext < BaseContext
commit = project.repository.commit(params[:id]) commit = project.repository.commit(params[:id])
if commit if commit
commit = CommitDecorator.decorate(commit)
line_notes = project.notes.for_commit_id(commit.id).inline line_notes = project.notes.for_commit_id(commit.id).inline
result[:commit] = commit result[:commit] = commit
......
...@@ -8,7 +8,6 @@ class BlameController < ProjectResourceController ...@@ -8,7 +8,6 @@ class BlameController < ProjectResourceController
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
@repo = @project.repo @blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
@blame = Grit::Blob.blame(@repo, @commit.id, @path)
end end
end end
...@@ -13,7 +13,6 @@ class CommitsController < ProjectResourceController ...@@ -13,7 +13,6 @@ class CommitsController < ProjectResourceController
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset) @commits = @repo.commits(@ref, @path, @limit, @offset)
@commits = CommitDecorator.decorate_collection(@commits)
respond_to do |format| respond_to do |format|
format.html # index.html.erb format.html # index.html.erb
......
...@@ -8,15 +8,13 @@ class CompareController < ProjectResourceController ...@@ -8,15 +8,13 @@ class CompareController < ProjectResourceController
end end
def show def show
result = Commit.compare(project, params[:from], params[:to]) compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to])
@commits = result[:commits] @commits = compare.commits
@commit = result[:commit] @commit = compare.commit
@diffs = result[:diffs] @diffs = compare.diffs
@refs_are_same = result[:same] @refs_are_same = compare.same
@line_notes = [] @line_notes = []
@commits = CommitDecorator.decorate_collection(@commits)
end end
def create def create
......
...@@ -94,12 +94,10 @@ class MergeRequestsController < ProjectResourceController ...@@ -94,12 +94,10 @@ class MergeRequestsController < ProjectResourceController
def branch_from def branch_from
@commit = @repository.commit(params[:ref]) @commit = @repository.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end end
def branch_to def branch_to
@commit = @repository.commit(params[:ref]) @commit = @repository.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end end
def ci_status def ci_status
...@@ -143,7 +141,6 @@ class MergeRequestsController < ProjectResourceController ...@@ -143,7 +141,6 @@ class MergeRequestsController < ProjectResourceController
# Get commits from repository # Get commits from repository
# or from cache if already merged # or from cache if already merged
@commits = @merge_request.commits @commits = @merge_request.commits
@commits = CommitDecorator.decorate_collection(@commits)
@allowed_to_merge = allowed_to_merge? @allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
......
...@@ -34,7 +34,6 @@ class RefsController < ProjectResourceController ...@@ -34,7 +34,6 @@ class RefsController < ProjectResourceController
@logs = contents.map do |content| @logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @repo.commits(@commit.id, file, 1).last last_commit = @repo.commits(@commit.id, file, 1).last
last_commit = CommitDecorator.decorate(last_commit)
{ {
file_name: content.name, file_name: content.name,
commit: last_commit commit: last_commit
...@@ -49,9 +48,7 @@ class RefsController < ProjectResourceController ...@@ -49,9 +48,7 @@ class RefsController < ProjectResourceController
@repo = project.repository @repo = project.repository
@commit = @repo.commit(@ref) @commit = @repo.commit(@ref)
@commit = CommitDecorator.decorate(@commit)
@tree = Tree.new(@commit.tree, @ref, params[:path]) @tree = Tree.new(@commit.tree, @ref, params[:path])
@tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "") @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
if params[:path] if params[:path]
......
class CommitDecorator < ApplicationDecorator
decorates :commit
# Returns a string describing the commit for use in a link title
#
# Example
#
# "Commit: Alex Denisov - Project git clone panel"
def link_title
"Commit: #{author_name} - #{title}"
end
# Returns the commits title.
#
# Usually, the commit title is the first line of the commit message.
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
title = safe_message
return no_commit_message if title.blank?
title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else
title.split(/\n/, 2).first
end
end
# Returns the commits description
#
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description
description = safe_message
title_end = description.index(/\n/)
if (!title_end && description.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << description[70..-1]
else
description.split(/\n/, 2)[1].try(:chomp)
end
end
# Returns a link to the commit author. If the author has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the author email as specified in the commit.
#
# options:
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def author_link(options = {})
person_link(options.merge source: :author)
end
# Just like #author_link but for the committer.
def committer_link(options = {})
person_link(options.merge source: :committer)
end
protected
def no_commit_message
"--no commit message"
end
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def person_link(options = {})
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
user = User.where('name like ? or email like ?', source_name, source_email).first
if user.nil?
h.mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
else
h.link_to(text.html_safe, h.user_path(user), class: "commit-#{options[:source]}-link")
end
end
end
class TreeDecorator < ApplicationDecorator
decorates :tree
def breadcrumbs(max_links = 2)
if path
part_path = ""
parts = path.split("\/")
yield('..', nil) if parts.count > max_links
parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
yield(part, h.tree_join(ref, part_path))
end
end
end
def up_dir?
path.present?
end
def up_dir_path
file = File.join(path, "..")
h.tree_join(ref, file)
end
def readme
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
end
...@@ -96,7 +96,7 @@ module ApplicationHelper ...@@ -96,7 +96,7 @@ module ApplicationHelper
] ]
project_nav = [] project_nav = []
if @project && @project.repository && @project.repository.root_ref if @project && @project.repository.exists? && @project.repository.root_ref
project_nav = [ project_nav = [
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) }, { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) }, { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
......
module CommitsHelper module CommitsHelper
# Returns a link to the commit author. If the author has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the author email as specified in the commit.
#
# options:
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_author_link(commit, options = {})
commit_person_link(commit, options.merge(source: :author))
end
# Just like #author_link but for the committer.
def commit_committer_link(commit, options = {})
commit_person_link(commit, options.merge(source: :committer))
end
def identification_type(line) def identification_type(line)
if line[0] == "+" if line[0] == "+"
"new" "new"
...@@ -93,9 +109,7 @@ module CommitsHelper ...@@ -93,9 +109,7 @@ module CommitsHelper
end end
def commit_to_html commit def commit_to_html commit
if commit.model escape_javascript(render 'commits/commit', commit: commit)
escape_javascript(render 'commits/commit', commit: commit)
end
end end
def diff_line_content(line) def diff_line_content(line)
...@@ -105,4 +119,58 @@ module CommitsHelper ...@@ -105,4 +119,58 @@ module CommitsHelper
line line
end end
end end
# Breadcrumb links for a Project and, if applicable, a tree path
def commits_breadcrumbs
return unless @project && @ref
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
if @path
parts = @path.split('/')
parts.each_with_index do |part, i|
crumbs += content_tag(:span, '/', class: 'divider')
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end
end
crumbs.html_safe
end
protected
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_person_link(commit, options = {})
source_name = commit.send "#{options[:source]}_name".to_sym
source_email = commit.send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
user = User.where('name like ? or email like ?', source_name, source_email).first
if user.nil?
mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
else
link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link")
end
end
end end
...@@ -70,28 +70,26 @@ module TreeHelper ...@@ -70,28 +70,26 @@ module TreeHelper
end end
end end
# Breadcrumb links for a Project and, if applicable, a tree path def tree_breadcrumbs(tree, max_links = 2)
def breadcrumbs if tree.path
return unless @project && @ref part_path = ""
parts = tree.path.split("\/")
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do yield('..', nil) if parts.count > max_links
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
if @path parts.each do |part|
parts = @path.split('/') part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
parts.each_with_index do |part, i| next unless parts.last(2).include?(part) if parts.count > max_links
crumbs += content_tag(:span, '/', class: 'divider') yield(part, tree_join(tree.ref, part_path))
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end end
end end
end
crumbs.html_safe def up_dir_path tree
file = File.join(tree.path, "..")
tree_join(tree.ref, file)
end end
end end
...@@ -3,7 +3,6 @@ module Emails ...@@ -3,7 +3,6 @@ module Emails
def note_commit_email(recipient_id, note_id) def note_commit_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@commit = @note.noteable @commit = @note.noteable
@commit = CommitDecorator.decorate(@commit)
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
end end
......
...@@ -8,174 +8,70 @@ class Commit ...@@ -8,174 +8,70 @@ class Commit
# #
DIFF_SAFE_SIZE = 100 DIFF_SAFE_SIZE = 100
attr_accessor :commit, :head, :refs def self.decorate(commits)
commits.map { |c| self.new(c) }
delegate :message, :authored_date, :committed_date, :parents, :sha,
:date, :committer, :author, :diffs, :tree, :id, :stats,
:to_patch, to: :commit
class << self
def find_or_first(repo, commit_id = nil, root_ref)
commit = if commit_id
repo.commit(commit_id)
else
repo.commits(root_ref).first
end
Commit.new(commit) if commit
end
def fresh_commits(repo, n = 10)
commits = repo.heads.map do |h|
repo.commits(h.name, n).map { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0...n]
end
def commits_with_refs(repo, n = 20)
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0..n]
end
def commits_since(repo, date)
commits = repo.heads.map do |h|
repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits
end
def commits(repo, ref, path = nil, limit = nil, offset = nil)
if path
repo.log(ref, path, max_count: limit, skip: offset)
elsif limit && offset
repo.commits(ref, limit, offset)
else
repo.commits(ref)
end.map{ |c| Commit.new(c) }
end
def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
def compare(project, from, to)
result = {
commits: [],
diffs: [],
commit: nil,
same: false
}
return result unless from && to
first = project.repository.commit(to.try(:strip))
last = project.repository.commit(from.try(:strip))
if first && last
result[:same] = (first.id == last.id)
result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
# Dont load diff for 100+ commits
result[:diffs] = if result[:commits].size > 100
[]
else
project.repo.diff(last.id, first.id) rescue []
end
result[:commit] = Commit.new(first)
end
result
end
end
def initialize(raw_commit, head = nil)
raise "Nil as raw commit passed" unless raw_commit
@commit = raw_commit
@head = head
end
def short_id(length = 10)
id.to_s[0..length]
end end
def safe_message attr_accessor :raw
@safe_message ||= message
end
def created_at
committed_date
end
def author_email def initialize(raw_commit)
author.email raise "Nil as raw commit passed" unless raw_commit
end
def author_name @raw = raw_commit
author.name
end end
# Was this commit committed by a different person than the original author? def id
def different_committer? @raw.id
author_name != committer_name || author_email != committer_email
end end
def committer_name # Returns a string describing the commit for use in a link title
committer.name #
# Example
#
# "Commit: Alex Denisov - Project git clone panel"
def link_title
"Commit: #{author_name} - #{title}"
end end
def committer_email # Returns the commits title.
committer.email #
# Usually, the commit title is the first line of the commit message.
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
title = safe_message
return no_commit_message if title.blank?
title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else
title.split(/\n/, 2).first
end
end end
def prev_commit # Returns the commits description
@prev_commit ||= if parents.present? #
Commit.new(parents.first) # cut off, ellipses (`&hellp;`) are prepended to the commit message.
else def description
nil description = safe_message
end
title_end = description.index(/\n/)
if (!title_end && description.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << description[70..-1]
else
description.split(/\n/, 2)[1].try(:chomp)
end
end end
def prev_commit_id def method_missing(m, *args, &block)
prev_commit.try :id @raw.send(m, *args, &block)
end end
# Shows the diff between the commit's parent and the commit. def respond_to?(method)
# return true if @raw.respond_to?(method)
# Cuts out the header and stats from #to_patch and returns only the diff.
def to_diff
# see Grit::Commit#show
patch = to_patch
# discard lines before the diff
lines = patch.split("\n")
while !lines.first.start_with?("diff --git") do
lines.shift
end
lines.pop if lines.last =~ /^[\d.]+$/ # Git version
lines.pop if lines.last == "-- " # end of diff
lines.join("\n")
end
def has_zero_stats? super
stats.total.zero?
rescue
true
end end
end end
...@@ -50,7 +50,7 @@ class GollumWiki ...@@ -50,7 +50,7 @@ class GollumWiki
# Returns the last 30 Commit objects across the entire # Returns the last 30 Commit objects across the entire
# repository. # repository.
def recent_history def recent_history
Commit.fresh_commits(wiki.repo, 30) Gitlab::Git::Commit.fresh_commits(wiki.repo, 30)
end end
# Finds a page within the repository based on a tile # Finds a page within the repository based on a tile
...@@ -90,13 +90,17 @@ class GollumWiki ...@@ -90,13 +90,17 @@ class GollumWiki
private private
def create_repo! def create_repo!
if gitlab_shell.add_repository(path_with_namespace) if init_repo(path_with_namespace)
Gollum::Wiki.new(path_to_repo) Gollum::Wiki.new(path_to_repo)
else else
raise CouldNotCreateWikiError raise CouldNotCreateWikiError
end end
end end
def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace)
end
def commit_details(action, message = nil, title = nil) def commit_details(action, message = nil, title = nil)
commit_message = message || default_message(action, title) commit_message = message || default_message(action, title)
...@@ -114,5 +118,4 @@ class GollumWiki ...@@ -114,5 +118,4 @@ class GollumWiki
def path_to_repo def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
end end
end end
...@@ -152,7 +152,17 @@ class MergeRequest < ActiveRecord::Base ...@@ -152,7 +152,17 @@ class MergeRequest < ActiveRecord::Base
end end
def commits def commits
st_commits || [] if st_commits.present?
# check if merge request commits are valid
if st_commits.first.respond_to?(:short_id)
st_commits
else
# if commits are invalid - simply reload it from repo
reloaded_commits
end
else
[]
end
end end
def probably_merged? def probably_merged?
...@@ -169,9 +179,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -169,9 +179,8 @@ class MergeRequest < ActiveRecord::Base
end end
def unmerged_commits def unmerged_commits
self.project.repo. self.project.repository.
commits_between(self.target_branch, self.source_branch). commits_between(self.target_branch, self.source_branch).
map {|c| Commit.new(c)}.
sort_by(&:created_at). sort_by(&:created_at).
reverse reverse
end end
......
...@@ -8,7 +8,7 @@ module Network ...@@ -8,7 +8,7 @@ module Network
attr_accessor :time, :spaces, :parent_spaces attr_accessor :time, :spaces, :parent_spaces
def initialize(raw_commit, refs) def initialize(raw_commit, refs)
@commit = ::Commit.new(raw_commit) @commit = Gitlab::Git::Commit.new(raw_commit)
@time = -1 @time = -1
@spaces = [] @spaces = []
@parent_spaces = [] @parent_spaces = []
......
...@@ -141,13 +141,7 @@ class Project < ActiveRecord::Base ...@@ -141,13 +141,7 @@ class Project < ActiveRecord::Base
end end
def repository def repository
if path @repository ||= Repository.new(path_with_namespace, default_branch)
@repository ||= Repository.new(path_with_namespace, default_branch)
else
nil
end
rescue Grit::NoSuchPathError
nil
end end
def saved? def saved?
...@@ -332,14 +326,14 @@ class Project < ActiveRecord::Base ...@@ -332,14 +326,14 @@ class Project < ActiveRecord::Base
end end
def valid_repo? def valid_repo?
repo repository.exists?
rescue rescue
errors.add(:path, "Invalid repository path") errors.add(:path, "Invalid repository path")
false false
end end
def empty_repo? def empty_repo?
!repository || repository.empty? !repository.exists? || repository.empty?
end end
def ensure_satellite_exists def ensure_satellite_exists
...@@ -363,7 +357,7 @@ class Project < ActiveRecord::Base ...@@ -363,7 +357,7 @@ class Project < ActiveRecord::Base
end end
def repo_exists? def repo_exists?
@repo_exists ||= (repository && repository.branches.present?) @repo_exists ||= repository.exists?
rescue rescue
@repo_exists = false @repo_exists = false
end end
......
class Repository class Repository
include Gitlab::Popen attr_accessor :raw_repository
# Repository directory name with namespace direcotry def initialize(path_with_namespace, default_branch)
# Examples: @raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
# gitlab/gitolite rescue Gitlab::Git::Repository::NoRepository
# diaspora nil
#
attr_accessor :path_with_namespace
# Grit repo object
attr_accessor :repo
# Default branch in the repository
attr_accessor :root_ref
def initialize(path_with_namespace, root_ref = 'master')
@root_ref = root_ref || "master"
@path_with_namespace = path_with_namespace
# Init grit repo object
repo
end
def raw
repo
end
def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
end
def repo
@repo ||= Grit::Repo.new(path_to_repo)
end
def commit(commit_id = nil)
Commit.find_or_first(repo, commit_id, root_ref)
end end
def fresh_commits(n = 10) def exists?
Commit.fresh_commits(repo, n) raw_repository
end end
def commits_with_refs(n = 20) def empty?
Commit.commits_with_refs(repo, n) raw_repository.empty?
end end
def commits_since(date) def commit(id = nil)
Commit.commits_since(repo, date) commit = raw_repository.commit(id)
commit = Commit.new(commit) if commit
commit
end end
def commits(ref, path = nil, limit = nil, offset = nil) def commits(ref, path = nil, limit = nil, offset = nil)
Commit.commits(repo, ref, path, limit, offset) commits = raw_repository.commits(ref, path, limit, offset)
end commits = Commit.decorate(commits) if commits.present?
commits
def last_commit_for(ref, path = nil)
commits(ref, path, 1).first
end
def commits_between(from, to)
Commit.commits_between(repo, from, to)
end end
# Returns an Array of branch names def commits_between(target, source)
# sorted by name ASC commits = raw_repository.commits_between(target, source)
def branch_names commits = Commit.decorate(commits) if commits.present?
branches.map(&:name) commits
end end
# Returns an Array of Branches def method_missing(m, *args, &block)
def branches raw_repository.send(m, *args, &block)
repo.branches.sort_by(&:name)
end end
# Returns an Array of tag names def respond_to?(method)
def tag_names return true if raw_repository.respond_to?(method)
repo.tags.collect(&:name).sort.reverse
end
# Returns an Array of Tags
def tags
repo.tags.sort_by(&:name).reverse
end
# Returns an Array of branch and tag names
def ref_names
[branch_names + tag_names].flatten
end
def heads
@heads ||= repo.heads
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
path ? (tree / path) : tree
end
def has_commits?
!!commit
rescue Grit::NoSuchPathError
false
end
def empty?
!has_commits?
end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
if branch_names.length == 0
nil
elsif branch_names.length == 1
branch_names.first
else
branch_names.select { |v| v == root_ref }.first
end
end
# Archive Project to .tar.gz
#
# Already packed repo archives stored at
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
#
def archive_repo(ref)
ref = ref || self.root_ref
commit = self.commit(ref)
return nil unless commit
# Build file path
file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
storage_path = Rails.root.join("tmp", "repositories")
file_path = File.join(storage_path, self.path_with_namespace, file_name)
# Put files into a directory before archiving
prefix = File.basename(self.path_with_namespace) + "/"
# Create file if not exists
unless File.exists?(file_path)
FileUtils.mkdir_p File.dirname(file_path)
file = self.repo.archive_to_file(ref, prefix, file_path)
end
file_path
end
# Return repo size in megabytes
# Cached in redis
def size
Rails.cache.fetch(cache_key(:size)) do
size = popen('du -s', path_to_repo).first.strip.to_i
(size.to_f / 1024).round(2)
end
end
def expire_cache
Rails.cache.delete(cache_key(:size))
end
def cache_key(type) super
"#{type}:#{path_with_namespace}"
end end
end end
...@@ -26,4 +26,12 @@ class Tree ...@@ -26,4 +26,12 @@ class Tree
def empty? def empty?
data.blank? data.blank?
end end
def up_dir?
path.present?
end
def readme
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
end end
...@@ -79,14 +79,14 @@ class WikiPage ...@@ -79,14 +79,14 @@ class WikiPage
def version def version
return nil unless persisted? return nil unless persisted?
@version ||= Commit.new(@page.version) @version ||= Commit.new(Gitlab::Git::Commit.new(@page.version))
end end
# Returns an array of Gitlab Commit instances. # Returns an array of Gitlab Commit instances.
def versions def versions
return [] unless persisted? return [] unless persisted?
@page.versions.map { |v| Commit.new(v) } @page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) }
end end
# Returns the Date that this latest version was # Returns the Date that this latest version was
......
...@@ -46,18 +46,21 @@ ...@@ -46,18 +46,21 @@
%span.light ssh: %span.light ssh:
%strong %strong
= link_to @project.ssh_url_to_repo = link_to @project.ssh_url_to_repo
%li - if @project.repository.exists?
%span.light fs: %li
%strong %span.light fs:
= @repository.path_to_repo %strong
= @repository.path_to_repo
%li %li
%span.light last commit: %span.light last commit:
%strong %strong
- if @repository
= last_commit(@project) = last_commit(@project)
- else - else
never %li
%span.light repository:
%strong.cred
does not exist
%li %li
%span.light access: %span.light access:
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%i.icon-angle-right %i.icon-angle-right
= link_to project_tree_path(@project, @ref) do = link_to project_tree_path(@project, @ref) do
= @project.name = @project.name
- @tree.breadcrumbs(6) do |link| - tree_breadcrumbs(@tree, 6) do |link|
\/ \/
%li= link %li= link
.clear .clear
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
%table %table
- current_line = 1 - current_line = 1
- @blame.each do |commit, lines| - @blame.each do |commit, lines|
- commit = CommitDecorator.decorate(Commit.new(commit)) - commit = Commit.new(commit)
%tr %tr
%td.blame-commit %td.blame-commit
%span.commit %span.commit
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
&nbsp; &nbsp;
= commit.author_link avatar: true, size: 16 = commit_author_link(commit, avatar: true, size: 16)
&nbsp; &nbsp;
= link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
%td.lines.blame-numbers %td.lines.blame-numbers
......
...@@ -24,14 +24,14 @@ ...@@ -24,14 +24,14 @@
.row .row
.span5 .span5
.author .author
= @commit.author_link avatar: true, size: 32 = commit_author_link(@commit, avatar: true, size: 32)
authored authored
%time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")} %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.authored_date)} ago #{time_ago_in_words(@commit.authored_date)} ago
- if @commit.different_committer? - if @commit.different_committer?
.committer .committer
&rarr; &rarr;
= @commit.committer_link = commit_committer_link(@commit)
committed committed
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.committed_date)} ago #{time_ago_in_words(@commit.committed_date)} ago
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
%p %p
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
= commit.author_link avatar: true, size: 24 = commit_author_link(commit, avatar: true, size: 24)
&nbsp; &nbsp;
= link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- if @path.present? - if @path.present?
%ul.breadcrumb %ul.breadcrumb
= breadcrumbs = commits_breadcrumbs
%div{id: dom_id(@project)} %div{id: dom_id(@project)}
#commits-list= render "commits" #commits-list= render "commits"
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%div.ui-box %div.ui-box
%h5.title %h5.title
Commits (#{@commits.count}) Commits (#{@commits.count})
%ul.well-list= render @commits %ul.well-list= render Commit.decorate(@commits)
- unless @diffs.empty? - unless @diffs.empty?
%h4 Diff %h4 Diff
......
- commit = CommitDecorator.decorate(commit)
%li.commit %li.commit
%p %p
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.form-horizontal= render "shared/clone_panel" .form-horizontal= render "shared/clone_panel"
.span4.pull-right .span4.pull-right
.pull-right .pull-right
- unless @project.empty_repo? - if @project.empty_repo?
- if can? current_user, :download_code, @project - if can? current_user, :download_code, @project
= link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do = link_to archive_project_repository_path(@project), class: "btn-small btn grouped" do
%i.icon-download-alt %i.icon-download-alt
......
- commit = Commit.new(branch.commit) - commit = Commit.new(Gitlab::Git::Commit.new(branch.commit))
- commit = CommitDecorator.decorate(commit)
%tr %tr
%td %td
= link_to project_commits_path(@project, branch.name) do = link_to project_commits_path(@project, branch.name) do
......
- commit = update - commit = update
- commit = CommitDecorator.new(commit)
%tr %tr
%td %td
= link_to project_commits_path(@project, commit.head.name) do = link_to project_commits_path(@project, commit.head.name) do
......
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
%th Last commit %th Last commit
%th %th
- @tags.each do |tag| - @tags.each do |tag|
- commit = Commit.new(tag.commit) - commit = Commit.new(Gitlab::Git::Commit.new(tag.commit))
- commit = CommitDecorator.decorate(commit)
%tr %tr
%td %td
%strong %strong
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%i.icon-angle-right %i.icon-angle-right
= link_to project_tree_path(@project, @ref) do = link_to project_tree_path(@project, @ref) do
= @project.path = @project.path
- tree.breadcrumbs(6) do |title, path| - tree_breadcrumbs(tree, 6) do |title, path|
\/ \/
%li %li
- if path - if path
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
%tr.tree-item %tr.tree-item
%td.tree-item-file-name %td.tree-item-file-name
= image_tag "file_empty.png", size: '16x16' = image_tag "file_empty.png", size: '16x16'
= link_to "..", project_tree_path(@project, tree.up_dir_path) = link_to "..", project_tree_path(@project, up_dir_path(tree))
%td %td
%td %td
%td %td
......
%span.tree_author= commit.author_link avatar: true %span.tree_author= commit_author_link(commit, avatar: true)
= link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link" = link_to_gfm truncate(commit.title, length: 80), project_commit_path(@project, commit.id), class: "tree-commit-link"
...@@ -14,12 +14,13 @@ ...@@ -14,12 +14,13 @@
%th Format %th Format
%tbody %tbody
- @wiki.versions.each do |version| - @wiki.versions.each do |version|
- commit = CommitDecorator.new(version) - commit = version
%tr %tr
%td %td
= link_to project_wiki_path(@project, @wiki, version_id: commit.id) do = link_to project_wiki_path(@project, @wiki, version_id: commit.id) do
= commit.short_id = commit.short_id
%td= commit.author_link avatar: true, size: 24 %td
= commit_author_link(commit, avatar: true, size: 24)
%td %td
= commit.title = commit.title
%td %td
......
...@@ -21,5 +21,5 @@ ...@@ -21,5 +21,5 @@
= wiki_page.created_at.to_s(:short) do = wiki_page.created_at.to_s(:short) do
(#{time_ago_in_words(wiki_page.created_at)} (#{time_ago_in_words(wiki_page.created_at)}
ago) ago)
- commit = CommitDecorator.decorate(wiki_page.version) %td
%td= commit.author_link avatar: true, size: 24 = commit_author_link(wiki_page.version, avatar: true, size: 24)
...@@ -13,5 +13,4 @@ ...@@ -13,5 +13,4 @@
= preserve do = preserve do
= render_wiki_content(@wiki) = render_wiki_content(@wiki)
- commit = CommitDecorator.new(@wiki.version) %p.time Last edited by #{commit_author_link(@wiki.version, avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago
%p.time Last edited by #{commit.author_link(avatar: true, size: 16)} #{time_ago_in_words @wiki.created_at} ago
...@@ -15,7 +15,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps ...@@ -15,7 +15,7 @@ class ProjectBrowseCommits < Spinach::FeatureSteps
end end
Then 'I see commits atom feed' do Then 'I see commits atom feed' do
commit = CommitDecorator.decorate(@project.repository.commit) commit = @project.repository.commit
page.response_headers['Content-Type'].should have_content("application/atom+xml") page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", :text => "Recent commits to #{@project.name}") page.body.should have_selector("title", :text => "Recent commits to #{@project.name}")
page.body.should have_selector("author email", :text => commit.author_email) page.body.should have_selector("author email", :text => commit.author_email)
......
...@@ -3,14 +3,14 @@ module SharedProject ...@@ -3,14 +3,14 @@ module SharedProject
# Create a project without caring about what it's called # Create a project without caring about what it's called
And "I own a project" do And "I own a project" do
@project = create(:project) @project = create(:project_with_code)
@project.team << [@user, :master] @project.team << [@user, :master]
end end
# Create a specific project called "Shop" # Create a specific project called "Shop"
And 'I own project "Shop"' do And 'I own project "Shop"' do
@project = Project.find_by_name "Shop" @project = Project.find_by_name "Shop"
@project ||= create(:project, name: "Shop") @project ||= create(:project_with_code, name: "Shop")
@project.team << [@user, :master] @project.team << [@user, :master]
end end
......
...@@ -132,7 +132,7 @@ class Userteams < Spinach::FeatureSteps ...@@ -132,7 +132,7 @@ class Userteams < Spinach::FeatureSteps
team = UserTeam.last team = UserTeam.last
team.projects.each do |project| team.projects.each do |project|
team.members.each do |member| team.members.each do |member|
3.times { project.merge_requests << create(:merge_request, assignee: member) } 3.times { create(:merge_request, assignee: member, project: project) }
end end
end end
end end
...@@ -157,7 +157,7 @@ class Userteams < Spinach::FeatureSteps ...@@ -157,7 +157,7 @@ class Userteams < Spinach::FeatureSteps
team = UserTeam.last team = UserTeam.last
team.projects.each do |project| team.projects.each do |project|
team.members.each do |member| team.members.each do |member|
3.times { project.merge_requests << create(:merge_request, assignee: member) } 3.times { create(:merge_request, assignee: member, project: project) }
end end
end end
end end
......
...@@ -14,7 +14,7 @@ require 'spinach/capybara' ...@@ -14,7 +14,7 @@ require 'spinach/capybara'
require 'sidekiq/testing/inline' require 'sidekiq/testing/inline'
%w(stubbed_repository valid_commit select2_helper).each do |f| %w(valid_commit select2_helper test_env).each do |f|
require Rails.root.join('spec', 'support', f) require Rails.root.join('spec', 'support', f)
end end
...@@ -35,13 +35,8 @@ Capybara.default_wait_time = 10 ...@@ -35,13 +35,8 @@ Capybara.default_wait_time = 10
DatabaseCleaner.strategy = :truncation DatabaseCleaner.strategy = :truncation
Spinach.hooks.before_scenario do Spinach.hooks.before_scenario do
# Use tmp dir for FS manipulations TestEnv.init
Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
Gitlab::Shell.any_instance.stub(:add_repository) do |path|
create_temp_repo("#{Rails.root}/tmp/test-git-base-path/#{path}.git")
end
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
DatabaseCleaner.start DatabaseCleaner.start
end end
...@@ -54,9 +49,3 @@ Spinach.hooks.before_run do ...@@ -54,9 +49,3 @@ Spinach.hooks.before_run do
include FactoryGirl::Syntax::Methods include FactoryGirl::Syntax::Methods
end end
def create_temp_repo(path)
FileUtils.mkdir_p path
command = "git init --quiet --bare #{path};"
system(command)
end
...@@ -372,7 +372,7 @@ module Gitlab ...@@ -372,7 +372,7 @@ module Gitlab
ref = params[:ref_name] || user_project.try(:default_branch) || 'master' ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
commits = user_project.repository.commits(ref, nil, per_page, page * per_page) commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
present CommitDecorator.decorate(commits), with: Entities::RepoCommit present commits, with: Entities::RepoCommit
end end
# Get a project snippets # Get a project snippets
......
...@@ -85,8 +85,8 @@ module ExtractsPath ...@@ -85,8 +85,8 @@ module ExtractsPath
# - @id - A string representing the joined ref and path # - @id - A string representing the joined ref and path
# - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA) # - @ref - A string representing the ref (e.g., the branch, tag, or commit SHA)
# - @path - A string representing the filesystem path # - @path - A string representing the filesystem path
# - @commit - A CommitDecorator representing the commit from the given ref # - @commit - A Commit representing the commit from the given ref
# - @tree - A TreeDecorator representing the tree at the given ref/path # - @tree - A Tree representing the tree at the given ref/path
# #
# If the :id parameter appears to be requesting a specific response format, # If the :id parameter appears to be requesting a specific response format,
# that will be handled as well. # that will be handled as well.
...@@ -100,11 +100,9 @@ module ExtractsPath ...@@ -100,11 +100,9 @@ module ExtractsPath
# It is used "@project.repository.commits(@ref, @path, 1, 0)", # It is used "@project.repository.commits(@ref, @path, 1, 0)",
# because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name. # because "@project.repository.commit(@ref)" returns wrong commit when @ref is tag name.
commits = @project.repository.commits(@ref, @path, 1, 0) @commit = @project.repository.commits(@ref, @path, 1, 0).first
@commit = CommitDecorator.decorate(commits.first)
@tree = Tree.new(@commit.tree, @ref, @path) @tree = Tree.new(@commit.tree, @ref, @path)
@tree = TreeDecorator.new(@tree)
raise InvalidPathError if @tree.invalid? raise InvalidPathError if @tree.invalid?
rescue RuntimeError, NoMethodError, InvalidPathError rescue RuntimeError, NoMethodError, InvalidPathError
......
module Gitlab
module Git
class Blame
attr_accessor :repository, :sha, :path
def initialize(repository, sha, path)
@repository, @sha, @path = repository, sha, path
end
def each
raw_blame = Grit::Blob.blame(repository.repo, sha, path)
raw_blame.each do |commit, lines|
commit = Gitlab::Git::Commit.new(commit)
yield(commit, lines)
end
end
end
end
end
# Gitlab::Git::Commit is a wrapper around native Grit::Commit object
# We dont want to use grit objects inside app/
# It helps us easily migrate to rugged in future
module Gitlab
module Git
class Commit
attr_accessor :raw_commit, :head, :refs
delegate :message, :authored_date, :committed_date, :parents, :sha,
:date, :committer, :author, :diffs, :tree, :id, :stats, :to_patch,
to: :raw_commit
class << self
def find_or_first(repo, commit_id = nil, root_ref)
commit = if commit_id
repo.commit(commit_id)
else
repo.commits(root_ref).first
end
Commit.new(commit) if commit
end
def fresh_commits(repo, n = 10)
commits = repo.heads.map do |h|
repo.commits(h.name, n).map { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0...n]
end
def commits_with_refs(repo, n = 20)
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0..n]
end
def commits_since(repo, date)
commits = repo.heads.map do |h|
repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits
end
def commits(repo, ref, path = nil, limit = nil, offset = nil)
if path
repo.log(ref, path, max_count: limit, skip: offset)
elsif limit && offset
repo.commits(ref, limit, offset)
else
repo.commits(ref)
end.map{ |c| Commit.new(c) }
end
def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
end
def initialize(raw_commit, head = nil)
raise "Nil as raw commit passed" unless raw_commit
@raw_commit = raw_commit
@head = head
end
def short_id(length = 10)
id.to_s[0..length]
end
def safe_message
@safe_message ||= message
end
def created_at
committed_date
end
def author_email
author.email
end
def author_name
author.name
end
# Was this commit committed by a different person than the original author?
def different_committer?
author_name != committer_name || author_email != committer_email
end
def committer_name
committer.name
end
def committer_email
committer.email
end
def prev_commit
@prev_commit ||= if parents.present?
Commit.new(parents.first)
else
nil
end
end
def prev_commit_id
prev_commit.try :id
end
# Shows the diff between the commit's parent and the commit.
#
# Cuts out the header and stats from #to_patch and returns only the diff.
def to_diff
# see Grit::Commit#show
patch = to_patch
# discard lines before the diff
lines = patch.split("\n")
while !lines.first.start_with?("diff --git") do
lines.shift
end
lines.pop if lines.last =~ /^[\d.]+$/ # Git version
lines.pop if lines.last == "-- " # end of diff
lines.join("\n")
end
def has_zero_stats?
stats.total.zero?
rescue
true
end
def no_commit_message
"--no commit message"
end
end
end
end
module Gitlab
module Git
class Compare
attr_accessor :commits, :commit, :diffs, :same
def initialize(repository, from, to)
@commits, @diffs = [], []
@commit = nil
@same = false
return unless from && to
first = repository.commit(to.try(:strip))
last = repository.commit(from.try(:strip))
return unless first && last
if first.id == last.id
@same = true
return
end
@commit = Commit.new(first)
@commits = repository.commits_between(last.id, first.id)
@commits = @commits.map { |c| Commit.new(c) }
@diffs = if @commits.size > 100
[]
else
repository.repo.diff(last.id, first.id) rescue []
end
end
end
end
end
# Gitlab::Git::Gitlab::Git::Commit is a wrapper around native Grit::Repository object
# We dont want to use grit objects inside app/
# It helps us easily migrate to rugged in future
module Gitlab
module Git
class Repository
include Gitlab::Popen
class NoRepository < StandardError; end
# Repository directory name with namespace direcotry
# Examples:
# gitlab/gitolite
# diaspora
#
attr_accessor :path_with_namespace
# Grit repo object
attr_accessor :repo
# Default branch in the repository
attr_accessor :root_ref
def initialize(path_with_namespace, root_ref = 'master')
@root_ref = root_ref || "master"
@path_with_namespace = path_with_namespace
# Init grit repo object
repo
end
def raw
repo
end
def path_to_repo
@path_to_repo ||= File.join(repos_path, "#{path_with_namespace}.git")
end
def repos_path
Gitlab.config.gitlab_shell.repos_path
end
def repo
@repo ||= Grit::Repo.new(path_to_repo)
rescue Grit::NoSuchPathError
raise NoRepository.new('no repository for such path')
end
def commit(commit_id = nil)
Gitlab::Git::Commit.find_or_first(repo, commit_id, root_ref)
end
def fresh_commits(n = 10)
Gitlab::Git::Commit.fresh_commits(repo, n)
end
def commits_with_refs(n = 20)
Gitlab::Git::Commit.commits_with_refs(repo, n)
end
def commits_since(date)
Gitlab::Git::Commit.commits_since(repo, date)
end
def commits(ref, path = nil, limit = nil, offset = nil)
Gitlab::Git::Commit.commits(repo, ref, path, limit, offset)
end
def last_commit_for(ref, path = nil)
commits(ref, path, 1).first
end
def commits_between(from, to)
Gitlab::Git::Commit.commits_between(repo, from, to)
end
# Returns an Array of branch names
# sorted by name ASC
def branch_names
branches.map(&:name)
end
# Returns an Array of Branches
def branches
repo.branches.sort_by(&:name)
end
# Returns an Array of tag names
def tag_names
repo.tags.collect(&:name).sort.reverse
end
# Returns an Array of Tags
def tags
repo.tags.sort_by(&:name).reverse
end
# Returns an Array of branch and tag names
def ref_names
[branch_names + tag_names].flatten
end
def heads
@heads ||= repo.heads
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
path ? (tree / path) : tree
end
def has_commits?
!!commit
rescue Grit::NoSuchPathError
false
end
def empty?
!has_commits?
end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
if branch_names.length == 0
nil
elsif branch_names.length == 1
branch_names.first
else
branch_names.select { |v| v == root_ref }.first
end
end
# Archive Project to .tar.gz
#
# Already packed repo archives stored at
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
#
def archive_repo(ref)
ref = ref || self.root_ref
commit = self.commit(ref)
return nil unless commit
# Build file path
file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
storage_path = Rails.root.join("tmp", "repositories")
file_path = File.join(storage_path, self.path_with_namespace, file_name)
# Put files into a directory before archiving
prefix = File.basename(self.path_with_namespace) + "/"
# Create file if not exists
unless File.exists?(file_path)
FileUtils.mkdir_p File.dirname(file_path)
file = self.repo.archive_to_file(ref, prefix, file_path)
end
file_path
end
# Return repo size in megabytes
# Cached in redis
def size
Rails.cache.fetch(cache_key(:size)) do
size = popen('du -s', path_to_repo).first.strip.to_i
(size.to_f / 1024).round(2)
end
end
def expire_cache
Rails.cache.delete(cache_key(:size))
end
def cache_key(type)
"#{type}:#{path_with_namespace}"
end
end
end
end
...@@ -187,7 +187,7 @@ module Gitlab ...@@ -187,7 +187,7 @@ module Gitlab
def reference_commit(identifier) def reference_commit(identifier)
if @project.valid_repo? && commit = @project.repository.commit(identifier) if @project.valid_repo? && commit = @project.repository.commit(identifier)
link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: CommitDecorator.new(commit).link_title, class: "gfm gfm-commit #{html_options[:class]}")) link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}"))
end end
end end
end end
......
namespace :cache do
desc "GITLAB | Clear redis cache"
task :clear => :environment do
Rails.cache.clear
end
end
require 'spec_helper' require 'spec_helper'
describe CommitController do describe CommitController do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:commit) { project.repository.last_commit_for("master") } let(:commit) { project.repository.last_commit_for("master") }
......
require 'spec_helper' require 'spec_helper'
describe CommitsController do describe CommitsController do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
require 'spec_helper' require 'spec_helper'
describe MergeRequestsController do describe MergeRequestsController do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") } let(:merge_request) { create(:merge_request_with_diffs, project: project, target_branch: "bcf03b5d~3", source_branch: "bcf03b5d") }
......
require 'spec_helper' require 'spec_helper'
describe TreeController do describe TreeController do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -34,6 +34,10 @@ FactoryGirl.define do ...@@ -34,6 +34,10 @@ FactoryGirl.define do
issues_tracker_id { "project_name_in_redmine" } issues_tracker_id { "project_name_in_redmine" }
end end
factory :project_with_code, parent: :project do
path { 'gitlabhq' }
end
factory :group do factory :group do
sequence(:name) { |n| "group#{n}" } sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
...@@ -73,7 +77,7 @@ FactoryGirl.define do ...@@ -73,7 +77,7 @@ FactoryGirl.define do
factory :merge_request do factory :merge_request do
title title
author author
project project factory: :project_with_code
source_branch "master" source_branch "master"
target_branch "stable" target_branch "stable"
...@@ -82,9 +86,9 @@ FactoryGirl.define do ...@@ -82,9 +86,9 @@ FactoryGirl.define do
target_branch "master" # pretend bcf03b5d~3 target_branch "master" # pretend bcf03b5d~3
source_branch "stable" # pretend bcf03b5d source_branch "stable" # pretend bcf03b5d
st_commits do st_commits do
[Commit.new(project.repo.commit('bcf03b5d')), [Commit.new(project.repository.commit('bcf03b5d')),
Commit.new(project.repo.commit('bcf03b5d~1')), Commit.new(project.repository.commit('bcf03b5d~1')),
Commit.new(project.repo.commit('bcf03b5d~2'))] Commit.new(project.repository.commit('bcf03b5d~2'))]
end end
st_diffs do st_diffs do
project.repo.diff("bcf03b5d~3", "bcf03b5d") project.repo.diff("bcf03b5d~3", "bcf03b5d")
...@@ -116,6 +120,7 @@ FactoryGirl.define do ...@@ -116,6 +120,7 @@ FactoryGirl.define do
factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff] factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
trait :on_commit do trait :on_commit do
project factory: :project_with_code
commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" commit_id "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
noteable_type "Commit" noteable_type "Commit"
end end
...@@ -125,6 +130,7 @@ FactoryGirl.define do ...@@ -125,6 +130,7 @@ FactoryGirl.define do
end end
trait :on_merge_request do trait :on_merge_request do
project factory: :project_with_code
noteable_id 1 noteable_id 1
noteable_type "MergeRequest" noteable_type "MergeRequest"
end end
......
require 'spec_helper' require 'spec_helper'
describe "Gitlab Flavored Markdown" do describe "Gitlab Flavored Markdown" do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) } let(:merge_request) { create(:merge_request, project: project) }
let(:fred) do let(:fred) do
......
require 'spec_helper' require 'spec_helper'
describe "On a merge request", js: true do describe "On a merge request", js: true do
let!(:project) { create(:project) } let!(:project) { create(:project_with_code) }
let!(:merge_request) { create(:merge_request, project: project) } let!(:merge_request) { create(:merge_request, project: project) }
before do before do
...@@ -83,7 +83,7 @@ end ...@@ -83,7 +83,7 @@ end
describe "On a merge request diff", js: true, focus: true do describe "On a merge request diff", js: true, focus: true do
let!(:project) { create(:project) } let!(:project) { create(:project_with_code) }
let!(:merge_request) { create(:merge_request_with_diffs, project: project) } let!(:merge_request) { create(:merge_request_with_diffs, project: project) }
before do before do
......
...@@ -12,8 +12,9 @@ describe "Profile account page" do ...@@ -12,8 +12,9 @@ describe "Profile account page" do
Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
visit account_profile_path visit account_profile_path
end end
it { page.should have_content("Remove account") } it { page.should have_content("Remove account") }
it "should delete the account", js: true do it "should delete the account", js: true do
expect { click_link "Delete account" }.to change {User.count}.by(-1) expect { click_link "Delete account" }.to change {User.count}.by(-1)
current_path.should == new_user_session_path current_path.should == new_user_session_path
...@@ -45,4 +46,4 @@ describe "Profile account page" do ...@@ -45,4 +46,4 @@ describe "Profile account page" do
current_path.should == account_profile_path current_path.should == account_profile_path
end end
end end
end end
\ No newline at end of file
...@@ -14,7 +14,7 @@ describe "Application access" do ...@@ -14,7 +14,7 @@ describe "Application access" do
end end
describe "Project" do describe "Project" do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:master) { create(:user) } let(:master) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
......
...@@ -4,10 +4,10 @@ describe GitlabMarkdownHelper do ...@@ -4,10 +4,10 @@ describe GitlabMarkdownHelper do
include ApplicationHelper include ApplicationHelper
include IssuesHelper include IssuesHelper
let!(:project) { create(:project) } let!(:project) { create(:project_with_code) }
let(:user) { create(:user, username: 'gfm') } let(:user) { create(:user, username: 'gfm') }
let(:commit) { CommitDecorator.decorate(project.repository.commit) } let(:commit) { project.repository.commit }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, project: project) } let(:merge_request) { create(:merge_request, project: project) }
let(:snippet) { create(:snippet, project: project) } let(:snippet) { create(:snippet, project: project) }
......
require "spec_helper"
describe Gitlab::Git::Commit do
let(:commit) { create(:project_with_code).repository.commit }
describe "Commit info" do
before do
@committer = double(
email: 'mike@smith.com',
name: 'Mike Smith'
)
@author = double(
email: 'john@smith.com',
name: 'John Smith'
)
@raw_commit = double(
id: "bcf03b5de6abcf03b5de6c",
author: @author,
committer: @committer,
committed_date: Date.yesterday,
message: 'Refactoring specs'
)
@commit = Gitlab::Git::Commit.new(@raw_commit)
end
it { @commit.short_id.should == "bcf03b5de6a" }
it { @commit.safe_message.should == @raw_commit.message }
it { @commit.created_at.should == @raw_commit.committed_date }
it { @commit.author_email.should == @author.email }
it { @commit.author_name.should == @author.name }
it { @commit.committer_name.should == @committer.name }
it { @commit.committer_email.should == @committer.email }
it { @commit.different_committer?.should be_true }
end
describe "Class methods" do
subject { Gitlab::Git::Commit }
it { should respond_to(:find_or_first) }
it { should respond_to(:fresh_commits) }
it { should respond_to(:commits_with_refs) }
it { should respond_to(:commits_since) }
it { should respond_to(:commits_between) }
it { should respond_to(:commits) }
end
end
require "spec_helper" require "spec_helper"
describe Repository do describe Gitlab::Git::Repository do
let(:project) { create(:project) } let(:repository) { Gitlab::Git::Repository.new('gitlabhq', 'master') }
let(:repository) { project.repository }
describe "Respond to" do describe "Respond to" do
subject { repository } subject { repository }
......
...@@ -5,7 +5,7 @@ describe Notify do ...@@ -5,7 +5,7 @@ describe Notify do
include EmailSpec::Matchers include EmailSpec::Matchers
let(:recipient) { create(:user, email: 'recipient@example.com') } let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
shared_examples 'a multiple recipients email' do shared_examples 'a multiple recipients email' do
it 'is sent to the given recipient' do it 'is sent to the given recipient' do
...@@ -277,14 +277,7 @@ describe Notify do ...@@ -277,14 +277,7 @@ describe Notify do
end end
describe 'on a commit' do describe 'on a commit' do
let(:commit) do let(:commit) { project.repository.commit }
mock(:commit).tap do |commit|
commit.stub(:id).and_return('fauxsha1')
commit.stub(:project).and_return(project)
commit.stub(:short_id).and_return('fauxsha1')
commit.stub(:safe_message).and_return('some message')
end
end
before(:each) { note.stub(:noteable).and_return(commit) } before(:each) { note.stub(:noteable).and_return(commit) }
...@@ -297,7 +290,7 @@ describe Notify do ...@@ -297,7 +290,7 @@ describe Notify do
end end
it 'contains a link to the commit' do it 'contains a link to the commit' do
should have_body_text /fauxsha1/ should have_body_text commit.short_id
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Commit do describe Commit do
let(:commit) { create(:project).repository.commit } let(:commit) { create(:project_with_code).repository.commit }
describe CommitDecorator do
let(:decorator) { CommitDecorator.new(commit) }
describe '#title' do describe '#title' do
it "returns no_commit_message when safe_message is blank" do it "returns no_commit_message when safe_message is blank" do
decorator.stub(:safe_message).and_return('') commit.stub(:safe_message).and_return('')
decorator.title.should == "--no commit message" commit.title.should == "--no commit message"
end
it "truncates a message without a newline at 70 characters" do
message = commit.safe_message * 10
decorator.stub(:safe_message).and_return(message)
decorator.title.should == "#{message[0..69]}&hellip;"
end
it "truncates a message with a newline before 80 characters at the newline" do
message = commit.safe_message.split(" ").first
decorator.stub(:safe_message).and_return(message + "\n" + message)
decorator.title.should == message
end
it "truncates a message with a newline after 80 characters at 70 characters" do
message = (commit.safe_message * 10) + "\n"
decorator.stub(:safe_message).and_return(message)
decorator.title.should == "#{message[0..69]}&hellip;"
end
end end
end
describe "Commit info" do it "truncates a message without a newline at 70 characters" do
before do message = commit.safe_message * 10
@committer = double(
email: 'mike@smith.com',
name: 'Mike Smith'
)
@author = double( commit.stub(:safe_message).and_return(message)
email: 'john@smith.com', commit.title.should == "#{message[0..69]}&hellip;"
name: 'John Smith' end
)
@raw_commit = double( it "truncates a message with a newline before 80 characters at the newline" do
id: "bcf03b5de6abcf03b5de6c", message = commit.safe_message.split(" ").first
author: @author,
committer: @committer,
committed_date: Date.yesterday,
message: 'Refactoring specs'
)
@commit = Commit.new(@raw_commit) commit.stub(:safe_message).and_return(message + "\n" + message)
commit.title.should == message
end end
it { @commit.short_id.should == "bcf03b5de6a" } it "truncates a message with a newline after 80 characters at 70 characters" do
it { @commit.safe_message.should == @raw_commit.message } message = (commit.safe_message * 10) + "\n"
it { @commit.created_at.should == @raw_commit.committed_date }
it { @commit.author_email.should == @author.email }
it { @commit.author_name.should == @author.name }
it { @commit.committer_name.should == @committer.name }
it { @commit.committer_email.should == @committer.email }
it { @commit.different_committer?.should be_true }
end
describe "Class methods" do commit.stub(:safe_message).and_return(message)
subject { Commit } commit.title.should == "#{message[0..69]}&hellip;"
end
it { should respond_to(:find_or_first) }
it { should respond_to(:fresh_commits) }
it { should respond_to(:commits_with_refs) }
it { should respond_to(:commits_since) }
it { should respond_to(:commits_between) }
it { should respond_to(:commits) }
it { should respond_to(:compare) }
end end
describe "delegation" do describe "delegation" do
......
...@@ -81,7 +81,7 @@ describe GollumWiki do ...@@ -81,7 +81,7 @@ describe GollumWiki do
end end
it "raises CouldNotCreateWikiError if it can't create the wiki repository" do it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
Gitlab::Shell.any_instance.stub(:add_repository).and_return(false) GollumWiki.any_instance.stub(:init_repo).and_return(false)
expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError) expect { GollumWiki.new(project, user).wiki }.to raise_exception(GollumWiki::CouldNotCreateWikiError)
end end
end end
......
...@@ -119,7 +119,7 @@ describe Project do ...@@ -119,7 +119,7 @@ describe Project do
end end
describe :update_merge_requests do describe :update_merge_requests do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
before do before do
@merge_request = create(:merge_request, project: project) @merge_request = create(:merge_request, project: project)
...@@ -189,10 +189,6 @@ describe Project do ...@@ -189,10 +189,6 @@ describe Project do
it "should return valid repo" do it "should return valid repo" do
project.repository.should be_kind_of(Repository) project.repository.should be_kind_of(Repository)
end end
it "should return nil" do
Project.new(path: "empty").repository.should be_nil
end
end end
describe :issue_exists? do describe :issue_exists? do
...@@ -249,7 +245,7 @@ describe Project do ...@@ -249,7 +245,7 @@ describe Project do
end end
describe :open_branches do describe :open_branches do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
before do before do
project.protected_branches.create(name: 'master') project.protected_branches.create(name: 'master')
......
...@@ -4,7 +4,7 @@ describe Gitlab::API do ...@@ -4,7 +4,7 @@ describe Gitlab::API do
include ApiHelpers include ApiHelpers
let(:user) { create(:user ) } let(:user) { create(:user ) }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project_with_code, creator_id: user.id) }
let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") } let!(:merge_request) { create(:merge_request, author: user, assignee: user, project: project, title: "Test") }
before { project.team << [user, :reporters] } before { project.team << [user, :reporters] }
......
...@@ -7,7 +7,7 @@ describe Gitlab::API do ...@@ -7,7 +7,7 @@ describe Gitlab::API do
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:user3) { create(:user) } let(:user3) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project_with_code, creator_id: user.id) }
let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') }
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe GitPushService do describe GitPushService do
let (:user) { create :user } let (:user) { create :user }
let (:project) { create :project } let (:project) { create :project_with_code }
let (:service) { GitPushService.new } let (:service) { GitPushService.new }
before do before do
......
...@@ -47,11 +47,7 @@ Spork.prefork do ...@@ -47,11 +47,7 @@ Spork.prefork do
config.use_transactional_fixtures = false config.use_transactional_fixtures = false
config.before do config.before do
# Use tmp dir for FS manipulations TestEnv.init
temp_repos_path = Rails.root.join('tmp', 'test-git-base-path')
Gitlab.config.gitlab_shell.stub(repos_path: temp_repos_path)
FileUtils.rm_rf temp_repos_path
FileUtils.mkdir_p temp_repos_path
end end
end end
end end
......
require "repository"
require "project"
require "merge_request"
require "shell"
# Stubs out all Git repository access done by models so that specs can run
# against fake repositories without Grit complaining that they don't exist.
class Project
def repository
if path == "empty" || !path
nil
else
GitLabTestRepo.new(path_with_namespace)
end
end
def satellite
FakeSatellite.new
end
class FakeSatellite
def exists?
true
end
def destroy
true
end
def create
true
end
end
end
class MergeRequest
def check_if_can_be_merged
true
end
end
class GitLabTestRepo < Repository
def repo
@repo ||= Grit::Repo.new(Rails.root.join('tmp', 'repositories', 'gitlabhq'))
end
# patch repo size (in mb)
def size
12.45
end
end
module Gitlab
class Shell
def add_repository name
true
end
def mv_repository name, new_name
true
end
def remove_repository name
true
end
def add_key id, key
true
end
def remove_key id, key
true
end
end
end
module TestEnv
extend self
# Test environment
#
# all repositories and namespaces stored at
# RAILS_APP/tmp/test-git-base-path
#
# Next shell methods are stubbed and return true
# - mv_repository
# - remove_repository
# - add_key
# - remove_key
#
def init
# Use tmp dir for FS manipulations
repos_path = Rails.root.join('tmp', 'test-git-base-path')
Gitlab.config.gitlab_shell.stub(repos_path: repos_path)
GollumWiki.any_instance.stub(:init_repo) do |path|
create_temp_repo(File.join(repos_path, "#{path}.git"))
end
Gitlab::Shell.any_instance.stub(
add_repository: true,
mv_repository: true,
remove_repository: true,
add_key: true,
remove_key: true
)
Gitlab::Satellite::Satellite.any_instance.stub(
exists?: true,
destroy: true,
create: true
)
MergeRequest.any_instance.stub(
check_if_can_be_merged: true
)
Repository.any_instance.stub(
size: 12.45
)
# Remove tmp/test-git-base-path
FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
# Recreate tmp/test-git-base-path
FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
# Symlink tmp/repositories/gitlabhq to tmp/test-git-base-path/gitlabhq
seed_repo = Rails.root.join('tmp', 'repositories', 'gitlabhq')
target_repo = File.join(repos_path, 'gitlabhq.git')
system("ln -s #{seed_repo} #{target_repo}")
end
def create_temp_repo(path)
FileUtils.mkdir_p path
command = "git init --quiet --bare #{path};"
system(command)
end
end
...@@ -9,7 +9,7 @@ describe PostReceive do ...@@ -9,7 +9,7 @@ describe PostReceive do
end end
context "web hook" do context "web hook" do
let(:project) { create(:project) } let(:project) { create(:project_with_code) }
let(:key) { create(:key, user: project.owner) } let(:key) { create(:key, user: project.owner) }
let(:key_id) { key.shell_id } let(:key_id) { key.shell_id }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment