Merge branch 'master' into 'add-svg-loader'
# Conflicts: # app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6
This commit is contained in:
commit
442a268f8d
49 changed files with 931 additions and 834 deletions
|
@ -6,8 +6,6 @@
|
|||
/* global AwardsHandler */
|
||||
/* global Aside */
|
||||
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
require('jquery-ui/ui/draggable');
|
||||
require('jquery-ui/ui/sortable');
|
||||
|
@ -44,15 +42,176 @@ require('./shortcuts_dashboard_navigation');
|
|||
require('./shortcuts_issuable');
|
||||
require('./shortcuts_network');
|
||||
require('vendor/jquery.nicescroll');
|
||||
requireAll(require.context('./behaviors', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./blob', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./templates', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./commit', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./extensions', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./lib/utils', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
|
||||
|
||||
// behaviors
|
||||
require('./behaviors/autosize');
|
||||
require('./behaviors/details_behavior');
|
||||
require('./behaviors/quick_submit');
|
||||
require('./behaviors/requires_input');
|
||||
require('./behaviors/toggler_behavior');
|
||||
|
||||
// blob
|
||||
require('./blob/blob_ci_yaml');
|
||||
require('./blob/blob_dockerfile_selector');
|
||||
require('./blob/blob_dockerfile_selectors');
|
||||
require('./blob/blob_file_dropzone');
|
||||
require('./blob/blob_gitignore_selector');
|
||||
require('./blob/blob_gitignore_selectors');
|
||||
require('./blob/blob_license_selector');
|
||||
require('./blob/blob_license_selectors');
|
||||
require('./blob/template_selector');
|
||||
|
||||
// templates
|
||||
require('./templates/issuable_template_selector');
|
||||
require('./templates/issuable_template_selectors');
|
||||
|
||||
// commit
|
||||
require('./commit/file.js');
|
||||
require('./commit/image_file.js');
|
||||
|
||||
// extensions
|
||||
require('./extensions/array');
|
||||
require('./extensions/custom_event');
|
||||
require('./extensions/element');
|
||||
require('./extensions/jquery');
|
||||
require('./extensions/object');
|
||||
|
||||
// lib/utils
|
||||
require('./lib/utils/animate');
|
||||
require('./lib/utils/bootstrap_linked_tabs');
|
||||
require('./lib/utils/common_utils');
|
||||
require('./lib/utils/datetime_utility');
|
||||
require('./lib/utils/notify');
|
||||
require('./lib/utils/pretty_time');
|
||||
require('./lib/utils/text_utility');
|
||||
require('./lib/utils/type_utility');
|
||||
require('./lib/utils/url_utility');
|
||||
|
||||
// u2f
|
||||
require('./u2f/authenticate');
|
||||
require('./u2f/error');
|
||||
require('./u2f/register');
|
||||
require('./u2f/util');
|
||||
|
||||
// droplab
|
||||
require('./droplab/droplab');
|
||||
require('./droplab/droplab_ajax');
|
||||
require('./droplab/droplab_ajax_filter');
|
||||
require('./droplab/droplab_filter');
|
||||
|
||||
// everything else
|
||||
require('./abuse_reports');
|
||||
require('./activities');
|
||||
require('./admin');
|
||||
require('./ajax_loading_spinner');
|
||||
require('./api');
|
||||
require('./aside');
|
||||
require('./autosave');
|
||||
require('./awards_handler');
|
||||
require('./breakpoints');
|
||||
require('./broadcast_message');
|
||||
require('./build');
|
||||
require('./build_artifacts');
|
||||
require('./build_variables');
|
||||
require('./ci_lint_editor');
|
||||
require('./commit');
|
||||
require('./commits');
|
||||
require('./compare');
|
||||
require('./compare_autocomplete');
|
||||
require('./confirm_danger_modal');
|
||||
require('./copy_as_gfm');
|
||||
require('./copy_to_clipboard');
|
||||
require('./create_label');
|
||||
require('./diff');
|
||||
require('./dispatcher');
|
||||
require('./dropzone_input');
|
||||
require('./due_date_select');
|
||||
require('./files_comment_button');
|
||||
require('./flash');
|
||||
require('./gfm_auto_complete');
|
||||
require('./gl_dropdown');
|
||||
require('./gl_field_error');
|
||||
require('./gl_field_errors');
|
||||
require('./gl_form');
|
||||
require('./group_avatar');
|
||||
require('./group_label_subscription');
|
||||
require('./groups_select');
|
||||
require('./header');
|
||||
require('./importer_status');
|
||||
require('./issuable');
|
||||
require('./issuable_context');
|
||||
require('./issuable_form');
|
||||
require('./issue');
|
||||
require('./issue_status_select');
|
||||
require('./issues_bulk_assignment');
|
||||
require('./label_manager');
|
||||
require('./labels');
|
||||
require('./labels_select');
|
||||
require('./layout_nav');
|
||||
require('./line_highlighter');
|
||||
require('./logo');
|
||||
require('./member_expiration_date');
|
||||
require('./members');
|
||||
require('./merge_request');
|
||||
require('./merge_request_tabs');
|
||||
require('./merge_request_widget');
|
||||
require('./merged_buttons');
|
||||
require('./milestone');
|
||||
require('./milestone_select');
|
||||
require('./mini_pipeline_graph_dropdown');
|
||||
require('./namespace_select');
|
||||
require('./new_branch_form');
|
||||
require('./new_commit_form');
|
||||
require('./notes');
|
||||
require('./notifications_dropdown');
|
||||
require('./notifications_form');
|
||||
require('./pager');
|
||||
require('./pipelines');
|
||||
require('./preview_markdown');
|
||||
require('./project');
|
||||
require('./project_avatar');
|
||||
require('./project_find_file');
|
||||
require('./project_fork');
|
||||
require('./project_import');
|
||||
require('./project_label_subscription');
|
||||
require('./project_new');
|
||||
require('./project_select');
|
||||
require('./project_show');
|
||||
require('./project_variables');
|
||||
require('./projects_list');
|
||||
require('./render_gfm');
|
||||
require('./render_math');
|
||||
require('./right_sidebar');
|
||||
require('./search');
|
||||
require('./search_autocomplete');
|
||||
require('./shortcuts');
|
||||
require('./shortcuts_blob');
|
||||
require('./shortcuts_dashboard_navigation');
|
||||
require('./shortcuts_find_file');
|
||||
require('./shortcuts_issuable');
|
||||
require('./shortcuts_navigation');
|
||||
require('./shortcuts_network');
|
||||
require('./signin_tabs_memoizer');
|
||||
require('./single_file_diff');
|
||||
require('./smart_interval');
|
||||
require('./snippets_list');
|
||||
require('./star');
|
||||
require('./subbable_resource');
|
||||
require('./subscription');
|
||||
require('./subscription_select');
|
||||
require('./syntax_highlight');
|
||||
require('./task_list');
|
||||
require('./todos');
|
||||
require('./tree');
|
||||
require('./user');
|
||||
require('./user_tabs');
|
||||
require('./username_validator');
|
||||
require('./users_select');
|
||||
require('./version_check_image');
|
||||
require('./visibility_select');
|
||||
require('./wikis');
|
||||
require('./zen_mode');
|
||||
|
||||
require('vendor/fuzzaldrin-plus');
|
||||
require('es6-promise').polyfill();
|
||||
|
||||
|
|
|
@ -123,14 +123,18 @@ class List {
|
|||
|
||||
if (listFrom) {
|
||||
this.issuesSize += 1;
|
||||
gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
|
||||
.then(() => {
|
||||
listFrom.getIssues(false);
|
||||
});
|
||||
this.updateIssueLabel(issue, listFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateIssueLabel(issue, listFrom) {
|
||||
gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
|
||||
.then(() => {
|
||||
listFrom.getIssues(false);
|
||||
});
|
||||
}
|
||||
|
||||
findIssue (id) {
|
||||
return this.issues.filter(issue => issue.id === id)[0];
|
||||
}
|
||||
|
|
|
@ -92,9 +92,12 @@
|
|||
const issueLists = issue.getLists();
|
||||
const listLabels = issueLists.map(listIssue => listIssue.label);
|
||||
|
||||
// Add to new lists issues if it doesn't already exist
|
||||
if (!issueTo) {
|
||||
// Add to new lists issues if it doesn't already exist
|
||||
listTo.addIssue(issue, listFrom, newIndex);
|
||||
} else {
|
||||
listTo.updateIssueLabel(issue, listFrom);
|
||||
issueTo.removeLabel(listFrom.label);
|
||||
}
|
||||
|
||||
if (listTo.type === 'done') {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
/* global Shortcuts */
|
||||
|
||||
const ShortcutsBlob = require('./shortcuts_blob');
|
||||
const UserCallout = require('./user_callout');
|
||||
|
||||
(function() {
|
||||
var Dispatcher;
|
||||
|
@ -277,6 +278,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
|
|||
case 'ci:lints:show':
|
||||
new gl.CILintEditor();
|
||||
break;
|
||||
case 'users:show':
|
||||
new UserCallout();
|
||||
break;
|
||||
}
|
||||
switch (path.first()) {
|
||||
case 'sessions':
|
||||
|
@ -313,6 +317,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
|
|||
case 'dashboard':
|
||||
case 'root':
|
||||
shortcut_handler = new ShortcutsDashboardNavigation();
|
||||
new UserCallout();
|
||||
break;
|
||||
case 'profiles':
|
||||
new NotificationsForm();
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
// require everything else in this directory
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
requireAll(require.context('.', false, /^\.\/(?!profile_bundle).*\.(js|es6)$/));
|
||||
require('./gl_crop');
|
||||
require('./profile');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// require everything else in this directory
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
requireAll(require.context('.', false, /^\.\/(?!protected_branches_bundle).*\.(js|es6)$/));
|
||||
require('./protected_branch_access_dropdown');
|
||||
require('./protected_branch_create');
|
||||
require('./protected_branch_dropdown');
|
||||
require('./protected_branch_edit');
|
||||
require('./protected_branch_edit_list');
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, max-len */
|
||||
/* global ace */
|
||||
|
||||
// require everything else in this directory
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
requireAll(require.context('.', false, /^\.\/(?!snippet_bundle).*\.(js|es6)$/));
|
||||
|
||||
(function() {
|
||||
$(function() {
|
||||
var editor = ace.edit("editor");
|
||||
|
|
58
app/assets/javascripts/user_callout.js
Normal file
58
app/assets/javascripts/user_callout.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* global Cookies */
|
||||
|
||||
const userCalloutElementName = '.user-callout';
|
||||
const closeButton = '.close-user-callout';
|
||||
const userCalloutBtn = '.user-callout-btn';
|
||||
const userCalloutSvgAttrName = 'callout-svg';
|
||||
|
||||
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
|
||||
|
||||
const USER_CALLOUT_TEMPLATE = `
|
||||
<div class="bordered-box landing content-block">
|
||||
<button class="btn btn-default close close-user-callout" type="button">
|
||||
<i class="fa fa-times dismiss-icon"></i>
|
||||
</button>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-xs-12 svg-container">
|
||||
</div>
|
||||
<div class="col-sm-8 col-xs-12 inner-content">
|
||||
<h4>
|
||||
Customize your experience
|
||||
</h4>
|
||||
<p>
|
||||
Change syntax themes, default project pages, and more in preferences.
|
||||
</p>
|
||||
<a class="btn user-callout-btn" href="/profile/preferences">Check it out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
class UserCallout {
|
||||
constructor() {
|
||||
this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
|
||||
this.userCalloutBody = $(userCalloutElementName);
|
||||
this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName);
|
||||
$(userCalloutElementName).removeAttr(userCalloutSvgAttrName);
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const $template = $(USER_CALLOUT_TEMPLATE);
|
||||
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
|
||||
$template.find('.svg-container').append(this.userCalloutSvg);
|
||||
this.userCalloutBody.append($template);
|
||||
$template.find(closeButton).on('click', e => this.dismissCallout(e));
|
||||
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
|
||||
}
|
||||
}
|
||||
|
||||
dismissCallout(e) {
|
||||
Cookies.set(USER_CALLOUT_COOKIE, 'true');
|
||||
const $currentTarget = $(e.currentTarget);
|
||||
if ($currentTarget.hasClass('close-user-callout')) {
|
||||
this.userCalloutBody.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserCallout;
|
|
@ -1,3 +1 @@
|
|||
// require everything else in this directory
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
requireAll(require.context('.', false, /^\.\/(?!users_bundle).*\.(js|es6)$/));
|
||||
require('./calendar');
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* global Vue, Flash, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable no-param-reassign, no-alert */
|
||||
const playIconSvg = require('../../../views/shared/icons/_icon_play.svg');
|
||||
|
||||
|
||||
((gl) => {
|
||||
gl.VuePipelineActions = Vue.extend({
|
||||
props: ['pipeline'],
|
||||
|
@ -17,6 +18,20 @@ const playIconSvg = require('../../../views/shared/icons/_icon_play.svg');
|
|||
download(name) {
|
||||
return `Download ${name} artifacts`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a dialog when the user clicks in the cancel button.
|
||||
* We need to prevent the default behavior and stop propagation because the
|
||||
* link relies on UJS.
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
confirmAction(event) {
|
||||
if (!confirm('Are you sure you want to cancel this pipeline?')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -93,6 +108,7 @@ const playIconSvg = require('../../../views/shared/icons/_icon_play.svg');
|
|||
</a>
|
||||
<a
|
||||
v-if='pipeline.flags.cancelable'
|
||||
@click="confirmAction"
|
||||
class="btn btn-remove has-tooltip"
|
||||
title="Cancel"
|
||||
rel="nofollow"
|
||||
|
|
|
@ -277,3 +277,41 @@ table.u2f-registrations {
|
|||
padding-left: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-callout {
|
||||
margin: 24px auto 0;
|
||||
|
||||
.bordered-box {
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $border-radius-default;
|
||||
}
|
||||
|
||||
.landing {
|
||||
margin-bottom: $gl-padding;
|
||||
|
||||
.close {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.dismiss-icon {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
color: $cycle-analytics-dismiss-icon-color;
|
||||
}
|
||||
|
||||
.svg-container {
|
||||
text-align: center;
|
||||
|
||||
svg {
|
||||
width: 136px;
|
||||
height: 136px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $screen-xs-max) {
|
||||
.inner-content {
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
- page_title "Projects"
|
||||
- header_title "Projects", dashboard_projects_path
|
||||
|
||||
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
|
||||
|
||||
- if @projects.any? || params[:filter_projects]
|
||||
= render 'dashboard/projects_head'
|
||||
|
||||
|
|
1
app/views/shared/icons/_icon_customization.svg
Normal file
1
app/views/shared/icons/_icon_customization.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 112 90" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" fill-rule="evenodd"><rect width="112" height="90" fill="#fff" rx="6"/><path fill="#eee" fill-rule="nonzero" d="m4 6.01v77.98c0 1.11.899 2.01 2 2.01h100c1.105 0 2-.898 2-2.01v-77.98c0-1.11-.899-2.01-2-2.01h-100c-1.105 0-2 .898-2 2.01m-4 0c0-3.319 2.686-6.01 6-6.01h100c3.315 0 6 2.694 6 6.01v77.98c0 3.319-2.686 6.01-6 6.01h-100c-3.315 0-6-2.694-6-6.01v-77.98"/><g transform="translate(26 35)"><rect width="4" height="39" x="5" fill="#eee" rx="2" id="0"/><rect width="4" height="21" x="5" y="18" fill="#fef0ea" rx="2"/><circle cx="7" cy="13" r="5" fill="#fff"/><path fill="#fb722e" fill-rule="nonzero" d="m7 20c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g transform="translate(49 35)"><use xlink:href="#0"/><rect width="4" height="21" x="5" y="18" fill="#b5a7dd" rx="2"/><circle cx="7" cy="25" r="5" fill="#fff"/><path fill="#6b4fbb" fill-rule="nonzero" d="m7 32c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g transform="translate(72 33)"><rect width="4" height="39" x="5" y="2" fill="#eee" rx="2"/><rect width="4" height="34" x="5" y="7" fill="#fef0ea" rx="2"/><circle cx="7" cy="7" r="5" fill="#fff"/><path fill="#fb722e" fill-rule="nonzero" d="m7 14c-3.866 0-7-3.134-7-7 0-3.866 3.134-7 7-7 3.866 0 7 3.134 7 7 0 3.866-3.134 7-7 7m0-4c1.657 0 3-1.343 3-3 0-1.657-1.343-3-3-3-1.657 0-3 1.343-3 3 0 1.657 1.343 3 3 3"/></g><g fill="#6b4fbb"><circle cx="13.5" cy="11.5" r="2.5"/><circle cx="23.5" cy="11.5" r="2.5" opacity=".5"/><circle cx="33.5" cy="11.5" r="2.5" opacity=".5"/></g><path fill="#eee" d="m0 19h111v4h-111z"/></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -98,6 +98,7 @@
|
|||
Snippets
|
||||
|
||||
%div{ class: container_class }
|
||||
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
|
||||
.tab-content
|
||||
#activity.tab-pane
|
||||
.row-content-block.calender-block.white.second-block.hidden-xs
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Removes label when moving issue to another list that it is currently in
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/user-callouts.yml
Normal file
4
changelogs/unreleased/user-callouts.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Added user callouts to the projects dashboard and user profile
|
||||
merge_request:
|
||||
author:
|
|
@ -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,
|
||||
|
|
|
@ -27,19 +27,19 @@
|
|||
- **Hobbies / interests**<br>Functional programming, open source, gaming, web development and web security.
|
||||
|
||||
#### Motivations
|
||||
Steven works for a software development company which currently hires around 80 people. When Steven first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Steven felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wishlist of features, Steven began comparing source control tools. A search for “self-hosted Git server repository management” returned GitLab. In his own words, Steven explains why he wanted the engineering team to start using GitLab:
|
||||
Nazim works for a software development company which currently hires around 80 people. When Nazim first joined the company, the engineering team were using Subversion (SVN) as their primary form of source control. However, Nazim felt SVN was not flexible enough to work with many feature branches and noticed that developers with less experience of source control struggled with the central-repository nature of SVN. Armed with a wishlist of features, Nazim began comparing source control tools. A search for “self-hosted Git server repository management” returned GitLab. In his own words, Nazim explains why he wanted the engineering team to start using GitLab:
|
||||
|
||||
>
|
||||
“I wanted them to switch away from SVN. I needed a server application to manage repositories. The common tools that were around just didn’t meet the requirements. Most of them were too simple or plain...GitLab provided all the required features. Also costs had to be low, since we don’t have a big budget for those things...the Community Edition was perfect in this regard.”
|
||||
>
|
||||
|
||||
In his role as a full-stack web developer, Steven could recommend products that he would like the engineering team to use, but final approval lay with his line manager, Mike, VP of Engineering. Steven recalls that he was met with reluctance from his colleagues when he raised moving to Git and using GitLab.
|
||||
In his role as a full-stack web developer, Nazim could recommend products that he would like the engineering team to use, but final approval lay with his line manager, Mike, VP of Engineering. Nazim recalls that he was met with reluctance from his colleagues when he raised moving to Git and using GitLab.
|
||||
|
||||
>
|
||||
“The biggest challenge...why should we change anything at all from the status quo? We needed to switch from SVN to Git. They knew they needed to learn Git and a Git workflow...using Git was scary to my colleagues...they thought it was more complex than SVN to use.”
|
||||
>
|
||||
|
||||
Undeterred, Steven decided to migrate a couple of projects across to GitLab.
|
||||
Undeterred, Nazim decided to migrate a couple of projects across to GitLab.
|
||||
|
||||
>
|
||||
“Old SVN users couldn’t see the benefits of Git at first. It took a month or two to convince them.”
|
||||
|
@ -47,17 +47,17 @@ Undeterred, Steven decided to migrate a couple of projects across to GitLab.
|
|||
|
||||
Slowly, by showing his colleagues how easy it was to use Git, the majority of the team’s projects were migrated to GitLab.
|
||||
|
||||
The engineering team have been using GitLab CE for around 2 years now. Steven credits himself as being entirely responsible for his company’s decision to move to GitLab.
|
||||
The engineering team have been using GitLab CE for around 2 years now. Nazim credits himself as being entirely responsible for his company’s decision to move to GitLab.
|
||||
|
||||
#### Frustrations
|
||||
##### Adoption to GitLab has been slow
|
||||
Not only has the engineering team had to get to grips with Git, they’ve also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilised. Steven sold GitLab to his manager as an ‘all in one’ tool which would replace multiple tools used within the company, thus saving costs. Steven hasn’t had the time to integrate the legacy tools to GitLab and he’s struggling to convince his peers to change their habits.
|
||||
Not only has the engineering team had to get to grips with Git, they’ve also had to adapt to using GitLab. Due to lack of training and existing skills in other tools, the full feature set of GitLab CE is not being utilised. Nazim sold GitLab to his manager as an ‘all in one’ tool which would replace multiple tools used within the company, thus saving costs. Nazim hasn’t had the time to integrate the legacy tools to GitLab and he’s struggling to convince his peers to change their habits.
|
||||
|
||||
##### Missing Features
|
||||
Steven’s company want GitLab to be able to do everything. There isn’t a large budget for software, so they’re selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Steven’s company wants to know if GitLab has a specific feature or does a particular thing, Steven is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Steven gets frustrated when GitLab isn’t able to do what he or his colleagues need it to do.
|
||||
Nazim’s company want GitLab to be able to do everything. There isn’t a large budget for software, so they’re selective about what tools are implemented. It needs to add real value to the company. In order for GitLab to be widely adopted and to meet the requirements of different roles within the company, it needs a host of features. When an individual within Nazim’s company wants to know if GitLab has a specific feature or does a particular thing, Nazim is the person to ask. He becomes the point of contact to investigate, build or sometimes just raise the feature request. Nazim gets frustrated when GitLab isn’t able to do what he or his colleagues need it to do.
|
||||
|
||||
##### Regressions and bugs
|
||||
Steven often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it “every new version adds something awesome, but breaks something”. He feels that “old issues for "minor" annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs.” Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it’s also counterproductive to fix workflows every month.
|
||||
Nazim often has to calm down his colleagues, when a release contains regressions or new bugs. As he puts it “every new version adds something awesome, but breaks something”. He feels that “old issues for "minor" annoyances get quickly buried in the mass of open issues and linger for a very long time. More generally, I have the feeling that GitLab focus on adding new functionalities, but overlook a bunch of annoying minor regressions or introduced bugs.” Due to limited resource and expertise within the team, not only is it difficult to remain up-to-date with the frequent release cycle, it’s also counterproductive to fix workflows every month.
|
||||
|
||||
##### Uses too much RAM and CPU
|
||||
>
|
||||
|
@ -65,7 +65,7 @@ Steven often has to calm down his colleagues, when a release contains regression
|
|||
>
|
||||
|
||||
##### UI/UX
|
||||
GitLab’s interface initially attracted Steven when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab’s interface daily has left him frustrated at the lack of personalisation / control over his user experience. He’s also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. “Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release.”
|
||||
GitLab’s interface initially attracted Nazim when he was comparing version control software. He thought it would help his less technical colleagues to adapt to using Git and perhaps, GitLab could be rolled out to other areas of the business, beyond engineering. However, using GitLab’s interface daily has left him frustrated at the lack of personalisation / control over his user experience. He’s also regularly lost in a maze of navigation. Whilst he acknowledges that GitLab listens to its users and that the interface is improving, he becomes annoyed when the changes are too progressive. “Too frequent UI changes. Most of them tend to turn out great after a few cycles of fixes, but the frequency is still far too high for me to feel comfortable to always stay on the current release.”
|
||||
|
||||
#### Goals
|
||||
* To convince his colleagues to fully adopt GitLab CE, thus improving workflow and collaboration.
|
||||
|
@ -121,8 +121,8 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w
|
|||
|
||||
#### Goals
|
||||
* To be able to integrate third party tools easily with GitLab EE and to create custom integrations and patches where needed.
|
||||
* To use GitLab EE primarily for code hosting, merge requests, continuous integration and issue management. Steven and his team want to be able to understand and use these particular features easily.
|
||||
* To able to share one instance of GitLab EE with multiple teams across the business. Advanced user management, the ability to separate permissions on different parts of the source code, etc are important to Steven.
|
||||
* To use GitLab EE primarily for code hosting, merge requests, continuous integration and issue management. James and his team want to be able to understand and use these particular features easily.
|
||||
* To able to share one instance of GitLab EE with multiple teams across the business. Advanced user management, the ability to separate permissions on different parts of the source code, etc are important to James.
|
||||
|
||||
<hr>
|
||||
|
||||
|
@ -144,21 +144,21 @@ James and his team use CI quite heavily for several projects. Whilst they’ve w
|
|||
- **Hobbies / interests**<br>Web development, mobile development, UX, open source, gaming and travel.
|
||||
|
||||
#### Motivations
|
||||
Harry has been using GitLab.com for around a year. He roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Harry contributes to open source projects to gain programming experience and to give back to the community. He likes GitLab.com for its free private repositories and range of features which provide him with everything he needs for his personal projects. Harry is also a massive fan of GitLab’s values and the fact that it isn’t a “behemoth of a company”. He explains that “displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work.” He’s also an avid reader of GitLab’s blog.
|
||||
Karolina has been using GitLab.com for around a year. She roughly spends 8 hours every week programming, of that, 2 hours is spent contributing to open source projects. Karolina contributes to open source projects to gain programming experience and to give back to the community. She likes GitLab.com for its free private repositories and range of features which provide her with everything she needs for her personal projects. Karolina is also a massive fan of GitLab’s values and the fact that it isn’t a “behemoth of a company”. She explains that “displaying every single thing (doc, culture, assumptions, development...) in the open gives me greater confidence to choose Gitlab personally and to recommend it at work.” She’s also an avid reader of GitLab’s blog.
|
||||
|
||||
Harry works for a software development company which currently hires around 500 people. Harry would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. He describes management at his company as “old fashioned” and explains that it’s “less of a technical issue and more of a cultural issue” to convince upper management to move to GitLab. Harry is also relatively new to the company so he’s apprehensive about pushing too hard to change version control platforms.
|
||||
Karolina works for a software development company which currently hires around 500 people. Karolina would love to use GitLab at work but the company has used GitHub Enterprise for a number of years. She describes management at her company as “old fashioned” and explains that it’s “less of a technical issue and more of a cultural issue” to convince upper management to move to GitLab. Karolina is also relatively new to the company so she’s apprehensive about pushing too hard to change version control platforms.
|
||||
|
||||
#### Frustrations
|
||||
##### Unable to use GitLab at work
|
||||
Harry wants to use GitLab at work but isn’t sure how to approach the subject with management. In his current role, he doesn’t feel that he has the authority to request GitLab.
|
||||
Karolina wants to use GitLab at work but isn’t sure how to approach the subject with management. In her current role, she doesn’t feel that she has the authority to request GitLab.
|
||||
|
||||
##### Performance
|
||||
GitLab.com is frequently slow and unavailable. Harry has also heard that GitLab is a “memory hog” which has deterred him from running GitLab on his own machine for just hobby / personal projects.
|
||||
GitLab.com is frequently slow and unavailable. Karolina has also heard that GitLab is a “memory hog” which has deterred her from running GitLab on her own machine for just hobby / personal projects.
|
||||
|
||||
##### UX/UI
|
||||
Harry has an interest in UX and therefore has strong opinions about how GitLab should look and feel. He feels the interface is cluttered, “it has too many links/buttons” and the navigation “feels a bit weird sometimes. I get lost if I don’t pay attention.” As Harry also enjoys contributing to open-source projects, it’s important to him that GitLab is well designed for public repositories, he doesn’t feel that GitLab currently achieves this.
|
||||
Karolina has an interest in UX and therefore has strong opinions about how GitLab should look and feel. She feels the interface is cluttered, “it has too many links/buttons” and the navigation “feels a bit weird sometimes. I get lost if I don’t pay attention.” As Karolina also enjoys contributing to open-source projects, it’s important to her that GitLab is well designed for public repositories, she doesn’t feel that GitLab currently achieves this.
|
||||
|
||||
#### Goals
|
||||
* To develop his programming experience and to learn from other developers.
|
||||
* To contribute to both his own and other open source projects.
|
||||
* To develop her programming experience and to learn from other developers.
|
||||
* To contribute to both her own and other open source projects.
|
||||
* To use a fast and intuitive version control platform.
|
|
@ -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
|
||||
|
|
37
spec/features/user_callout_spec.rb
Normal file
37
spec/features/user_callout_spec.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'User Callouts', js: true do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
project.team << [user, :master]
|
||||
end
|
||||
|
||||
it 'takes you to the profile preferences when the link is clicked' do
|
||||
visit dashboard_projects_path
|
||||
click_link 'Check it out'
|
||||
expect(current_path).to eq profile_preferences_path
|
||||
end
|
||||
|
||||
describe 'user callout should appear in two routes' do
|
||||
it 'shows up on the user profile' do
|
||||
visit user_path(user)
|
||||
expect(find('.user-callout')).to have_content 'Customize your experience'
|
||||
end
|
||||
|
||||
it 'shows up on the dashboard projects' do
|
||||
visit dashboard_projects_path
|
||||
expect(find('.user-callout')).to have_content 'Customize your experience'
|
||||
end
|
||||
end
|
||||
|
||||
it 'hides the user callout when click on the dismiss icon' do
|
||||
visit user_path(user)
|
||||
within('.user-callout') do
|
||||
find('.close-user-callout').click
|
||||
end
|
||||
expect(page).not_to have_selector('#user-callout')
|
||||
end
|
||||
end
|
|
@ -3,7 +3,9 @@
|
|||
/* global boardsMockInterceptor */
|
||||
/* global BoardService */
|
||||
/* global List */
|
||||
/* global ListIssue */
|
||||
/* global listObj */
|
||||
/* global listObjDuplicate */
|
||||
|
||||
require('~/lib/utils/url_utility');
|
||||
require('~/boards/models/issue');
|
||||
|
@ -84,4 +86,23 @@ describe('List model', () => {
|
|||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('sends service request to update issue label', () => {
|
||||
const listDup = new List(listObjDuplicate);
|
||||
const issue = new ListIssue({
|
||||
title: 'Testing',
|
||||
iid: 1,
|
||||
confidential: false,
|
||||
labels: [list.label, listDup.label]
|
||||
});
|
||||
|
||||
list.issues.push(issue);
|
||||
listDup.issues.push(issue);
|
||||
|
||||
spyOn(gl.boardService, 'moveIssue').and.callThrough();
|
||||
|
||||
listDup.updateIssueLabel(list, issue);
|
||||
|
||||
expect(gl.boardService.moveIssue).toHaveBeenCalledWith(issue.id, list.id, listDup.id);
|
||||
});
|
||||
});
|
||||
|
|
2
spec/javascripts/fixtures/user_callout.html.haml
Normal file
2
spec/javascripts/fixtures/user_callout.html.haml
Normal file
|
@ -0,0 +1,2 @@
|
|||
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
|
||||
|
37
spec/javascripts/user_callout_spec.js.es6
Normal file
37
spec/javascripts/user_callout_spec.js.es6
Normal file
|
@ -0,0 +1,37 @@
|
|||
const UserCallout = require('~/user_callout');
|
||||
|
||||
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
|
||||
const Cookie = window.Cookies;
|
||||
|
||||
describe('UserCallout', () => {
|
||||
const fixtureName = 'static/user_callout.html.raw';
|
||||
preloadFixtures(fixtureName);
|
||||
|
||||
beforeEach(function () {
|
||||
loadFixtures(fixtureName);
|
||||
this.userCallout = new UserCallout();
|
||||
this.closeButton = $('.close-user-callout');
|
||||
this.userCalloutBtn = $('.user-callout-btn');
|
||||
this.userCalloutContainer = $('.user-callout');
|
||||
Cookie.set(USER_CALLOUT_COOKIE, 'false');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Cookie.set(USER_CALLOUT_COOKIE, 'false');
|
||||
});
|
||||
|
||||
it('shows when cookie is set to false', function () {
|
||||
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
|
||||
expect(this.userCalloutContainer.is(':visible')).toBe(true);
|
||||
});
|
||||
|
||||
it('hides when user clicks on the dismiss-icon', function () {
|
||||
this.closeButton.click();
|
||||
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
|
||||
});
|
||||
|
||||
it('hides when user clicks on the "check it out" button', function () {
|
||||
this.userCalloutBtn.click();
|
||||
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
|
||||
});
|
||||
});
|
|
@ -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