Merge branch 'dm-fix-web-edit-new-lines' into 'master'
Consistently create, update, and delete files, taking CRLF settings into account See merge request !9207
This commit is contained in:
commit
6e2f2f5da7
28 changed files with 497 additions and 787 deletions
|
@ -746,136 +746,63 @@ class Repository
|
|||
@tags ||= raw_repository.tags
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def commit_dir(
|
||||
user, path,
|
||||
message:, branch_name:,
|
||||
author_email: nil, author_name: nil,
|
||||
start_branch_name: nil, start_project: project)
|
||||
check_tree_entry_for_dir(branch_name, path)
|
||||
def create_dir(user, path, **options)
|
||||
options[:user] = user
|
||||
options[:actions] = [{ action: :create_dir, file_path: path }]
|
||||
|
||||
if start_branch_name
|
||||
start_project.repository.
|
||||
check_tree_entry_for_dir(start_branch_name, path)
|
||||
end
|
||||
|
||||
commit_file(
|
||||
user,
|
||||
"#{path}/.gitkeep",
|
||||
'',
|
||||
message: message,
|
||||
branch_name: branch_name,
|
||||
update: false,
|
||||
author_email: author_email,
|
||||
author_name: author_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project)
|
||||
multi_action(**options)
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def commit_file(
|
||||
user, path, content,
|
||||
message:, branch_name:, update: true,
|
||||
author_email: nil, author_name: nil,
|
||||
start_branch_name: nil, start_project: project)
|
||||
unless update
|
||||
error_message = "Filename already exists; update not allowed"
|
||||
def create_file(user, path, content, **options)
|
||||
options[:user] = user
|
||||
options[:actions] = [{ action: :create, file_path: path, content: content }]
|
||||
|
||||
if tree_entry_at(branch_name, path)
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
|
||||
end
|
||||
|
||||
if start_branch_name &&
|
||||
start_project.repository.tree_entry_at(start_branch_name, path)
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
|
||||
end
|
||||
end
|
||||
|
||||
multi_action(
|
||||
user: user,
|
||||
message: message,
|
||||
branch_name: branch_name,
|
||||
author_email: author_email,
|
||||
author_name: author_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project,
|
||||
actions: [{ action: :create,
|
||||
file_path: path,
|
||||
content: content }])
|
||||
multi_action(**options)
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def update_file(
|
||||
user, path, content,
|
||||
message:, branch_name:, previous_path:,
|
||||
author_email: nil, author_name: nil,
|
||||
start_branch_name: nil, start_project: project)
|
||||
action = if previous_path && previous_path != path
|
||||
:move
|
||||
else
|
||||
:update
|
||||
end
|
||||
def update_file(user, path, content, **options)
|
||||
previous_path = options.delete(:previous_path)
|
||||
action = previous_path && previous_path != path ? :move : :update
|
||||
|
||||
multi_action(
|
||||
user: user,
|
||||
message: message,
|
||||
branch_name: branch_name,
|
||||
author_email: author_email,
|
||||
author_name: author_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project,
|
||||
actions: [{ action: action,
|
||||
file_path: path,
|
||||
content: content,
|
||||
previous_path: previous_path }])
|
||||
options[:user] = user
|
||||
options[:actions] = [{ action: action, file_path: path, previous_path: previous_path, content: content }]
|
||||
|
||||
multi_action(**options)
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def remove_file(
|
||||
user, path,
|
||||
message:, branch_name:,
|
||||
author_email: nil, author_name: nil,
|
||||
start_branch_name: nil, start_project: project)
|
||||
multi_action(
|
||||
user: user,
|
||||
message: message,
|
||||
branch_name: branch_name,
|
||||
author_email: author_email,
|
||||
author_name: author_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project,
|
||||
actions: [{ action: :delete,
|
||||
file_path: path }])
|
||||
def delete_file(user, path, **options)
|
||||
options[:user] = user
|
||||
options[:actions] = [{ action: :delete, file_path: path }]
|
||||
|
||||
multi_action(**options)
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def multi_action(
|
||||
user:, branch_name:, message:, actions:,
|
||||
author_email: nil, author_name: nil,
|
||||
start_branch_name: nil, start_project: project)
|
||||
|
||||
GitOperationService.new(user, self).with_branch(
|
||||
branch_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project) do |start_commit|
|
||||
index = rugged.index
|
||||
|
||||
parents = if start_commit
|
||||
index.read_tree(start_commit.raw_commit.tree)
|
||||
[start_commit.sha]
|
||||
else
|
||||
[]
|
||||
end
|
||||
index = Gitlab::Git::Index.new(raw_repository)
|
||||
|
||||
actions.each do |act|
|
||||
git_action(index, act)
|
||||
if start_commit
|
||||
index.read_tree(start_commit.raw_commit.tree)
|
||||
parents = [start_commit.sha]
|
||||
else
|
||||
parents = []
|
||||
end
|
||||
|
||||
actions.each do |options|
|
||||
index.public_send(options.delete(:action), options)
|
||||
end
|
||||
|
||||
options = {
|
||||
tree: index.write_tree(rugged),
|
||||
tree: index.write_tree,
|
||||
message: message,
|
||||
parents: parents
|
||||
}
|
||||
|
@ -1166,30 +1093,6 @@ class Repository
|
|||
blob_data_at(sha, '.gitlab-ci.yml')
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def tree_entry_at(branch_name, path)
|
||||
branch_exists?(branch_name) &&
|
||||
# tree_entry is private
|
||||
raw_repository.send(:tree_entry, commit(branch_name), path)
|
||||
end
|
||||
|
||||
def check_tree_entry_for_dir(branch_name, path)
|
||||
return unless branch_exists?(branch_name)
|
||||
|
||||
entry = tree_entry_at(branch_name, path)
|
||||
|
||||
return unless entry
|
||||
|
||||
if entry[:type] == :blob
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new(
|
||||
"Directory already exists as a file")
|
||||
else
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new(
|
||||
"Directory already exists")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def blob_data_at(sha, path)
|
||||
|
@ -1200,58 +1103,6 @@ class Repository
|
|||
blob.data
|
||||
end
|
||||
|
||||
def git_action(index, action)
|
||||
path = normalize_path(action[:file_path])
|
||||
|
||||
if action[:action] == :move
|
||||
previous_path = normalize_path(action[:previous_path])
|
||||
end
|
||||
|
||||
case action[:action]
|
||||
when :create, :update, :move
|
||||
mode =
|
||||
case action[:action]
|
||||
when :update
|
||||
index.get(path)[:mode]
|
||||
when :move
|
||||
index.get(previous_path)[:mode]
|
||||
end
|
||||
mode ||= 0o100644
|
||||
|
||||
index.remove(previous_path) if action[:action] == :move
|
||||
|
||||
content = if action[:encoding] == 'base64'
|
||||
Base64.decode64(action[:content])
|
||||
else
|
||||
action[:content]
|
||||
end
|
||||
|
||||
detect = CharlockHolmes::EncodingDetector.new.detect(content) if content
|
||||
|
||||
unless detect && detect[:type] == :binary
|
||||
# When writing to the repo directly as we are doing here,
|
||||
# the `core.autocrlf` config isn't taken into account.
|
||||
content.gsub!("\r\n", "\n") if self.autocrlf
|
||||
end
|
||||
|
||||
oid = rugged.write(content, :blob)
|
||||
|
||||
index.add(path: path, oid: oid, mode: mode)
|
||||
when :delete
|
||||
index.remove(path)
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_path(path)
|
||||
pathname = Gitlab::Git::PathHelper.normalize_path(path)
|
||||
|
||||
if pathname.each_filename.include?('..')
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path')
|
||||
end
|
||||
|
||||
pathname.to_s
|
||||
end
|
||||
|
||||
def refs_directory_exists?
|
||||
return false unless path_with_namespace
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Files
|
||||
class CreateDirService < Files::BaseService
|
||||
def commit
|
||||
repository.commit_dir(
|
||||
repository.create_dir(
|
||||
current_user,
|
||||
@file_path,
|
||||
message: @commit_message,
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
module Files
|
||||
class CreateService < Files::BaseService
|
||||
def commit
|
||||
repository.commit_file(
|
||||
repository.create_file(
|
||||
current_user,
|
||||
@file_path,
|
||||
@file_content,
|
||||
message: @commit_message,
|
||||
branch_name: @target_branch,
|
||||
update: false,
|
||||
author_email: @author_email,
|
||||
author_name: @author_name,
|
||||
start_project: @start_project,
|
||||
|
@ -17,6 +16,10 @@ module Files
|
|||
def validate
|
||||
super
|
||||
|
||||
if @file_content.nil?
|
||||
raise_error("You must provide content.")
|
||||
end
|
||||
|
||||
if @file_path =~ Gitlab::Regex.directory_traversal_regex
|
||||
raise_error(
|
||||
'Your changes could not be committed, because the file name ' +
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Files
|
||||
class DestroyService < Files::BaseService
|
||||
def commit
|
||||
repository.remove_file(
|
||||
repository.delete_file(
|
||||
current_user,
|
||||
@file_path,
|
||||
message: @commit_message,
|
||||
|
|
|
@ -2,6 +2,8 @@ module Files
|
|||
class MultiService < Files::BaseService
|
||||
class FileChangedError < StandardError; end
|
||||
|
||||
ACTIONS = %w[create update delete move].freeze
|
||||
|
||||
def commit
|
||||
repository.multi_action(
|
||||
user: current_user,
|
||||
|
@ -21,10 +23,19 @@ module Files
|
|||
super
|
||||
|
||||
params[:actions].each_with_index do |action, index|
|
||||
if ACTIONS.include?(action[:action].to_s)
|
||||
action[:action] = action[:action].to_sym
|
||||
else
|
||||
raise_error("Unknown action type `#{action[:action]}`.")
|
||||
end
|
||||
|
||||
unless action[:file_path].present?
|
||||
raise_error("You must specify a file_path.")
|
||||
end
|
||||
|
||||
action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/')
|
||||
action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/')
|
||||
|
||||
regex_check(action[:file_path])
|
||||
regex_check(action[:previous_path]) if action[:previous_path]
|
||||
|
||||
|
@ -43,8 +54,6 @@ module Files
|
|||
validate_delete(action)
|
||||
when :move
|
||||
validate_move(action, index)
|
||||
else
|
||||
raise_error("Unknown action type `#{action[:action]}`.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -92,6 +101,20 @@ module Files
|
|||
if repository.blob_at_branch(params[:branch], action[:file_path])
|
||||
raise_error("Your changes could not be committed because a file with the name `#{action[:file_path]}` already exists.")
|
||||
end
|
||||
|
||||
if action[:content].nil?
|
||||
raise_error("You must provide content.")
|
||||
end
|
||||
end
|
||||
|
||||
def validate_update(action)
|
||||
if action[:content].nil?
|
||||
raise_error("You must provide content.")
|
||||
end
|
||||
|
||||
if file_has_changed?
|
||||
raise FileChangedError.new("You are attempting to update a file `#{action[:file_path]}` that has changed since you started editing it.")
|
||||
end
|
||||
end
|
||||
|
||||
def validate_delete(action)
|
||||
|
@ -114,11 +137,5 @@ module Files
|
|||
params[:actions][index][:content] = blob.data
|
||||
end
|
||||
end
|
||||
|
||||
def validate_update(action)
|
||||
if file_has_changed?
|
||||
raise FileChangedError.new("You are attempting to update a file `#{action[:file_path]}` that has changed since you started editing it.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,10 @@ module Files
|
|||
def validate
|
||||
super
|
||||
|
||||
if @file_content.nil?
|
||||
raise_error("You must provide content.")
|
||||
end
|
||||
|
||||
if file_has_changed?
|
||||
raise FileChangedError.new("You are attempting to update a file that has changed since you started editing it.")
|
||||
end
|
||||
|
|
|
@ -155,17 +155,9 @@ class Gitlab::Seeder::CycleAnalytics
|
|||
|
||||
issue.project.repository.add_branch(@user, branch_name, 'master')
|
||||
|
||||
options = {
|
||||
committer: issue.project.repository.user_to_committer(@user),
|
||||
author: issue.project.repository.user_to_committer(@user),
|
||||
commit: { message: "Commit for ##{issue.iid}", branch: branch_name, update_ref: true },
|
||||
file: { content: "content", path: filename, update: false }
|
||||
}
|
||||
|
||||
commit_sha = Gitlab::Git::Blob.commit(issue.project.repository, options)
|
||||
commit_sha = issue.project.repository.create_file(@user, filename, "content", options, message: "Commit for ##{issue.iid}", branch_name: branch_name)
|
||||
issue.project.repository.commit(commit_sha)
|
||||
|
||||
|
||||
GitPushService.new(issue.project,
|
||||
@user,
|
||||
oldrev: issue.project.repository.commit("master").sha,
|
||||
|
|
|
@ -52,13 +52,6 @@ module API
|
|||
|
||||
attrs = declared_params.merge(start_branch: declared_params[:branch], target_branch: declared_params[:branch])
|
||||
|
||||
attrs[:actions].map! do |action|
|
||||
action[:action] = action[:action].to_sym
|
||||
action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/')
|
||||
action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/')
|
||||
action
|
||||
end
|
||||
|
||||
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
|
||||
|
||||
if result[:status] == :success
|
||||
|
|
|
@ -55,13 +55,6 @@ module API
|
|||
branch = attrs.delete(:branch_name)
|
||||
attrs.merge!(branch: branch, start_branch: branch, target_branch: branch)
|
||||
|
||||
attrs[:actions].map! do |action|
|
||||
action[:action] = action[:action].to_sym
|
||||
action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/')
|
||||
action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/')
|
||||
action
|
||||
end
|
||||
|
||||
result = ::Files::MultiService.new(user_project, current_user, attrs).execute
|
||||
|
||||
if result[:status] == :success
|
||||
|
|
|
@ -93,163 +93,6 @@ module Gitlab
|
|||
commit_id: sha,
|
||||
)
|
||||
end
|
||||
|
||||
# Commit file in repository and return commit sha
|
||||
#
|
||||
# options should contain next structure:
|
||||
# file: {
|
||||
# content: 'Lorem ipsum...',
|
||||
# path: 'documents/story.txt',
|
||||
# update: true
|
||||
# },
|
||||
# author: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# committer: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# commit: {
|
||||
# message: 'Wow such commit',
|
||||
# branch: 'master',
|
||||
# update_ref: false
|
||||
# }
|
||||
#
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def commit(repository, options, action = :add)
|
||||
file = options[:file]
|
||||
update = file[:update].nil? ? true : file[:update]
|
||||
author = options[:author]
|
||||
committer = options[:committer]
|
||||
commit = options[:commit]
|
||||
repo = repository.rugged
|
||||
ref = commit[:branch]
|
||||
update_ref = commit[:update_ref].nil? ? true : commit[:update_ref]
|
||||
parents = []
|
||||
mode = 0o100644
|
||||
|
||||
unless ref.start_with?('refs/')
|
||||
ref = 'refs/heads/' + ref
|
||||
end
|
||||
|
||||
path_name = Gitlab::Git::PathHelper.normalize_path(file[:path])
|
||||
# Abort if any invalid characters remain (e.g. ../foo)
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("Invalid path") if path_name.each_filename.to_a.include?('..')
|
||||
|
||||
filename = path_name.to_s
|
||||
index = repo.index
|
||||
|
||||
unless repo.empty?
|
||||
rugged_ref = repo.references[ref]
|
||||
raise Gitlab::Git::Repository::InvalidRef.new("Invalid branch name") unless rugged_ref
|
||||
last_commit = rugged_ref.target
|
||||
index.read_tree(last_commit.tree)
|
||||
parents = [last_commit]
|
||||
end
|
||||
|
||||
if action == :remove
|
||||
index.remove(filename)
|
||||
else
|
||||
file_entry = index.get(filename)
|
||||
|
||||
if action == :rename
|
||||
old_path_name = Gitlab::Git::PathHelper.normalize_path(file[:previous_path])
|
||||
old_filename = old_path_name.to_s
|
||||
file_entry = index.get(old_filename)
|
||||
index.remove(old_filename) unless file_entry.blank?
|
||||
end
|
||||
|
||||
if file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists; update not allowed") unless update
|
||||
|
||||
# Preserve the current file mode if one is available
|
||||
mode = file_entry[:mode] if file_entry[:mode]
|
||||
end
|
||||
|
||||
content = file[:content]
|
||||
detect = CharlockHolmes::EncodingDetector.new.detect(content) if content
|
||||
|
||||
unless detect && detect[:type] == :binary
|
||||
# When writing to the repo directly as we are doing here,
|
||||
# the `core.autocrlf` config isn't taken into account.
|
||||
content.gsub!("\r\n", "\n") if repository.autocrlf
|
||||
end
|
||||
|
||||
oid = repo.write(content, :blob)
|
||||
index.add(path: filename, oid: oid, mode: mode)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
opts[:tree] = index.write_tree(repo)
|
||||
opts[:author] = author
|
||||
opts[:committer] = committer
|
||||
opts[:message] = commit[:message]
|
||||
opts[:parents] = parents
|
||||
opts[:update_ref] = ref if update_ref
|
||||
|
||||
Rugged::Commit.create(repo, opts)
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
# Remove file from repository and return commit sha
|
||||
#
|
||||
# options should contain next structure:
|
||||
# file: {
|
||||
# path: 'documents/story.txt'
|
||||
# },
|
||||
# author: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# committer: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# commit: {
|
||||
# message: 'Remove FILENAME',
|
||||
# branch: 'master'
|
||||
# }
|
||||
#
|
||||
def remove(repository, options)
|
||||
commit(repository, options, :remove)
|
||||
end
|
||||
|
||||
# Rename file from repository and return commit sha
|
||||
#
|
||||
# options should contain next structure:
|
||||
# file: {
|
||||
# previous_path: 'documents/old_story.txt'
|
||||
# path: 'documents/story.txt'
|
||||
# content: 'Lorem ipsum...',
|
||||
# update: true
|
||||
# },
|
||||
# author: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# committer: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# commit: {
|
||||
# message: 'Rename FILENAME',
|
||||
# branch: 'master'
|
||||
# }
|
||||
#
|
||||
def rename(repository, options)
|
||||
commit(repository, options, :rename)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(options)
|
||||
|
|
126
lib/gitlab/git/index.rb
Normal file
126
lib/gitlab/git/index.rb
Normal file
|
@ -0,0 +1,126 @@
|
|||
module Gitlab
|
||||
module Git
|
||||
class Index
|
||||
DEFAULT_MODE = 0o100644
|
||||
|
||||
attr_reader :repository, :raw_index
|
||||
|
||||
def initialize(repository)
|
||||
@repository = repository
|
||||
@raw_index = repository.rugged.index
|
||||
end
|
||||
|
||||
delegate :read_tree, :get, to: :raw_index
|
||||
|
||||
def write_tree
|
||||
raw_index.write_tree(repository.rugged)
|
||||
end
|
||||
|
||||
def dir_exists?(path)
|
||||
raw_index.find { |entry| entry[:path].start_with?("#{path}/") }
|
||||
end
|
||||
|
||||
def create(options)
|
||||
options = normalize_options(options)
|
||||
|
||||
file_entry = get(options[:file_path])
|
||||
if file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("Filename already exists")
|
||||
end
|
||||
|
||||
add_blob(options)
|
||||
end
|
||||
|
||||
def create_dir(options)
|
||||
options = normalize_options(options)
|
||||
|
||||
file_entry = get(options[:file_path])
|
||||
if file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists as a file")
|
||||
end
|
||||
|
||||
if dir_exists?(options[:file_path])
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("Directory already exists")
|
||||
end
|
||||
|
||||
options = options.dup
|
||||
options[:file_path] += '/.gitkeep'
|
||||
options[:content] = ''
|
||||
|
||||
add_blob(options)
|
||||
end
|
||||
|
||||
def update(options)
|
||||
options = normalize_options(options)
|
||||
|
||||
file_entry = get(options[:file_path])
|
||||
unless file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
|
||||
end
|
||||
|
||||
add_blob(options, mode: file_entry[:mode])
|
||||
end
|
||||
|
||||
def move(options)
|
||||
options = normalize_options(options)
|
||||
|
||||
file_entry = get(options[:previous_path])
|
||||
unless file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
|
||||
end
|
||||
|
||||
raw_index.remove(options[:previous_path])
|
||||
|
||||
add_blob(options, mode: file_entry[:mode])
|
||||
end
|
||||
|
||||
def delete(options)
|
||||
options = normalize_options(options)
|
||||
|
||||
file_entry = get(options[:file_path])
|
||||
unless file_entry
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new("File doesn't exist")
|
||||
end
|
||||
|
||||
raw_index.remove(options[:file_path])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize_options(options)
|
||||
options = options.dup
|
||||
options[:file_path] = normalize_path(options[:file_path]) if options[:file_path]
|
||||
options[:previous_path] = normalize_path(options[:previous_path]) if options[:previous_path]
|
||||
options
|
||||
end
|
||||
|
||||
def normalize_path(path)
|
||||
pathname = Gitlab::Git::PathHelper.normalize_path(path.dup)
|
||||
|
||||
if pathname.each_filename.include?('..')
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path')
|
||||
end
|
||||
|
||||
pathname.to_s
|
||||
end
|
||||
|
||||
def add_blob(options, mode: nil)
|
||||
content = options[:content]
|
||||
content = Base64.decode64(content) if options[:encoding] == 'base64'
|
||||
|
||||
detect = CharlockHolmes::EncodingDetector.new.detect(content)
|
||||
unless detect && detect[:type] == :binary
|
||||
# When writing to the repo directly as we are doing here,
|
||||
# the `core.autocrlf` config isn't taken into account.
|
||||
content.gsub!("\r\n", "\n") if repository.autocrlf
|
||||
end
|
||||
|
||||
oid = repository.rugged.write(content, :blob)
|
||||
|
||||
raw_index.add(path: options[:file_path], oid: oid, mode: mode || DEFAULT_MODE)
|
||||
rescue Rugged::IndexError => e
|
||||
raise Gitlab::Git::Repository::InvalidBlobName.new(e.message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -837,57 +837,6 @@ module Gitlab
|
|||
rugged.config['core.autocrlf'] = AUTOCRLF_VALUES.invert[value]
|
||||
end
|
||||
|
||||
# Create a new directory with a .gitkeep file. Creates
|
||||
# all required nested directories (i.e. mkdir -p behavior)
|
||||
#
|
||||
# options should contain next structure:
|
||||
# author: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# committer: {
|
||||
# email: 'user@example.com',
|
||||
# name: 'Test User',
|
||||
# time: Time.now
|
||||
# },
|
||||
# commit: {
|
||||
# message: 'Wow such commit',
|
||||
# branch: 'master',
|
||||
# update_ref: false
|
||||
# }
|
||||
def mkdir(path, options = {})
|
||||
# Check if this directory exists; if it does, then don't bother
|
||||
# adding .gitkeep file.
|
||||
ref = options[:commit][:branch]
|
||||
path = Gitlab::Git::PathHelper.normalize_path(path).to_s
|
||||
rugged_ref = rugged.ref(ref)
|
||||
|
||||
raise InvalidRef.new("Invalid ref") if rugged_ref.nil?
|
||||
|
||||
target_commit = rugged_ref.target
|
||||
|
||||
raise InvalidRef.new("Invalid target commit") if target_commit.nil?
|
||||
|
||||
entry = tree_entry(target_commit, path)
|
||||
|
||||
if entry
|
||||
if entry[:type] == :blob
|
||||
raise InvalidBlobName.new("Directory already exists as a file")
|
||||
else
|
||||
raise InvalidBlobName.new("Directory already exists")
|
||||
end
|
||||
end
|
||||
|
||||
options[:file] = {
|
||||
content: '',
|
||||
path: "#{path}/.gitkeep",
|
||||
update: true
|
||||
}
|
||||
|
||||
Gitlab::Git::Blob.commit(self, options)
|
||||
end
|
||||
|
||||
# Returns result like "git ls-files" , recursive and full file path
|
||||
#
|
||||
# Ex.
|
||||
|
|
|
@ -14,8 +14,8 @@ describe Projects::TemplatesController do
|
|||
|
||||
before do
|
||||
project.add_user(user, Gitlab::Access::MASTER)
|
||||
project.repository.commit_file(user, file_path_1, 'something valid',
|
||||
message: 'test 3', branch_name: 'master', update: false)
|
||||
project.repository.create_file(user, file_path_1, 'something valid',
|
||||
message: 'test 3', branch_name: 'master')
|
||||
end
|
||||
|
||||
describe '#show' do
|
||||
|
|
|
@ -138,27 +138,24 @@ FactoryGirl.define do
|
|||
|
||||
project.add_user(args[:user], args[:access])
|
||||
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
args[:user],
|
||||
".gitlab/#{args[:path]}/bug.md",
|
||||
'something valid',
|
||||
message: 'test 3',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
project.repository.commit_file(
|
||||
branch_name: 'master')
|
||||
project.repository.create_file(
|
||||
args[:user],
|
||||
".gitlab/#{args[:path]}/template_test.md",
|
||||
'template_test',
|
||||
message: 'test 1',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
project.repository.commit_file(
|
||||
branch_name: 'master')
|
||||
project.repository.create_file(
|
||||
args[:user],
|
||||
".gitlab/#{args[:path]}/feature_proposal.md",
|
||||
'feature_proposal',
|
||||
message: 'test 2',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ feature 'project owner creates a license file', feature: true, js: true do
|
|||
let(:project_master) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
background do
|
||||
project.repository.remove_file(project_master, 'LICENSE',
|
||||
project.repository.delete_file(project_master, 'LICENSE',
|
||||
message: 'Remove LICENSE', branch_name: 'master')
|
||||
project.team << [project_master, :master]
|
||||
login_as(project_master)
|
||||
|
|
|
@ -18,20 +18,18 @@ feature 'issuable templates', feature: true, js: true do
|
|||
let(:description_addition) { ' appending to description' }
|
||||
|
||||
background do
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab/issue_templates/bug.md',
|
||||
template_content,
|
||||
message: 'added issue template',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
project.repository.commit_file(
|
||||
branch_name: 'master')
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab/issue_templates/test.md',
|
||||
longtemplate_content,
|
||||
message: 'added issue template',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
visit edit_namespace_project_issue_path project.namespace, project, issue
|
||||
fill_in :'issue[title]', with: 'test issue title'
|
||||
end
|
||||
|
@ -79,13 +77,12 @@ feature 'issuable templates', feature: true, js: true do
|
|||
let(:issue) { create(:issue, author: user, assignee: user, project: project) }
|
||||
|
||||
background do
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab/issue_templates/bug.md',
|
||||
template_content,
|
||||
message: 'added issue template',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
visit edit_namespace_project_issue_path project.namespace, project, issue
|
||||
fill_in :'issue[title]', with: 'test issue title'
|
||||
fill_in :'issue[description]', with: prior_description
|
||||
|
@ -104,13 +101,12 @@ feature 'issuable templates', feature: true, js: true do
|
|||
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) }
|
||||
|
||||
background do
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab/merge_request_templates/feature-proposal.md',
|
||||
template_content,
|
||||
message: 'added merge request template',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
visit edit_namespace_project_merge_request_path project.namespace, project, merge_request
|
||||
fill_in :'merge_request[title]', with: 'test merge request title'
|
||||
end
|
||||
|
@ -135,13 +131,12 @@ feature 'issuable templates', feature: true, js: true do
|
|||
fork_project.team << [fork_user, :master]
|
||||
create(:forked_project_link, forked_to_project: fork_project, forked_from_project: project)
|
||||
login_as fork_user
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
fork_user,
|
||||
'.gitlab/merge_request_templates/feature-proposal.md',
|
||||
template_content,
|
||||
message: 'added merge request template',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
visit edit_namespace_project_merge_request_path project.namespace, project, merge_request
|
||||
fill_in :'merge_request[title]', with: 'test merge request title'
|
||||
end
|
||||
|
|
|
@ -222,191 +222,6 @@ describe Gitlab::Git::Blob, seed_helper: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe :commit do
|
||||
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
|
||||
|
||||
let(:commit_options) do
|
||||
{
|
||||
file: {
|
||||
content: 'Lorem ipsum...',
|
||||
path: 'documents/story.txt'
|
||||
},
|
||||
author: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
committer: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
commit: {
|
||||
message: 'Wow such commit',
|
||||
branch: 'fix-mode'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:commit_sha) { Gitlab::Git::Blob.commit(repository, commit_options) }
|
||||
let(:commit) { repository.lookup(commit_sha) }
|
||||
|
||||
it 'should add file with commit' do
|
||||
# Commit message valid
|
||||
expect(commit.message).to eq('Wow such commit')
|
||||
|
||||
tree = commit.tree.to_a.find { |tree| tree[:name] == 'documents' }
|
||||
|
||||
# Directory was created
|
||||
expect(tree[:type]).to eq(:tree)
|
||||
|
||||
# File was created
|
||||
expect(repository.lookup(tree[:oid]).first[:name]).to eq('story.txt')
|
||||
end
|
||||
|
||||
describe "ref updating" do
|
||||
it 'creates a commit but does not udate a ref' do
|
||||
commit_opts = commit_options.tap{ |opts| opts[:commit][:update_ref] = false}
|
||||
commit_sha = Gitlab::Git::Blob.commit(repository, commit_opts)
|
||||
commit = repository.lookup(commit_sha)
|
||||
|
||||
# Commit message valid
|
||||
expect(commit.message).to eq('Wow such commit')
|
||||
|
||||
# Does not update any related ref
|
||||
expect(repository.lookup("fix-mode").oid).not_to eq(commit.oid)
|
||||
expect(repository.lookup("HEAD").oid).not_to eq(commit.oid)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'reject updates' do
|
||||
it 'should reject updates' do
|
||||
commit_options[:file][:update] = false
|
||||
commit_options[:file][:path] = 'files/executables/ls'
|
||||
|
||||
expect{ commit_sha }.to raise_error('Filename already exists; update not allowed')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'file modes' do
|
||||
it 'should preserve file modes with commit' do
|
||||
commit_options[:file][:path] = 'files/executables/ls'
|
||||
|
||||
entry = Gitlab::Git::Blob.find_entry_by_path(repository, commit.tree.oid, commit_options[:file][:path])
|
||||
expect(entry[:filemode]).to eq(0100755)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe :rename do
|
||||
let(:repository) { Gitlab::Git::Repository.new(TEST_NORMAL_REPO_PATH) }
|
||||
let(:rename_options) do
|
||||
{
|
||||
file: {
|
||||
path: 'NEWCONTRIBUTING.md',
|
||||
previous_path: 'CONTRIBUTING.md',
|
||||
content: 'Lorem ipsum...',
|
||||
update: true
|
||||
},
|
||||
author: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
committer: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
commit: {
|
||||
message: 'Rename readme',
|
||||
branch: 'master'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:rename_options2) do
|
||||
{
|
||||
file: {
|
||||
content: 'Lorem ipsum...',
|
||||
path: 'bin/new_executable',
|
||||
previous_path: 'bin/executable',
|
||||
},
|
||||
author: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
committer: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
commit: {
|
||||
message: 'Updates toberenamed.txt',
|
||||
branch: 'master',
|
||||
update_ref: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'maintains file permissions when renaming' do
|
||||
mode = 0o100755
|
||||
|
||||
Gitlab::Git::Blob.rename(repository, rename_options2)
|
||||
|
||||
expect(repository.rugged.index.get(rename_options2[:file][:path])[:mode]).to eq(mode)
|
||||
end
|
||||
|
||||
it 'renames the file with commit and not change file permissions' do
|
||||
ref = rename_options[:commit][:branch]
|
||||
|
||||
expect(repository.rugged.index.get('CONTRIBUTING.md')).not_to be_nil
|
||||
expect { Gitlab::Git::Blob.rename(repository, rename_options) }.to change { repository.commit_count(ref) }.by(1)
|
||||
|
||||
expect(repository.rugged.index.get('CONTRIBUTING.md')).to be_nil
|
||||
expect(repository.rugged.index.get('NEWCONTRIBUTING.md')).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe :remove do
|
||||
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
|
||||
|
||||
let(:commit_options) do
|
||||
{
|
||||
file: {
|
||||
path: 'README.md'
|
||||
},
|
||||
author: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
committer: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
commit: {
|
||||
message: 'Remove readme',
|
||||
branch: 'feature'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:commit_sha) { Gitlab::Git::Blob.remove(repository, commit_options) }
|
||||
let(:commit) { repository.lookup(commit_sha) }
|
||||
let(:blob) { Gitlab::Git::Blob.find(repository, commit_sha, "README.md") }
|
||||
|
||||
it 'should remove file with commit' do
|
||||
# Commit message valid
|
||||
expect(commit.message).to eq('Remove readme')
|
||||
|
||||
# File was removed
|
||||
expect(blob).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe :lfs_pointers do
|
||||
context 'file a valid lfs pointer' do
|
||||
let(:blob) do
|
||||
|
|
220
spec/lib/gitlab/git/index_spec.rb
Normal file
220
spec/lib/gitlab/git/index_spec.rb
Normal file
|
@ -0,0 +1,220 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Git::Index, seed_helper: true do
|
||||
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
|
||||
let(:index) { described_class.new(repository) }
|
||||
|
||||
before do
|
||||
index.read_tree(repository.lookup('master').tree)
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
let(:options) do
|
||||
{
|
||||
content: 'Lorem ipsum...',
|
||||
file_path: 'documents/story.txt'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when no file at that path exists' do
|
||||
it 'creates the file in the index' do
|
||||
index.create(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(entry).not_to be_nil
|
||||
expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a file at that path exists' do
|
||||
before do
|
||||
options[:file_path] = 'files/executables/ls'
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { index.create(options) }.to raise_error('Filename already exists')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when content is in base64' do
|
||||
before do
|
||||
options[:content] = Base64.encode64(options[:content])
|
||||
options[:encoding] = 'base64'
|
||||
end
|
||||
|
||||
it 'decodes base64' do
|
||||
index.create(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when content contains CRLF' do
|
||||
before do
|
||||
repository.autocrlf = :input
|
||||
options[:content] = "Hello,\r\nWorld"
|
||||
end
|
||||
|
||||
it 'converts to LF' do
|
||||
index.create(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_dir' do
|
||||
let(:options) do
|
||||
{
|
||||
file_path: 'newdir'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when no file or dir at that path exists' do
|
||||
it 'creates the dir in the index' do
|
||||
index.create_dir(options)
|
||||
|
||||
entry = index.get(options[:file_path] + '/.gitkeep')
|
||||
|
||||
expect(entry).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a file at that path exists' do
|
||||
before do
|
||||
options[:file_path] = 'files/executables/ls'
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { index.create_dir(options) }.to raise_error('Directory already exists as a file')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a directory at that path exists' do
|
||||
before do
|
||||
options[:file_path] = 'files/executables'
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { index.create_dir(options) }.to raise_error('Directory already exists')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
let(:options) do
|
||||
{
|
||||
content: 'Lorem ipsum...',
|
||||
file_path: 'README.md'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when no file at that path exists' do
|
||||
before do
|
||||
options[:file_path] = 'documents/story.txt'
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { index.update(options) }.to raise_error("File doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a file at that path exists' do
|
||||
it 'updates the file in the index' do
|
||||
index.update(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
|
||||
end
|
||||
|
||||
it 'preserves file mode' do
|
||||
options[:file_path] = 'files/executables/ls'
|
||||
|
||||
index.update(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(entry[:mode]).to eq(0100755)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#move' do
|
||||
let(:options) do
|
||||
{
|
||||
content: 'Lorem ipsum...',
|
||||
previous_path: 'README.md',
|
||||
file_path: 'NEWREADME.md'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when no file at that path exists' do
|
||||
it 'raises an error' do
|
||||
options[:previous_path] = 'documents/story.txt'
|
||||
|
||||
expect { index.move(options) }.to raise_error("File doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a file at that path exists' do
|
||||
it 'removes the old file in the index' do
|
||||
index.move(options)
|
||||
|
||||
entry = index.get(options[:previous_path])
|
||||
|
||||
expect(entry).to be_nil
|
||||
end
|
||||
|
||||
it 'creates the new file in the index' do
|
||||
index.move(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(entry).not_to be_nil
|
||||
expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
|
||||
end
|
||||
|
||||
it 'preserves file mode' do
|
||||
options[:previous_path] = 'files/executables/ls'
|
||||
|
||||
index.move(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(entry[:mode]).to eq(0100755)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#delete' do
|
||||
let(:options) do
|
||||
{
|
||||
file_path: 'README.md'
|
||||
}
|
||||
end
|
||||
|
||||
context 'when no file at that path exists' do
|
||||
before do
|
||||
options[:file_path] = 'documents/story.txt'
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { index.delete(options) }.to raise_error("File doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a file at that path exists' do
|
||||
it 'removes the file in the index' do
|
||||
index.delete(options)
|
||||
|
||||
entry = index.get(options[:file_path])
|
||||
|
||||
expect(entry).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -844,81 +844,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#mkdir' do
|
||||
let(:commit_options) do
|
||||
{
|
||||
author: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
committer: {
|
||||
email: 'user@example.com',
|
||||
name: 'Test User',
|
||||
time: Time.now
|
||||
},
|
||||
commit: {
|
||||
message: 'Test message',
|
||||
branch: 'refs/heads/fix',
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def generate_diff_for_path(path)
|
||||
"diff --git a/#{path}/.gitkeep b/#{path}/.gitkeep
|
||||
new file mode 100644
|
||||
index 0000000..e69de29
|
||||
--- /dev/null
|
||||
+++ b/#{path}/.gitkeep\n"
|
||||
end
|
||||
|
||||
shared_examples 'mkdir diff check' do |path, expected_path|
|
||||
it 'creates a directory' do
|
||||
result = repository.mkdir(path, commit_options)
|
||||
expect(result).not_to eq(nil)
|
||||
|
||||
# Verify another mkdir doesn't create a directory that already exists
|
||||
expect{ repository.mkdir(path, commit_options) }.to raise_error('Directory already exists')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'creates a directory in root directory' do
|
||||
it_should_behave_like 'mkdir diff check', 'new_dir', 'new_dir'
|
||||
end
|
||||
|
||||
describe 'creates a directory in subdirectory' do
|
||||
it_should_behave_like 'mkdir diff check', 'files/ruby/test', 'files/ruby/test'
|
||||
end
|
||||
|
||||
describe 'creates a directory in subdirectory with a slash' do
|
||||
it_should_behave_like 'mkdir diff check', '/files/ruby/test2', 'files/ruby/test2'
|
||||
end
|
||||
|
||||
describe 'creates a directory in subdirectory with multiple slashes' do
|
||||
it_should_behave_like 'mkdir diff check', '//files/ruby/test3', 'files/ruby/test3'
|
||||
end
|
||||
|
||||
describe 'handles relative paths' do
|
||||
it_should_behave_like 'mkdir diff check', 'files/ruby/../test_relative', 'files/test_relative'
|
||||
end
|
||||
|
||||
describe 'creates nested directories' do
|
||||
it_should_behave_like 'mkdir diff check', 'files/missing/test', 'files/missing/test'
|
||||
end
|
||||
|
||||
it 'does not attempt to create a directory with invalid relative path' do
|
||||
expect{ repository.mkdir('../files/missing/test', commit_options) }.to raise_error('Invalid path')
|
||||
end
|
||||
|
||||
it 'does not attempt to overwrite a file' do
|
||||
expect{ repository.mkdir('README.md', commit_options) }.to raise_error('Directory already exists as a file')
|
||||
end
|
||||
|
||||
it 'does not attempt to overwrite a directory' do
|
||||
expect{ repository.mkdir('files', commit_options) }.to raise_error('Directory already exists')
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ls_files" do
|
||||
let(:master_file_paths) { repository.ls_files("master") }
|
||||
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
|
||||
|
|
|
@ -209,13 +209,12 @@ describe Gitlab::GitAccess, lib: true do
|
|||
stub_git_hooks
|
||||
project.repository.add_branch(user, unprotected_branch, 'feature')
|
||||
target_branch = project.repository.lookup('feature')
|
||||
source_branch = project.repository.commit_file(
|
||||
source_branch = project.repository.create_file(
|
||||
user,
|
||||
FFaker::InternetSE.login_user_name,
|
||||
FFaker::HipsterIpsum.paragraph,
|
||||
message: FFaker::HipsterIpsum.sentence,
|
||||
branch_name: unprotected_branch,
|
||||
update: false)
|
||||
branch_name: unprotected_branch)
|
||||
rugged = project.repository.rugged
|
||||
author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
|
||||
|
||||
|
|
|
@ -21,13 +21,12 @@ describe 'CycleAnalytics#production', feature: true do
|
|||
["production deploy happens after merge request is merged (along with other changes)",
|
||||
lambda do |context, data|
|
||||
# Make other changes on master
|
||||
sha = context.project.repository.commit_file(
|
||||
sha = context.project.repository.create_file(
|
||||
context.user,
|
||||
context.random_git_name,
|
||||
'content',
|
||||
message: 'commit message',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
context.project.repository.commit(sha)
|
||||
|
||||
context.deploy_master
|
||||
|
|
|
@ -26,13 +26,12 @@ describe 'CycleAnalytics#staging', feature: true do
|
|||
["production deploy happens after merge request is merged (along with other changes)",
|
||||
lambda do |context, data|
|
||||
# Make other changes on master
|
||||
sha = context.project.repository.commit_file(
|
||||
sha = context.project.repository.create_file(
|
||||
context.user,
|
||||
context.random_git_name,
|
||||
'content',
|
||||
message: 'commit message',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
context.project.repository.commit(sha)
|
||||
|
||||
context.deploy_master
|
||||
|
|
|
@ -1765,7 +1765,7 @@ describe Project, models: true do
|
|||
end
|
||||
|
||||
before do
|
||||
project.repository.commit_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master', update: false)
|
||||
project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
|
||||
end
|
||||
|
||||
context 'when there is a .gitlab/route-map.yml at the commit' do
|
||||
|
|
|
@ -291,10 +291,10 @@ describe Repository, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#commit_dir" do
|
||||
describe "#create_dir" do
|
||||
it "commits a change that creates a new directory" do
|
||||
expect do
|
||||
repository.commit_dir(user, 'newdir',
|
||||
repository.create_dir(user, 'newdir',
|
||||
message: 'Create newdir', branch_name: 'master')
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
||||
|
@ -307,7 +307,7 @@ describe Repository, models: true do
|
|||
|
||||
it "creates a fork and commit to the forked project" do
|
||||
expect do
|
||||
repository.commit_dir(user, 'newdir',
|
||||
repository.create_dir(user, 'newdir',
|
||||
message: 'Create newdir', branch_name: 'patch',
|
||||
start_branch_name: 'master', start_project: forked_project)
|
||||
end.to change { repository.commits('master').count }.by(0)
|
||||
|
@ -323,7 +323,7 @@ describe Repository, models: true do
|
|||
context "when an author is specified" do
|
||||
it "uses the given email/name to set the commit's author" do
|
||||
expect do
|
||||
repository.commit_dir(user, 'newdir',
|
||||
repository.create_dir(user, 'newdir',
|
||||
message: 'Add newdir',
|
||||
branch_name: 'master',
|
||||
author_email: author_email, author_name: author_name)
|
||||
|
@ -337,25 +337,23 @@ describe Repository, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#commit_file" do
|
||||
it 'commits change to a file successfully' do
|
||||
describe "#create_file" do
|
||||
it 'commits new file successfully' do
|
||||
expect do
|
||||
repository.commit_file(user, 'CHANGELOG', 'Changelog!',
|
||||
message: 'Updates file content',
|
||||
branch_name: 'master',
|
||||
update: true)
|
||||
repository.create_file(user, 'NEWCHANGELOG', 'Changelog!',
|
||||
message: 'Create changelog',
|
||||
branch_name: 'master')
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
||||
blob = repository.blob_at('master', 'CHANGELOG')
|
||||
blob = repository.blob_at('master', 'NEWCHANGELOG')
|
||||
|
||||
expect(blob.data).to eq('Changelog!')
|
||||
end
|
||||
|
||||
it 'respects the autocrlf setting' do
|
||||
repository.commit_file(user, 'hello.txt', "Hello,\r\nWorld",
|
||||
repository.create_file(user, 'hello.txt', "Hello,\r\nWorld",
|
||||
message: 'Add hello world',
|
||||
branch_name: 'master',
|
||||
update: true)
|
||||
branch_name: 'master')
|
||||
|
||||
blob = repository.blob_at('master', 'hello.txt')
|
||||
|
||||
|
@ -365,10 +363,9 @@ describe Repository, models: true do
|
|||
context "when an author is specified" do
|
||||
it "uses the given email/name to set the commit's author" do
|
||||
expect do
|
||||
repository.commit_file(user, 'README', 'README!',
|
||||
repository.create_file(user, 'NEWREADME', 'README!',
|
||||
message: 'Add README',
|
||||
branch_name: 'master',
|
||||
update: true,
|
||||
author_email: author_email,
|
||||
author_name: author_name)
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
@ -382,6 +379,18 @@ describe Repository, models: true do
|
|||
end
|
||||
|
||||
describe "#update_file" do
|
||||
it 'updates file successfully' do
|
||||
expect do
|
||||
repository.update_file(user, 'CHANGELOG', 'Changelog!',
|
||||
message: 'Update changelog',
|
||||
branch_name: 'master')
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
||||
blob = repository.blob_at('master', 'CHANGELOG')
|
||||
|
||||
expect(blob.data).to eq('Changelog!')
|
||||
end
|
||||
|
||||
it 'updates filename successfully' do
|
||||
expect do
|
||||
repository.update_file(user, 'NEWLICENSE', 'Copyright!',
|
||||
|
@ -398,9 +407,6 @@ describe Repository, models: true do
|
|||
|
||||
context "when an author is specified" do
|
||||
it "uses the given email/name to set the commit's author" do
|
||||
repository.commit_file(user, 'README', 'README!',
|
||||
message: 'Add README', branch_name: 'master', update: true)
|
||||
|
||||
expect do
|
||||
repository.update_file(user, 'README', 'Updated README!',
|
||||
branch_name: 'master',
|
||||
|
@ -418,13 +424,10 @@ describe Repository, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#remove_file" do
|
||||
describe "#delete_file" do
|
||||
it 'removes file successfully' do
|
||||
repository.commit_file(user, 'README', 'README!',
|
||||
message: 'Add README', branch_name: 'master', update: true)
|
||||
|
||||
expect do
|
||||
repository.remove_file(user, 'README',
|
||||
repository.delete_file(user, 'README',
|
||||
message: 'Remove README', branch_name: 'master')
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
||||
|
@ -433,11 +436,8 @@ describe Repository, models: true do
|
|||
|
||||
context "when an author is specified" do
|
||||
it "uses the given email/name to set the commit's author" do
|
||||
repository.commit_file(user, 'README', 'README!',
|
||||
message: 'Add README', branch_name: 'master', update: true)
|
||||
|
||||
expect do
|
||||
repository.remove_file(user, 'README',
|
||||
repository.delete_file(user, 'README',
|
||||
message: 'Remove README', branch_name: 'master',
|
||||
author_email: author_email, author_name: author_name)
|
||||
end.to change { repository.commits('master').count }.by(1)
|
||||
|
@ -587,14 +587,14 @@ describe Repository, models: true do
|
|||
|
||||
describe "#license_blob", caching: true do
|
||||
before do
|
||||
repository.remove_file(
|
||||
repository.delete_file(
|
||||
user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master')
|
||||
end
|
||||
|
||||
it 'handles when HEAD points to non-existent ref' do
|
||||
repository.commit_file(
|
||||
repository.create_file(
|
||||
user, 'LICENSE', 'Copyright!',
|
||||
message: 'Add LICENSE', branch_name: 'master', update: false)
|
||||
message: 'Add LICENSE', branch_name: 'master')
|
||||
|
||||
allow(repository).to receive(:file_on_head).
|
||||
and_raise(Rugged::ReferenceError)
|
||||
|
@ -603,27 +603,27 @@ describe Repository, models: true do
|
|||
end
|
||||
|
||||
it 'looks in the root_ref only' do
|
||||
repository.remove_file(user, 'LICENSE',
|
||||
repository.delete_file(user, 'LICENSE',
|
||||
message: 'Remove LICENSE', branch_name: 'markdown')
|
||||
repository.commit_file(user, 'LICENSE',
|
||||
repository.create_file(user, 'LICENSE',
|
||||
Licensee::License.new('mit').content,
|
||||
message: 'Add LICENSE', branch_name: 'markdown', update: false)
|
||||
message: 'Add LICENSE', branch_name: 'markdown')
|
||||
|
||||
expect(repository.license_blob).to be_nil
|
||||
end
|
||||
|
||||
it 'detects license file with no recognizable open-source license content' do
|
||||
repository.commit_file(user, 'LICENSE', 'Copyright!',
|
||||
message: 'Add LICENSE', branch_name: 'master', update: false)
|
||||
repository.create_file(user, 'LICENSE', 'Copyright!',
|
||||
message: 'Add LICENSE', branch_name: 'master')
|
||||
|
||||
expect(repository.license_blob.name).to eq('LICENSE')
|
||||
end
|
||||
|
||||
%w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md].each do |filename|
|
||||
it "detects '#{filename}'" do
|
||||
repository.commit_file(user, filename,
|
||||
repository.create_file(user, filename,
|
||||
Licensee::License.new('mit').content,
|
||||
message: "Add #{filename}", branch_name: 'master', update: false)
|
||||
message: "Add #{filename}", branch_name: 'master')
|
||||
|
||||
expect(repository.license_blob.name).to eq(filename)
|
||||
end
|
||||
|
@ -632,7 +632,7 @@ describe Repository, models: true do
|
|||
|
||||
describe '#license_key', caching: true do
|
||||
before do
|
||||
repository.remove_file(user, 'LICENSE',
|
||||
repository.delete_file(user, 'LICENSE',
|
||||
message: 'Remove LICENSE', branch_name: 'master')
|
||||
end
|
||||
|
||||
|
@ -647,16 +647,16 @@ describe Repository, models: true do
|
|||
end
|
||||
|
||||
it 'detects license file with no recognizable open-source license content' do
|
||||
repository.commit_file(user, 'LICENSE', 'Copyright!',
|
||||
message: 'Add LICENSE', branch_name: 'master', update: false)
|
||||
repository.create_file(user, 'LICENSE', 'Copyright!',
|
||||
message: 'Add LICENSE', branch_name: 'master')
|
||||
|
||||
expect(repository.license_key).to be_nil
|
||||
end
|
||||
|
||||
it 'returns the license key' do
|
||||
repository.commit_file(user, 'LICENSE',
|
||||
repository.create_file(user, 'LICENSE',
|
||||
Licensee::License.new('mit').content,
|
||||
message: 'Add LICENSE', branch_name: 'master', update: false)
|
||||
message: 'Add LICENSE', branch_name: 'master')
|
||||
|
||||
expect(repository.license_key).to eq('mit')
|
||||
end
|
||||
|
@ -913,10 +913,9 @@ describe Repository, models: true do
|
|||
expect(empty_repository).to receive(:expire_emptiness_caches)
|
||||
expect(empty_repository).to receive(:expire_branches_cache)
|
||||
|
||||
empty_repository.commit_file(user, 'CHANGELOG', 'Changelog!',
|
||||
empty_repository.create_file(user, 'CHANGELOG', 'Changelog!',
|
||||
message: 'Updates file content',
|
||||
branch_name: 'master',
|
||||
update: false)
|
||||
branch_name: 'master')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1796,7 +1795,7 @@ describe Repository, models: true do
|
|||
|
||||
describe '#gitlab_ci_yml_for' do
|
||||
before do
|
||||
repository.commit_file(User.last, '.gitlab-ci.yml', 'CONTENT', message: 'Add .gitlab-ci.yml', branch_name: 'master', update: false)
|
||||
repository.create_file(User.last, '.gitlab-ci.yml', 'CONTENT', message: 'Add .gitlab-ci.yml', branch_name: 'master')
|
||||
end
|
||||
|
||||
context 'when there is a .gitlab-ci.yml at the commit' do
|
||||
|
@ -1814,7 +1813,7 @@ describe Repository, models: true do
|
|||
|
||||
describe '#route_map_for' do
|
||||
before do
|
||||
repository.commit_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master', update: false)
|
||||
repository.create_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master')
|
||||
end
|
||||
|
||||
context 'when there is a .gitlab/route-map.yml at the commit' do
|
||||
|
|
|
@ -127,7 +127,7 @@ describe API::Files, api: true do
|
|||
end
|
||||
|
||||
it "returns a 400 if editor fails to create file" do
|
||||
allow_any_instance_of(Repository).to receive(:commit_file).
|
||||
allow_any_instance_of(Repository).to receive(:create_file).
|
||||
and_return(false)
|
||||
|
||||
post api("/projects/#{project.id}/repository/files", user), valid_params
|
||||
|
@ -215,7 +215,7 @@ describe API::Files, api: true do
|
|||
end
|
||||
|
||||
it "returns a 400 if fails to create file" do
|
||||
allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
|
||||
allow_any_instance_of(Repository).to receive(:delete_file).and_return(false)
|
||||
|
||||
delete api("/projects/#{project.id}/repository/files", user), valid_params
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ describe API::V3::Files, api: true do
|
|||
end
|
||||
|
||||
it "returns a 400 if editor fails to create file" do
|
||||
allow_any_instance_of(Repository).to receive(:commit_file).
|
||||
allow_any_instance_of(Repository).to receive(:create_file).
|
||||
and_return(false)
|
||||
|
||||
post v3_api("/projects/#{project.id}/repository/files", user), valid_params
|
||||
|
@ -215,7 +215,7 @@ describe API::V3::Files, api: true do
|
|||
end
|
||||
|
||||
it "returns a 400 if fails to create file" do
|
||||
allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
|
||||
allow_any_instance_of(Repository).to receive(:delete_file).and_return(false)
|
||||
|
||||
delete v3_api("/projects/#{project.id}/repository/files", user), valid_params
|
||||
|
||||
|
|
|
@ -66,13 +66,12 @@ describe MergeRequests::ResolveService do
|
|||
|
||||
context 'when the source project is a fork and does not contain the HEAD of the target branch' do
|
||||
let!(:target_head) do
|
||||
project.repository.commit_file(
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'new-file-in-target',
|
||||
'',
|
||||
message: 'Add new file in target',
|
||||
branch_name: 'conflict-start',
|
||||
update: false)
|
||||
branch_name: 'conflict-start')
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
@ -9,14 +9,7 @@ module CycleAnalyticsHelpers
|
|||
commit_shas = Array.new(count) do |index|
|
||||
filename = random_git_name
|
||||
|
||||
options = {
|
||||
committer: project.repository.user_to_committer(user),
|
||||
author: project.repository.user_to_committer(user),
|
||||
commit: { message: message, branch: branch_name, update_ref: true },
|
||||
file: { content: "content", path: filename, update: false }
|
||||
}
|
||||
|
||||
commit_sha = Gitlab::Git::Blob.commit(project.repository, options)
|
||||
commit_sha = project.repository.create_file(user, filename, "content", message: message, branch_name: branch_name)
|
||||
project.repository.commit(commit_sha)
|
||||
|
||||
commit_sha
|
||||
|
@ -35,13 +28,12 @@ module CycleAnalyticsHelpers
|
|||
project.repository.add_branch(user, source_branch, 'master')
|
||||
end
|
||||
|
||||
sha = project.repository.commit_file(
|
||||
sha = project.repository.create_file(
|
||||
user,
|
||||
random_git_name,
|
||||
'content',
|
||||
message: 'commit message',
|
||||
branch_name: source_branch,
|
||||
update: false)
|
||||
branch_name: source_branch)
|
||||
project.repository.commit(sha)
|
||||
|
||||
opts = {
|
||||
|
|
Loading…
Reference in a new issue