Merge branch 'master' into jej-pages-picked-from-ee

This commit is contained in:
James Edwards-Jones 2017-02-03 17:25:26 +00:00
commit 1af3f3b673
306 changed files with 3388 additions and 1372 deletions

View File

@ -17,6 +17,7 @@ AllCops:
# Exclude some GitLab files # Exclude some GitLab files
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
- 'node_modules/**/*'
- 'db/*' - 'db/*'
- 'db/fixtures/**/*' - 'db/fixtures/**/*'
- 'tmp/**/*' - 'tmp/**/*'

View File

@ -112,7 +112,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.6' gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0' gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8' gem 'truncato', '~> 0.7.8'

View File

@ -54,7 +54,7 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
asciidoctor-plantuml (0.0.6) asciidoctor-plantuml (0.0.7)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
ast (2.3.0) ast (2.3.0)
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
@ -844,7 +844,7 @@ DEPENDENCIES
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.4.0) asana (~> 0.4.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
asciidoctor-plantuml (= 0.0.6) asciidoctor-plantuml (= 0.0.7)
attr_encrypted (~> 3.0.0) attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)

View File

@ -1,6 +1,19 @@
/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */ /* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */
(function(w) { (function(w) {
$(function() { $(function() {
var toggleContainer = function(container, /* optional */toggleState) {
var $container = $(container);
$container
.find('.js-toggle-button .fa')
.toggleClass('fa-chevron-up', toggleState)
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined);
$container
.find('.js-toggle-content')
.toggle(toggleState);
};
// Toggle button. Show/hide content inside parent container. // Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style. // Button does not change visibility. If button has icon - it changes chevron style.
// //
@ -10,14 +23,7 @@
// //
$('body').on('click', '.js-toggle-button', function(e) { $('body').on('click', '.js-toggle-button', function(e) {
e.preventDefault(); e.preventDefault();
$(this) toggleContainer($(this).closest('.js-toggle-container'));
.find('.fa')
.toggleClass('fa-chevron-down fa-chevron-up')
.end()
.closest('.js-toggle-container')
.find('.js-toggle-content')
.toggle()
;
}); });
// If we're accessing a permalink, ensure it is not inside a // If we're accessing a permalink, ensure it is not inside a
@ -26,8 +32,8 @@
var anchor = hash && document.getElementById(hash); var anchor = hash && document.getElementById(hash);
var container = anchor && $(anchor).closest('.js-toggle-container'); var container = anchor && $(anchor).closest('.js-toggle-container');
if (container && container.find('.js-toggle-content').is(':hidden')) { if (container) {
container.find('.js-toggle-button').trigger('click'); toggleContainer(container, true);
anchor.scrollIntoView(); anchor.scrollIntoView();
} }
}); });

View File

@ -180,9 +180,9 @@
<tr> <tr>
<th class="environments-name">Environment</th> <th class="environments-name">Environment</th>
<th class="environments-deploy">Last deployment</th> <th class="environments-deploy">Last deployment</th>
<th class="environments-build">Build</th> <th class="environments-build">Job</th>
<th class="environments-commit">Commit</th> <th class="environments-commit">Commit</th>
<th class="environments-date">Created</th> <th class="environments-date">Updated</th>
<th class="hidden-xs environments-actions"></th> <th class="hidden-xs environments-actions"></th>
</tr> </tr>
</thead> </thead>

View File

@ -39,8 +39,15 @@
getSearchInput() { getSearchInput() {
const query = gl.DropdownUtils.getSearchInput(this.input); const query = gl.DropdownUtils.getSearchInput(this.input);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
let value = lastToken.value || '';
return lastToken.value || ''; // Removes the first character if it is a quotation so that we can search
// with multiple words
if (value[0] === '"' || value[0] === '\'') {
value = value.slice(1);
}
return value;
} }
init() { init() {

View File

@ -83,12 +83,12 @@
_a = decodeURI("%C3%80"); _a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF"); _y = decodeURI("%C3%BF");
regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi'); regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])(([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
match = regexp.exec(subtext); match = regexp.exec(subtext);
if (match) { if (match) {
return match[2] || match[1]; return (match[1] || match[1] === "") ? match[1] : match[2];
} else { } else {
return null; return null;
} }

View File

@ -249,7 +249,7 @@
_this.fullData = data; _this.fullData = data;
_this.parseData(_this.fullData); _this.parseData(_this.fullData);
_this.focusTextInput(); _this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') { if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
return _this.filter.input.trigger('input'); return _this.filter.input.trigger('input');
} }
}; };

View File

@ -162,6 +162,7 @@
w.gl.utils.getSelectedFragment = () => { w.gl.utils.getSelectedFragment = () => {
const selection = window.getSelection(); const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
const documentFragment = selection.getRangeAt(0).cloneContents(); const documentFragment = selection.getRangeAt(0).cloneContents();
if (documentFragment.textContent.length === 0) return null; if (documentFragment.textContent.length === 0) return null;

View File

@ -110,9 +110,8 @@
}; };
MergeRequest.prototype.initCommitMessageListeners = function() { MergeRequest.prototype.initCommitMessageListeners = function() {
var textarea = $('textarea.js-commit-message'); $(document).on('click', 'a.js-with-description-link', function(e) {
var textarea = $('textarea.js-commit-message');
$('a.js-with-description-link').on('click', function(e) {
e.preventDefault(); e.preventDefault();
textarea.val(textarea.data('messageWithDescription')); textarea.val(textarea.data('messageWithDescription'));
@ -120,7 +119,8 @@
$('p.js-without-description-hint').show(); $('p.js-without-description-hint').show();
}); });
$('a.js-without-description-link').on('click', function(e) { $(document).on('click', 'a.js-without-description-link', function(e) {
var textarea = $('textarea.js-commit-message');
e.preventDefault(); e.preventDefault();
textarea.val(textarea.data('messageWithoutDescription')); textarea.val(textarea.data('messageWithoutDescription'));

View File

@ -154,12 +154,22 @@
return; return;
} }
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments); if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
if (data.status !== _this.opts.ci_status && (data.status != null)) { if (data.status !== _this.opts.ci_status ||
data.sha !== _this.opts.ci_sha ||
data.pipeline !== _this.opts.ci_pipeline) {
_this.opts.ci_status = data.status; _this.opts.ci_status = data.status;
_this.showCIStatus(data.status); _this.showCIStatus(data.status);
if (data.coverage) { if (data.coverage) {
_this.showCICoverage(data.coverage); _this.showCICoverage(data.coverage);
} }
if (data.pipeline) {
_this.opts.ci_pipeline = data.pipeline;
_this.updatePipelineUrls(data.pipeline);
}
if (data.sha) {
_this.opts.ci_sha = data.sha;
_this.updateCommitUrls(data.sha);
}
if (showNotification) { if (showNotification) {
status = _this.ciLabelForStatus(data.status); status = _this.ciLabelForStatus(data.status);
if (status === "preparing") { if (status === "preparing") {
@ -248,6 +258,16 @@
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class); return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
}; };
MergeRequestWidget.prototype.updatePipelineUrls = function(id) {
const pipelineUrl = this.opts.pipeline_path;
$('.pipeline').text(`#${id}`).attr('href', [pipelineUrl, id].join('/'));
};
MergeRequestWidget.prototype.updateCommitUrls = function(id) {
const commitsUrl = this.opts.commits_path;
$('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/'));
};
return MergeRequestWidget; return MergeRequestWidget;
})(); })();
})(window.gl || (window.gl = {})); })(window.gl || (window.gl = {}));

View File

@ -58,6 +58,11 @@
}; };
Project.prototype.initRefSwitcher = function() { Project.prototype.initRefSwitcher = function() {
var refListItem = document.createElement('li'),
refLink = document.createElement('a');
refLink.href = '#';
return $('.js-project-refs-dropdown').each(function() { return $('.js-project-refs-dropdown').each(function() {
var $dropdown, selected; var $dropdown, selected;
$dropdown = $(this); $dropdown = $(this);
@ -67,7 +72,8 @@
return $.ajax({ return $.ajax({
url: $dropdown.data('refs-url'), url: $dropdown.data('refs-url'),
data: { data: {
ref: $dropdown.data('ref') ref: $dropdown.data('ref'),
search: term
}, },
dataType: "json" dataType: "json"
}).done(function(refs) { }).done(function(refs) {
@ -76,16 +82,29 @@
}, },
selectable: true, selectable: true,
filterable: true, filterable: true,
filterRemote: true,
filterByText: true, filterByText: true,
fieldName: $dropdown.data('field-name'), fieldName: $dropdown.data('field-name'),
renderRow: function(ref) { renderRow: function(ref) {
var link; var li = refListItem.cloneNode(false);
if (ref.header != null) { if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header); li.className = 'dropdown-header';
li.textContent = ref.header;
} else { } else {
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', ref); var link = refLink.cloneNode(false);
return $('<li />').append(link);
if (ref === selected) {
link.className = 'is-active';
}
link.textContent = ref;
link.dataset.ref = ref;
li.appendChild(link);
} }
return li;
}, },
id: function(obj, $el) { id: function(obj, $el) {
return $el.attr('data-ref'); return $el.attr('data-ref');

View File

@ -49,7 +49,7 @@ class ProtectedBranchDropdown {
onClickCreateWildcard() { onClickCreateWildcard() {
// Refresh the dropdown's data, which ends up calling `getProtectedBranches` // Refresh the dropdown's data, which ends up calling `getProtectedBranches`
this.$dropdown.data('glDropdown').remote.execute(); this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0); this.$dropdown.data('glDropdown').selectRowAtIndex();
} }
getProtectedBranches(term, callback) { getProtectedBranches(term, callback) {

View File

@ -39,17 +39,20 @@
} }
ShortcutsIssuable.prototype.replyWithSelectedText = function() { ShortcutsIssuable.prototype.replyWithSelectedText = function() {
var quote, replyField, documentFragment, selected, separator; var quote, documentFragment, selected, separator;
var replyField = $('.js-main-target-form #note_note');
documentFragment = window.gl.utils.getSelectedFragment(); documentFragment = window.gl.utils.getSelectedFragment();
if (!documentFragment) return; if (!documentFragment) {
replyField.focus();
return;
}
// If the documentFragment contains more than just Markdown, don't copy as GFM. // If the documentFragment contains more than just Markdown, don't copy as GFM.
if (documentFragment.querySelector('.md, .wiki')) return; if (documentFragment.querySelector('.md, .wiki')) return;
selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment); selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment);
replyField = $('.js-main-target-form #note_note');
if (selected.trim() === "") { if (selected.trim() === "") {
return; return;
} }

View File

@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */
/* global d3 */ /* global d3 */
/* global dateFormat */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@ -33,7 +32,7 @@
date.setDate(date.getDate() + i); date.setDate(date.getDate() + i);
var day = date.getDay(); var day = date.getDay();
var count = timestamps[dateFormat(date, 'yyyy-mm-dd')]; var count = timestamps[date.format('yyyy-mm-dd')];
// Create a new group array if this is the first day of the week // Create a new group array if this is the first day of the week
// or if is first object // or if is first object
@ -122,7 +121,7 @@
if (stamp.count > 0) { if (stamp.count > 0) {
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
} }
dateText = dateFormat(date, 'mmm d, yyyy'); dateText = date.format('mmm d, yyyy');
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText; return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
}; };
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {

View File

@ -26,10 +26,9 @@
v-if='actions' v-if='actions'
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions" class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
data-toggle="dropdown" data-toggle="dropdown"
title="Manual build" title="Manual job"
data-placement="top" data-placement="top"
data-toggle="dropdown" aria-label="Manual job"
aria-label="Manual build"
> >
<span v-html='svgs.iconPlay' aria-hidden="true"></span> <span v-html='svgs.iconPlay' aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
@ -54,7 +53,6 @@
data-toggle="dropdown" data-toggle="dropdown"
title="Artifacts" title="Artifacts"
data-placement="top" data-placement="top"
data-toggle="dropdown"
aria-label="Artifacts" aria-label="Artifacts"
> >
<i class="fa fa-download" aria-hidden="true"></i> <i class="fa fa-download" aria-hidden="true"></i>

View File

@ -330,10 +330,6 @@
} }
} }
.btn-file-option {
background: linear-gradient(180deg, $white-light 25%, $gray-light 100%);
}
.btn-build { .btn-build {
margin-left: 10px; margin-left: 10px;

View File

@ -56,15 +56,24 @@
&.right { &.right {
float: right; float: right;
padding-right: 0; padding-right: 0;
a {
color: $gl-text-color;
}
} }
.remove_source_checkbox { .modify-merge-commit-link {
color: $gl-text-color;
}
.merge-param-checkbox {
margin: 0; margin: 0;
} }
a .fa-question-circle {
color: $gl-text-color-secondary;
&:hover,
&:focus {
color: $link-hover-color;
}
}
} }
} }

View File

@ -467,7 +467,7 @@ ul.notes {
} }
.add-diff-note { .add-diff-note {
margin-top: -4px; margin-top: -8px;
border-radius: 40px; border-radius: 40px;
background: $white-light; background: $white-light;
padding: 4px; padding: 4px;

View File

@ -201,7 +201,8 @@
.stage-container { .stage-container {
display: inline-block; display: inline-block;
position: relative; position: relative;
margin-right: 6px; height: 22px;
margin: 3px 6px 3px 0;
.tooltip { .tooltip {
white-space: nowrap; white-space: nowrap;

View File

@ -45,7 +45,7 @@ class Admin::ProjectsController < Admin::ApplicationController
protected protected
def project def project
@project = Project.find_with_namespace( @project = Project.find_by_full_path(
[params[:namespace_id], '/', params[:id]].join('') [params[:namespace_id], '/', params[:id]].join('')
) )
@project || render_404 @project || render_404

View File

@ -24,7 +24,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
private private
def project def project
@project = Project.find_with_namespace( @project = Project.find_by_full_path(
[params[:namespace_id], '/', params[:project_id]].join('') [params[:namespace_id], '/', params[:project_id]].join('')
) )
@project || render_404 @project || render_404

View File

@ -4,13 +4,15 @@ module CreatesCommit
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil) def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
set_commit_variables set_commit_variables
start_branch = @mr_target_branch unless initial_commit?
commit_params = @commit_params.merge( commit_params = @commit_params.merge(
source_project: @project, start_project: @mr_target_project,
source_branch: @ref, start_branch: start_branch,
target_branch: @target_branch target_branch: @mr_source_branch
) )
result = service.new(@tree_edit_project, current_user, commit_params).execute result = service.new(
@mr_source_project, current_user, commit_params).execute
if result[:status] == :success if result[:status] == :success
update_flash_notice(success_notice) update_flash_notice(success_notice)
@ -89,20 +91,18 @@ module CreatesCommit
@mr_source_project != @mr_target_project @mr_source_project != @mr_target_project
end end
def different_branch?
@mr_source_branch != @mr_target_branch || different_project?
end
def create_merge_request? def create_merge_request?
params[:create_merge_request].present? && different_branch? # XXX: Even if the field is set, if we're checking the same branch
# as the target branch in the same project,
# we don't want to create a merge request.
params[:create_merge_request].present? &&
(different_project? || @ref != @target_branch)
end end
# TODO: We should really clean this up
def set_commit_variables def set_commit_variables
@mr_source_branch ||= @target_branch
if can?(current_user, :push_code, @project) if can?(current_user, :push_code, @project)
# Edit file in this project # Edit file in this project
@tree_edit_project = @project
@mr_source_project = @project @mr_source_project = @project
if @project.forked? if @project.forked?
@ -112,15 +112,34 @@ module CreatesCommit
else else
# Merge request to this project # Merge request to this project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch ||= @ref @mr_target_branch = @ref || @target_branch
end end
else else
# Edit file in fork
@tree_edit_project = current_user.fork_of(@project)
# Merge request from fork to this project # Merge request from fork to this project
@mr_source_project = @tree_edit_project @mr_source_project = current_user.fork_of(@project)
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch ||= @ref @mr_target_branch = @ref || @target_branch
end end
@mr_source_branch = guess_mr_source_branch
end
def initial_commit?
@mr_target_branch.nil? ||
!@mr_target_project.repository.branch_exists?(@mr_target_branch)
end
def guess_mr_source_branch
# XXX: Happens when viewing a commit without a branch. In this case,
# @target_branch would be the default branch for @mr_source_project,
# however we want a generated new branch here. Thus we can't use
# @target_branch, but should pass nil to indicate that we want a new
# branch instead of @target_branch.
return if
create_merge_request? &&
# XXX: Don't understand why rubocop prefers this indention
@mr_source_project.repository.branch_exists?(@target_branch)
@target_branch
end end
end end

View File

@ -7,7 +7,7 @@ module SpammableActions
def mark_as_spam def mark_as_spam
if SpamService.new(spammable).mark_as_spam! if SpamService.new(spammable).mark_as_spam!
redirect_to spammable, notice: "#{spammable.class} was submitted to Akismet successfully." redirect_to spammable, notice: "#{spammable.spammable_entity_type.titlecase} was submitted to Akismet successfully."
else else
redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.' redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.'
end end

View File

@ -10,10 +10,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]) @projects = @projects.page(params[:page])
@last_push = current_user.recent_push
respond_to do |format| respond_to do |format|
format.html format.html { @last_push = current_user.recent_push }
format.atom do format.atom do
event_filter event_filter
load_events load_events

View File

@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController
if Groups::UpdateService.new(@group, current_user, group_params).execute if Groups::UpdateService.new(@group, current_user, group_params).execute
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated." redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else else
@group.reset_path! @group.restore_path!
render action: "edit" render action: "edit"
end end

View File

@ -24,7 +24,7 @@ class Projects::ApplicationController < ApplicationController
end end
project_path = "#{namespace}/#{id}" project_path = "#{namespace}/#{id}"
@project = Project.find_with_namespace(project_path) @project = Project.find_by_full_path(project_path)
if can?(current_user, :read_project, @project) && !@project.pending_delete? if can?(current_user, :read_project, @project) && !@project.pending_delete?
if @project.path_with_namespace != project_path if @project.path_with_namespace != project_path

View File

@ -50,7 +50,7 @@ class Projects::CommitController < Projects::ApplicationController
end end
def revert def revert
assign_change_commit_vars(@commit.revert_branch_name) assign_change_commit_vars
return render_404 if @target_branch.blank? return render_404 if @target_branch.blank?
@ -59,7 +59,7 @@ class Projects::CommitController < Projects::ApplicationController
end end
def cherry_pick def cherry_pick
assign_change_commit_vars(@commit.cherry_pick_branch_name) assign_change_commit_vars
return render_404 if @target_branch.blank? return render_404 if @target_branch.blank?
@ -116,11 +116,9 @@ class Projects::CommitController < Projects::ApplicationController
} }
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars
@commit = project.commit(params[:id]) @commit = project.commit(params[:id])
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@mr_source_branch = mr_source_branch
@mr_target_branch = @target_branch
@commit_params = { @commit_params = {
commit: @commit, commit: @commit,
create_merge_request: params[:create_merge_request].present? || different_project? create_merge_request: params[:create_merge_request].present? || different_project?

View File

@ -46,7 +46,8 @@ class Projects::CompareController < Projects::ApplicationController
end end
def define_diff_vars def define_diff_vars
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref) @compare = CompareService.new(@project, @head_ref)
.execute(@project, @start_ref)
if @compare if @compare
@commits = @compare.commits @commits = @compare.commits

View File

@ -79,7 +79,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
if project_id.blank? if project_id.blank?
@project = nil @project = nil
else else
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}") @project = Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
end end
end end

View File

@ -434,7 +434,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
title: merge_request.title, title: merge_request.title,
sha: (merge_request.diff_head_commit.short_id if merge_request.diff_head_sha), sha: (merge_request.diff_head_commit.short_id if merge_request.diff_head_sha),
status: status, status: status,
coverage: coverage coverage: coverage,
pipeline: pipeline.try(:id)
} }
render json: response render json: response

View File

@ -1,8 +1,9 @@
class Projects::SnippetsController < Projects::ApplicationController class Projects::SnippetsController < Projects::ApplicationController
include ToggleAwardEmoji include ToggleAwardEmoji
include SpammableActions
before_action :module_enabled before_action :module_enabled
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
# Allow read any snippet # Allow read any snippet
before_action :authorize_read_project_snippet!, except: [:new, :create, :index] before_action :authorize_read_project_snippet!, except: [:new, :create, :index]
@ -36,8 +37,8 @@ class Projects::SnippetsController < Projects::ApplicationController
end end
def create def create
@snippet = CreateSnippetService.new(@project, current_user, create_params = snippet_params.merge(request: request)
snippet_params).execute @snippet = CreateSnippetService.new(@project, current_user, create_params).execute
if @snippet.valid? if @snippet.valid?
respond_with(@snippet, respond_with(@snippet,
@ -88,6 +89,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet ||= @project.snippets.find(params[:id]) @snippet ||= @project.snippets.find(params[:id])
end end
alias_method :awardable, :snippet alias_method :awardable, :snippet
alias_method :spammable, :snippet
def authorize_read_project_snippet! def authorize_read_project_snippet!
return render_404 unless can?(current_user, :read_project_snippet, @snippet) return render_404 unless can?(current_user, :read_project_snippet, @snippet)

View File

@ -36,7 +36,7 @@ class Projects::UploadsController < Projects::ApplicationController
namespace = params[:namespace_id] namespace = params[:namespace_id]
id = params[:project_id] id = params[:project_id]
file_project = Project.find_with_namespace("#{namespace}/#{id}") file_project = Project.find_by_full_path("#{namespace}/#{id}")
if file_project.nil? if file_project.nil?
@uploader = nil @uploader = nil

View File

@ -231,12 +231,16 @@ class ProjectsController < Projects::ApplicationController
end end
def refs def refs
branches = BranchesFinder.new(@repository, params).execute.map(&:name)
options = { options = {
'Branches' => @repository.branch_names, 'Branches' => branches.take(100),
} }
unless @repository.tag_count.zero? unless @repository.tag_count.zero?
options['Tags'] = VersionSorter.rsort(@repository.tag_names) tags = TagsFinder.new(@repository, params).execute.map(&:name)
options['Tags'] = tags.take(100)
end end
# If reference is commit id - we should add it to branch/tag selectbox # If reference is commit id - we should add it to branch/tag selectbox

View File

@ -1,5 +1,6 @@
class SnippetsController < ApplicationController class SnippetsController < ApplicationController
include ToggleAwardEmoji include ToggleAwardEmoji
include SpammableActions
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
@ -40,8 +41,8 @@ class SnippetsController < ApplicationController
end end
def create def create
@snippet = CreateSnippetService.new(nil, current_user, create_params = snippet_params.merge(request: request)
snippet_params).execute @snippet = CreateSnippetService.new(nil, current_user, create_params).execute
respond_with @snippet.becomes(Snippet) respond_with @snippet.becomes(Snippet)
end end
@ -96,6 +97,7 @@ class SnippetsController < ApplicationController
end end
end end
alias_method :awardable, :snippet alias_method :awardable, :snippet
alias_method :spammable, :snippet
def authorize_read_snippet! def authorize_read_snippet!
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)

View File

@ -37,7 +37,7 @@ module ApplicationHelper
if project_id.is_a?(Project) if project_id.is_a?(Project)
project_id project_id
else else
Project.find_with_namespace(project_id) Project.find_by_full_path(project_id)
end end
if project.avatar_url if project.avatar_url

View File

@ -21,7 +21,7 @@ module BlobHelper
options[:link_opts]) options[:link_opts])
if !on_top_of_branch?(project, ref) if !on_top_of_branch?(project, ref)
button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' } button_tag "Edit", class: "btn disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref) elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn btn-sm' link_to "Edit", edit_path, class: 'btn btn-sm'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
@ -32,7 +32,7 @@ module BlobHelper
} }
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params) fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to "Edit", fork_path, class: 'btn btn-file-option', method: :post link_to "Edit", fork_path, class: 'btn', method: :post
end end
end end

View File

@ -198,7 +198,7 @@ module CommitsHelper
link_to( link_to(
namespace_project_blob_path(project.namespace, project, namespace_project_blob_path(project.namespace, project,
tree_join(commit_sha, diff_new_path)), tree_join(commit_sha, diff_new_path)),
class: 'btn view-file js-view-file btn-file-option' class: 'btn view-file js-view-file'
) do ) do
raw('View file @') + content_tag(:span, commit_sha[0..6], raw('View file @') + content_tag(:span, commit_sha[0..6],
class: 'commit-short-id') class: 'commit-short-id')

View File

@ -143,4 +143,16 @@ module MergeRequestsHelper
def different_base?(version1, version2) def different_base?(version1, version2)
version1 && version2 && version1.base_commit_sha != version2.base_commit_sha version1 && version2 && version1.base_commit_sha != version2.base_commit_sha
end end
def merge_params(merge_request)
{
merge_when_build_succeeds: true,
should_remove_source_branch: true,
sha: merge_request.diff_head_sha
}.merge(merge_params_ee(merge_request))
end
def merge_params_ee(merge_request)
{}
end
end end

View File

@ -93,10 +93,6 @@ module VisibilityLevelHelper
current_application_settings.default_project_visibility current_application_settings.default_project_visibility
end end
def default_snippet_visibility
current_application_settings.default_snippet_visibility
end
def default_group_visibility def default_group_visibility
current_application_settings.default_group_visibility current_application_settings.default_group_visibility
end end

View File

@ -275,29 +275,23 @@ module Ci
end end
def update_coverage def update_coverage
return unless project
coverage_regex = project.build_coverage_regex
return unless coverage_regex
coverage = extract_coverage(trace, coverage_regex) coverage = extract_coverage(trace, coverage_regex)
update_attributes(coverage: coverage) if coverage.present?
if coverage.is_a? Numeric
update_attributes(coverage: coverage)
end
end end
def extract_coverage(text, regex) def extract_coverage(text, regex)
begin return unless regex
matches = text.scan(Regexp.new(regex)).last
matches = matches.last if matches.kind_of?(Array)
coverage = matches.gsub(/\d+(\.\d+)?/).first
if coverage.present? matches = text.scan(Regexp.new(regex)).last
coverage.to_f matches = matches.last if matches.kind_of?(Array)
end coverage = matches.gsub(/\d+(\.\d+)?/).first
rescue
# if bad regex or something goes wrong we dont want to interrupt transition if coverage.present?
# so we just silentrly ignore error for now coverage.to_f
end end
rescue
# if bad regex or something goes wrong we dont want to interrupt transition
# so we just silentrly ignore error for now
end end
def has_trace_file? def has_trace_file?
@ -523,6 +517,10 @@ module Ci
self.update(artifacts_expire_at: nil) self.update(artifacts_expire_at: nil)
end end
def coverage_regex
super || project.try(:build_coverage_regex)
end
def when def when
read_attribute(:when) || build_attributes_from_config[:when] || 'on_success' read_attribute(:when) || build_attributes_from_config[:when] || 'on_success'
end end

View File

@ -34,7 +34,13 @@ module Spammable
end end
def check_for_spam def check_for_spam
self.errors.add(:base, "Your #{self.class.name.underscore} has been recognized as spam and has been discarded.") if spam? if spam?
self.errors.add(:base, "Your #{spammable_entity_type} has been recognized as spam and has been discarded.")
end
end
def spammable_entity_type
self.class.name.underscore
end end
def spam_title def spam_title

View File

@ -169,7 +169,8 @@ class MergeRequestDiff < ActiveRecord::Base
# When compare merge request versions we want diff A..B instead of A...B # When compare merge request versions we want diff A..B instead of A...B
# so we handle cases when user does squash and rebase of the commits between versions. # so we handle cases when user does squash and rebase of the commits between versions.
# For this reason we set straight to true by default. # For this reason we set straight to true by default.
CompareService.new.execute(project, head_commit_sha, project, sha, straight: straight) CompareService.new(project, head_commit_sha)
.execute(project, sha, straight: straight)
end end
def commits_count def commits_count

View File

@ -373,10 +373,6 @@ class Project < ActiveRecord::Base
def group_ids def group_ids
joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id) joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
end end
# Add alias for Routable method for compatibility with old code.
# In future all calls `find_with_namespace` should be replaced with `find_by_full_path`
alias_method :find_with_namespace, :find_by_full_path
end end
def lfs_enabled? def lfs_enabled?
@ -1395,6 +1391,6 @@ class Project < ActiveRecord::Base
def pending_delete_twin def pending_delete_twin
return false unless path return false unless path
Project.unscoped.where(pending_delete: true).find_with_namespace(path_with_namespace) Project.unscoped.where(pending_delete: true).find_by_full_path(path_with_namespace)
end end
end end

View File

@ -31,13 +31,13 @@ class ChatSlashCommandsService < Service
return unless valid_token?(params[:token]) return unless valid_token?(params[:token])
user = find_chat_user(params) user = find_chat_user(params)
unless user
url = authorize_chat_name_url(params)
return presenter.authorize_chat_name(url)
end
Gitlab::ChatCommands::Command.new(project, user, if user
params).execute Gitlab::ChatCommands::Command.new(project, user, params).execute
else
url = authorize_chat_name_url(params)
Gitlab::ChatCommands::Presenters::Access.new(url).authorize
end
end end
private private
@ -49,8 +49,4 @@ class ChatSlashCommandsService < Service
def authorize_chat_name_url(params) def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute ChatNames::AuthorizeUserService.new(self, params).execute
end end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end end

View File

@ -9,4 +9,8 @@ class ProjectSnippet < Snippet
participant :author participant :author
participant :notes_with_associations participant :notes_with_associations
def check_for_spam?
super && project.public?
end
end end

View File

@ -5,7 +5,7 @@ class Repository
attr_accessor :path_with_namespace, :project attr_accessor :path_with_namespace, :project
class CommitError < StandardError; end CommitError = Class.new(StandardError)
# Methods that cache data from the Git repository. # Methods that cache data from the Git repository.
# #
@ -64,10 +64,6 @@ class Repository
@raw_repository ||= Gitlab::Git::Repository.new(path_to_repo) @raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
end end
def update_autocrlf_option
raw_repository.autocrlf = :input if raw_repository.autocrlf != :input
end
# Return absolute path to repository # Return absolute path to repository
def path_to_repo def path_to_repo
@path_to_repo ||= File.expand_path( @path_to_repo ||= File.expand_path(
@ -168,63 +164,46 @@ class Repository
tags.find { |tag| tag.name == name } tags.find { |tag| tag.name == name }
end end
def add_branch(user, branch_name, target) def add_branch(user, branch_name, ref)
oldrev = Gitlab::Git::BLANK_SHA newrev = commit(ref).try(:sha)
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
target = commit(target).try(:id)
return false unless target return false unless newrev
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do GitOperationService.new(user, self).add_branch(branch_name, newrev)
update_ref!(ref, target, oldrev)
end
after_create_branch after_create_branch
find_branch(branch_name) find_branch(branch_name)
end end
def add_tag(user, tag_name, target, message = nil) def add_tag(user, tag_name, target, message = nil)
oldrev = Gitlab::Git::BLANK_SHA newrev = commit(target).try(:id)
ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
target = commit(target).try(:id)
return false unless target
options = { message: message, tagger: user_to_committer(user) } if message options = { message: message, tagger: user_to_committer(user) } if message
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service| return false unless newrev
raw_tag = rugged.tags.create(tag_name, target, options)
service.newrev = raw_tag.target_id GitOperationService.new(user, self).add_tag(tag_name, newrev, options)
end
find_tag(tag_name) find_tag(tag_name)
end end
def rm_branch(user, branch_name) def rm_branch(user, branch_name)
before_remove_branch before_remove_branch
branch = find_branch(branch_name) branch = find_branch(branch_name)
oldrev = branch.try(:dereferenced_target).try(:id)
newrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do GitOperationService.new(user, self).rm_branch(branch)
update_ref!(ref, newrev, oldrev)
end
after_remove_branch after_remove_branch
true true
end end
def rm_tag(tag_name) def rm_tag(user, tag_name)
before_remove_tag before_remove_tag
tag = find_tag(tag_name)
begin GitOperationService.new(user, self).rm_tag(tag)
rugged.tags.delete(tag_name)
true after_remove_tag
rescue Rugged::ReferenceError true
false
end
end end
def ref_names def ref_names
@ -241,21 +220,6 @@ class Repository
false false
end end
def update_ref!(name, newrev, oldrev)
# We use 'git update-ref' because libgit2/rugged currently does not
# offer 'compare and swap' ref updates. Without compare-and-swap we can
# (and have!) accidentally reset the ref to an earlier state, clobbering
# commits. See also https://github.com/libgit2/libgit2/issues/1534.
command = %W(#{Gitlab.config.git.bin_path} update-ref --stdin -z)
_, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin|
stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00")
end
return if status.zero?
raise CommitError.new("Could not update branch #{name.sub('refs/heads/', '')}. Please refresh and try again.")
end
# Makes sure a commit is kept around when Git garbage collection runs. # Makes sure a commit is kept around when Git garbage collection runs.
# Git GC will delete commits from the repository that are no longer in any # Git GC will delete commits from the repository that are no longer in any
# branches or tags, but we want to keep some of these commits around, for # branches or tags, but we want to keep some of these commits around, for
@ -435,6 +399,11 @@ class Repository
repository_event(:remove_tag) repository_event(:remove_tag)
end end
# Runs code after removing a tag.
def after_remove_tag
expire_tags_cache
end
def before_import def before_import
expire_content_cache expire_content_cache
end end
@ -779,121 +748,132 @@ class Repository
@tags ||= raw_repository.tags @tags ||= raw_repository.tags
end end
def commit_dir(user, path, message, branch, author_email: nil, author_name: nil) # rubocop:disable Metrics/ParameterLists
update_branch_with_hooks(user, branch) do |ref| def commit_dir(
options = { user, path,
commit: { message:, branch_name:,
branch: ref, author_email: nil, author_name: nil,
message: message, start_branch_name: nil, start_project: project)
update_ref: false check_tree_entry_for_dir(branch_name, path)
}
}
options.merge!(get_committer_and_author(user, email: author_email, name: author_name)) if start_branch_name
start_project.repository.
raw_repository.mkdir(path, options) check_tree_entry_for_dir(start_branch_name, path)
end 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)
end end
# rubocop:enable Metrics/ParameterLists
def commit_file(user, path, content, message, branch, update, author_email: nil, author_name: nil) # rubocop:disable Metrics/ParameterLists
update_branch_with_hooks(user, branch) do |ref| def commit_file(
options = { user, path, content,
commit: { message:, branch_name:, update: true,
branch: ref, author_email: nil, author_name: nil,
message: message, start_branch_name: nil, start_project: project)
update_ref: false unless update
}, error_message = "Filename already exists; update not allowed"
file: {
content: content,
path: path,
update: update
}
}
options.merge!(get_committer_and_author(user, email: author_email, name: author_name)) if tree_entry_at(branch_name, path)
raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
end
Gitlab::Git::Blob.commit(raw_repository, options) if start_branch_name &&
end start_project.repository.tree_entry_at(start_branch_name, path)
end raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
def update_file(user, path, content, branch:, previous_path:, message:, author_email: nil, author_name: nil)
update_branch_with_hooks(user, branch) do |ref|
options = {
commit: {
branch: ref,
message: message,
update_ref: false
},
file: {
content: content,
path: path,
update: true
}
}
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
if previous_path && previous_path != path
options[:file][:previous_path] = previous_path
Gitlab::Git::Blob.rename(raw_repository, options)
else
Gitlab::Git::Blob.commit(raw_repository, options)
end end
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 }])
end end
# rubocop:enable Metrics/ParameterLists
def remove_file(user, path, message, branch, author_email: nil, author_name: nil) # rubocop:disable Metrics/ParameterLists
update_branch_with_hooks(user, branch) do |ref| def update_file(
options = { user, path, content,
commit: { message:, branch_name:, previous_path:,
branch: ref, author_email: nil, author_name: nil,
message: message, start_branch_name: nil, start_project: project)
update_ref: false action = if previous_path && previous_path != path
}, :move
file: { else
path: path :update
} end
}
options.merge!(get_committer_and_author(user, email: author_email, name: author_name)) multi_action(
user: user,
Gitlab::Git::Blob.remove(raw_repository, options) message: message,
end 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 }])
end end
# rubocop:enable Metrics/ParameterLists
def multi_action(user:, branch:, message:, actions:, author_email: nil, author_name: nil) # rubocop:disable Metrics/ParameterLists
update_branch_with_hooks(user, branch) do |ref| 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 }])
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 index = rugged.index
parents = []
branch = find_branch(ref)
if branch parents = if start_commit
last_commit = branch.dereferenced_target index.read_tree(start_commit.raw_commit.tree)
index.read_tree(last_commit.raw_commit.tree) [start_commit.sha]
parents = [last_commit.sha] else
end []
end
actions.each do |action| actions.each do |act|
case action[:action] git_action(index, act)
when :create, :update, :move
mode =
case action[:action]
when :update
index.get(action[:file_path])[:mode]
when :move
index.get(action[:previous_path])[:mode]
end
mode ||= 0o100644
index.remove(action[:previous_path]) if action[:action] == :move
content = action[:encoding] == 'base64' ? Base64.decode64(action[:content]) : action[:content]
oid = rugged.write(content, :blob)
index.add(path: action[:file_path], oid: oid, mode: mode)
when :delete
index.remove(action[:file_path])
end
end end
options = { options = {
@ -906,6 +886,7 @@ class Repository
Rugged::Commit.create(rugged, options) Rugged::Commit.create(rugged, options)
end end
end end
# rubocop:enable Metrics/ParameterLists
def get_committer_and_author(user, email: nil, name: nil) def get_committer_and_author(user, email: nil, name: nil)
committer = user_to_committer(user) committer = user_to_committer(user)
@ -918,7 +899,7 @@ class Repository
end end
def user_to_committer(user) def user_to_committer(user)
Gitlab::Git::committer_hash(email: user.email, name: user.name) Gitlab::Git.committer_hash(email: user.email, name: user.name)
end end
def can_be_merged?(source_sha, target_branch) def can_be_merged?(source_sha, target_branch)
@ -932,17 +913,18 @@ class Repository
end end
end end
def merge(user, merge_request, options = {}) def merge(user, source, merge_request, options = {})
our_commit = rugged.branches[merge_request.target_branch].target GitOperationService.new(user, self).with_branch(
their_commit = rugged.lookup(merge_request.diff_head_sha) merge_request.target_branch) do |start_commit|
our_commit = start_commit.sha
their_commit = source
raise "Invalid merge target" if our_commit.nil? raise 'Invalid merge target' unless our_commit
raise "Invalid merge source" if their_commit.nil? raise 'Invalid merge source' unless their_commit
merge_index = rugged.merge_commits(our_commit, their_commit) merge_index = rugged.merge_commits(our_commit, their_commit)
return false if merge_index.conflicts? break if merge_index.conflicts?
update_branch_with_hooks(user, merge_request.target_branch) do
actual_options = options.merge( actual_options = options.merge(
parents: [our_commit, their_commit], parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged), tree: merge_index.write_tree(rugged),
@ -952,34 +934,48 @@ class Repository
merge_request.update(in_progress_merge_commit_sha: commit_id) merge_request.update(in_progress_merge_commit_sha: commit_id)
commit_id commit_id
end end
rescue Repository::CommitError # when merge_index.conflicts?
false
end end
def revert(user, commit, base_branch, revert_tree_id = nil) def revert(
source_sha = find_branch(base_branch).dereferenced_target.sha user, commit, branch_name, revert_tree_id = nil,
revert_tree_id ||= check_revert_content(commit, base_branch) start_branch_name: nil, start_project: project)
revert_tree_id ||= check_revert_content(commit, branch_name)
return false unless revert_tree_id return false unless revert_tree_id
update_branch_with_hooks(user, base_branch) do GitOperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_project: start_project) do |start_commit|
committer = user_to_committer(user) committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
Rugged::Commit.create(rugged,
message: commit.revert_message(user), message: commit.revert_message(user),
author: committer, author: committer,
committer: committer, committer: committer,
tree: revert_tree_id, tree: revert_tree_id,
parents: [rugged.lookup(source_sha)]) parents: [start_commit.sha])
end end
end end
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil) def cherry_pick(
source_sha = find_branch(base_branch).dereferenced_target.sha user, commit, branch_name, cherry_pick_tree_id = nil,
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch) start_branch_name: nil, start_project: project)
cherry_pick_tree_id ||= check_cherry_pick_content(commit, branch_name)
return false unless cherry_pick_tree_id return false unless cherry_pick_tree_id
update_branch_with_hooks(user, base_branch) do GitOperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_project: start_project) do |start_commit|
committer = user_to_committer(user) committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
Rugged::Commit.create(rugged,
message: commit.message, message: commit.message,
author: { author: {
email: commit.author_email, email: commit.author_email,
@ -988,22 +984,22 @@ class Repository
}, },
committer: committer, committer: committer,
tree: cherry_pick_tree_id, tree: cherry_pick_tree_id,
parents: [rugged.lookup(source_sha)]) parents: [start_commit.sha])
end end
end end
def resolve_conflicts(user, branch, params) def resolve_conflicts(user, branch_name, params)
update_branch_with_hooks(user, branch) do GitOperationService.new(user, self).with_branch(branch_name) do
committer = user_to_committer(user) committer = user_to_committer(user)
Rugged::Commit.create(rugged, params.merge(author: committer, committer: committer)) Rugged::Commit.create(rugged, params.merge(author: committer, committer: committer))
end end
end end
def check_revert_content(commit, base_branch) def check_revert_content(target_commit, branch_name)
source_sha = find_branch(base_branch).dereferenced_target.sha source_sha = commit(branch_name).sha
args = [commit.id, source_sha] args = [target_commit.sha, source_sha]
args << { mainline: 1 } if commit.merge_commit? args << { mainline: 1 } if target_commit.merge_commit?
revert_index = rugged.revert_commit(*args) revert_index = rugged.revert_commit(*args)
return false if revert_index.conflicts? return false if revert_index.conflicts?
@ -1014,10 +1010,10 @@ class Repository
tree_id tree_id
end end
def check_cherry_pick_content(commit, base_branch) def check_cherry_pick_content(target_commit, branch_name)
source_sha = find_branch(base_branch).dereferenced_target.sha source_sha = commit(branch_name).sha
args = [commit.id, source_sha] args = [target_commit.sha, source_sha]
args << 1 if commit.merge_commit? args << 1 if target_commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args) cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts? return false if cherry_pick_index.conflicts?
@ -1075,6 +1071,28 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip) Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip)
end end
def with_repo_branch_commit(start_repository, start_branch_name)
branch_name_or_sha =
if start_repository == self
start_branch_name
else
tmp_ref = "refs/tmp/#{SecureRandom.hex}/head"
fetch_ref(
start_repository.path_to_repo,
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}",
tmp_ref
)
start_repository.commit(start_branch_name).sha
end
yield(commit(branch_name_or_sha))
ensure
rugged.references.delete(tmp_ref) if tmp_ref
end
def fetch_ref(source_path, source_ref, target_ref) def fetch_ref(source_path, source_ref, target_ref)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo) Gitlab::Popen.popen(args, path_to_repo)
@ -1084,39 +1102,6 @@ class Repository
fetch_ref(path_to_repo, ref, ref_path) fetch_ref(path_to_repo, ref, ref_path)
end end
def update_branch_with_hooks(current_user, branch)
update_autocrlf_option
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
target_branch = find_branch(branch)
was_empty = empty?
# Make commit
newrev = yield(ref)
unless newrev
raise CommitError.new('Failed to create commit')
end
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
oldrev = Gitlab::Git::BLANK_SHA
else
oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha)
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
update_ref!(ref, newrev, oldrev)
if was_empty || !target_branch
# If repo was empty expire cache
after_create if was_empty
after_create_branch
end
end
newrev
end
def ls_files(ref) def ls_files(ref)
actual_ref = ref || root_ref actual_ref = ref || root_ref
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
@ -1175,8 +1160,76 @@ class Repository
end end
end 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 private
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
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? def refs_directory_exists?
return false unless path_with_namespace return false unless path_with_namespace

View File

@ -7,6 +7,7 @@ class Snippet < ActiveRecord::Base
include Sortable include Sortable
include Awardable include Awardable
include Mentionable include Mentionable
include Spammable
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :content cache_markdown_field :content
@ -17,7 +18,7 @@ class Snippet < ActiveRecord::Base
default_content_html_invalidator || file_name_changed? default_content_html_invalidator || file_name_changed?
end end
default_value_for :visibility_level, Snippet::PRIVATE default_value_for(:visibility_level) { current_application_settings.default_snippet_visibility }
belongs_to :author, class_name: 'User' belongs_to :author, class_name: 'User'
belongs_to :project belongs_to :project
@ -46,6 +47,9 @@ class Snippet < ActiveRecord::Base
participant :author participant :author
participant :notes_with_associations participant :notes_with_associations
attr_spammable :title, spam_title: true
attr_spammable :content, spam_description: true
def self.reference_prefix def self.reference_prefix
'$' '$'
end end
@ -127,6 +131,14 @@ class Snippet < ActiveRecord::Base
notes.includes(:author) notes.includes(:author)
end end
def check_for_spam?
public?
end
def spammable_entity_type
'snippet'
end
class << self class << self
# Searches for snippets with a matching title or file name. # Searches for snippets with a matching title or file name.
# #

View File

@ -61,7 +61,7 @@ module Auth
end end
def process_repository_access(type, name, actions) def process_repository_access(type, name, actions)
requested_project = Project.find_with_namespace(name) requested_project = Project.find_by_full_path(name)
return unless requested_project return unless requested_project
actions = actions.select do |action| actions = actions.select do |action|

View File

@ -4,7 +4,8 @@ module Commits
class ChangeError < StandardError; end class ChangeError < StandardError; end
def execute def execute
@source_project = params[:source_project] || @project @start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@commit = params[:commit] @commit = params[:commit]
@create_merge_request = params[:create_merge_request].present? @create_merge_request = params[:create_merge_request].present?
@ -25,13 +26,28 @@ module Commits
def commit_change(action) def commit_change(action)
raise NotImplementedError unless repository.respond_to?(action) raise NotImplementedError unless repository.respond_to?(action)
into = @create_merge_request ? @commit.public_send("#{action}_branch_name") : @target_branch if @create_merge_request
tree_id = repository.public_send("check_#{action}_content", @commit, @target_branch) into = @commit.public_send("#{action}_branch_name")
tree_branch = @start_branch
else
into = tree_branch = @target_branch
end
tree_id = repository.public_send(
"check_#{action}_content", @commit, tree_branch)
if tree_id if tree_id
create_target_branch(into) if @create_merge_request validate_target_branch(into) if @create_merge_request
repository.public_send(
action,
current_user,
@commit,
into,
tree_id,
start_project: @start_project,
start_branch_name: @start_branch)
repository.public_send(action, current_user, @commit, into, tree_id)
success success
else else
error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically. error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
@ -50,12 +66,12 @@ module Commits
true true
end end
def create_target_branch(new_branch) def validate_target_branch(new_branch)
# Temporary branch exists and contains the change commit # Temporary branch exists and contains the change commit
return success if repository.find_branch(new_branch) return if repository.find_branch(new_branch)
result = CreateBranchService.new(@project, current_user) result = ValidateNewBranchService.new(@project, current_user)
.execute(new_branch, @target_branch, source_project: @source_project) .execute(new_branch)
if result[:status] == :error if result[:status] == :error
raise ChangeError, "There was an error creating the source branch: #{result[:message]}" raise ChangeError, "There was an error creating the source branch: #{result[:message]}"

View File

@ -3,23 +3,27 @@ require 'securerandom'
# Compare 2 branches for one repo or between repositories # Compare 2 branches for one repo or between repositories
# and return Gitlab::Git::Compare object that responds to commits and diffs # and return Gitlab::Git::Compare object that responds to commits and diffs
class CompareService class CompareService
def execute(source_project, source_branch, target_project, target_branch, straight: false) attr_reader :start_project, :start_branch_name
source_commit = source_project.commit(source_branch)
return unless source_commit
source_sha = source_commit.sha def initialize(new_start_project, new_start_branch_name)
@start_project = new_start_project
@start_branch_name = new_start_branch_name
end
def execute(target_project, target_branch, straight: false)
# If compare with other project we need to fetch ref first # If compare with other project we need to fetch ref first
unless target_project == source_project target_project.repository.with_repo_branch_commit(
random_string = SecureRandom.hex start_project.repository,
start_branch_name) do |commit|
break unless commit
target_project.repository.fetch_ref( compare(commit.sha, target_project, target_branch, straight)
source_project.repository.path_to_repo,
"refs/heads/#{source_branch}",
"refs/tmp/#{random_string}/head"
)
end end
end
private
def compare(source_sha, target_project, target_branch, straight)
raw_compare = Gitlab::Git::Compare.new( raw_compare = Gitlab::Git::Compare.new(
target_project.repository.raw_repository, target_project.repository.raw_repository,
target_branch, target_branch,

View File

@ -1,31 +1,11 @@
class CreateBranchService < BaseService class CreateBranchService < BaseService
def execute(branch_name, ref, source_project: @project) def execute(branch_name, ref)
valid_branch = Gitlab::GitRefValidator.validate(branch_name) result = ValidateNewBranchService.new(project, current_user)
.execute(branch_name)
unless valid_branch return result if result[:status] == :error
return error('Branch name is invalid')
end
repository = project.repository new_branch = repository.add_branch(current_user, branch_name, ref)
existing_branch = repository.find_branch(branch_name)
if existing_branch
return error('Branch already exists')
end
new_branch = if source_project != @project
repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{ref}",
"refs/heads/#{branch_name}"
)
repository.after_create_branch
repository.find_branch(branch_name)
else
repository.add_branch(current_user, branch_name, ref)
end
if new_branch if new_branch
success(new_branch) success(new_branch)

View File

@ -1,5 +1,8 @@
class CreateSnippetService < BaseService class CreateSnippetService < BaseService
def execute def execute
request = params.delete(:request)
api = params.delete(:api)
snippet = if project snippet = if project
project.snippets.build(params) project.snippets.build(params)
else else
@ -12,8 +15,12 @@ class CreateSnippetService < BaseService
end end
snippet.author = current_user snippet.author = current_user
snippet.spam = SpamService.new(snippet, request).check(api)
if snippet.save
UserAgentDetailService.new(snippet, request).create
end
snippet.save
snippet snippet
end end
end end

View File

@ -7,7 +7,7 @@ class DeleteTagService < BaseService
return error('No such tag', 404) return error('No such tag', 404)
end end
if repository.rm_tag(tag_name) if repository.rm_tag(current_user, tag_name)
release = project.releases.find_by(tag: tag_name) release = project.releases.find_by(tag: tag_name)
release.destroy if release release.destroy if release

View File

@ -3,9 +3,9 @@ module Files
class ValidationError < StandardError; end class ValidationError < StandardError; end
def execute def execute
@source_project = params[:source_project] || @project @start_project = params[:start_project] || @project
@source_branch = params[:source_branch] @start_branch = params[:start_branch]
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@commit_message = params[:commit_message] @commit_message = params[:commit_message]
@file_path = params[:file_path] @file_path = params[:file_path]
@ -22,10 +22,8 @@ module Files
# Validate parameters # Validate parameters
validate validate
# Create new branch if it different from source_branch # Create new branch if it different from start_branch
if different_branch? validate_target_branch if different_branch?
create_target_branch
end
result = commit result = commit
if result if result
@ -40,7 +38,7 @@ module Files
private private
def different_branch? def different_branch?
@source_branch != @target_branch || @source_project != @project @start_branch != @target_branch || @start_project != @project
end end
def file_has_changed? def file_has_changed?
@ -61,22 +59,23 @@ module Files
end end
unless project.empty_repo? unless project.empty_repo?
unless @source_project.repository.branch_names.include?(@source_branch) unless @start_project.repository.branch_exists?(@start_branch)
raise_error('You can only create or edit files when you are on a branch') raise_error('You can only create or edit files when you are on a branch')
end end
if different_branch? if different_branch?
if repository.branch_names.include?(@target_branch) if repository.branch_exists?(@target_branch)
raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes') raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes')
end end
end end
end end
end end
def create_target_branch def validate_target_branch
result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project) result = ValidateNewBranchService.new(project, current_user).
execute(@target_branch)
unless result[:status] == :success if result[:status] == :error
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}") raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
end end
end end

View File

@ -1,7 +1,15 @@
module Files module Files
class CreateDirService < Files::BaseService class CreateDirService < Files::BaseService
def commit def commit
repository.commit_dir(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name) repository.commit_dir(
current_user,
@file_path,
message: @commit_message,
branch_name: @target_branch,
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch)
end end
def validate def validate

View File

@ -1,7 +1,17 @@
module Files module Files
class CreateService < Files::BaseService class CreateService < Files::BaseService
def commit def commit
repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, false, author_email: @author_email, author_name: @author_name) repository.commit_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,
start_branch_name: @start_branch)
end end
def validate def validate
@ -24,7 +34,7 @@ module Files
unless project.empty_repo? unless project.empty_repo?
@file_path.slice!(0) if @file_path.start_with?('/') @file_path.slice!(0) if @file_path.start_with?('/')
blob = repository.blob_at_branch(@source_branch, @file_path) blob = repository.blob_at_branch(@start_branch, @file_path)
if blob if blob
raise_error('Your changes could not be committed because a file with the same name already exists') raise_error('Your changes could not be committed because a file with the same name already exists')

View File

@ -1,7 +1,15 @@
module Files module Files
class DeleteService < Files::BaseService class DeleteService < Files::BaseService
def commit def commit
repository.remove_file(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name) repository.remove_file(
current_user,
@file_path,
message: @commit_message,
branch_name: @target_branch,
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch)
end end
end end
end end

View File

@ -5,11 +5,13 @@ module Files
def commit def commit
repository.multi_action( repository.multi_action(
user: current_user, user: current_user,
branch: @target_branch,
message: @commit_message, message: @commit_message,
branch_name: @target_branch,
actions: params[:actions], actions: params[:actions],
author_email: @author_email, author_email: @author_email,
author_name: @author_name author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch
) )
end end
@ -61,7 +63,7 @@ module Files
end end
def last_commit def last_commit
Gitlab::Git::Commit.last_for_path(repository, @source_branch, @file_path) Gitlab::Git::Commit.last_for_path(repository, @start_branch, @file_path)
end end
def regex_check(file) def regex_check(file)

View File

@ -4,11 +4,13 @@ module Files
def commit def commit
repository.update_file(current_user, @file_path, @file_content, repository.update_file(current_user, @file_path, @file_content,
branch: @target_branch,
previous_path: @previous_path,
message: @commit_message, message: @commit_message,
branch_name: @target_branch,
previous_path: @previous_path,
author_email: @author_email, author_email: @author_email,
author_name: @author_name) author_name: @author_name,
start_project: @start_project,
start_branch_name: @start_branch)
end end
private private
@ -23,7 +25,7 @@ module Files
def last_commit def last_commit
@last_commit ||= Gitlab::Git::Commit. @last_commit ||= Gitlab::Git::Commit.
last_for_path(@source_project.repository, @source_branch, @file_path) last_for_path(@start_project.repository, @start_branch, @file_path)
end end
end end
end end

View File

@ -18,9 +18,9 @@ class GitHooksService
end end
end end
yield self yield(self).tap do
run_hook('post-receive')
run_hook('post-receive') end
end end
private private

View File

@ -0,0 +1,179 @@
class GitOperationService
attr_reader :user, :repository
def initialize(new_user, new_repository)
@user = new_user
@repository = new_repository
end
def add_branch(branch_name, newrev)
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
oldrev = Gitlab::Git::BLANK_SHA
update_ref_in_hooks(ref, newrev, oldrev)
end
def rm_branch(branch)
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch.name
oldrev = branch.target
newrev = Gitlab::Git::BLANK_SHA
update_ref_in_hooks(ref, newrev, oldrev)
end
def add_tag(tag_name, newrev, options = {})
ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
oldrev = Gitlab::Git::BLANK_SHA
with_hooks(ref, newrev, oldrev) do |service|
# We want to pass the OID of the tag object to the hooks. For an
# annotated tag we don't know that OID until after the tag object
# (raw_tag) is created in the repository. That is why we have to
# update the value after creating the tag object. Only the
# "post-receive" hook will receive the correct value in this case.
raw_tag = repository.rugged.tags.create(tag_name, newrev, options)
service.newrev = raw_tag.target_id
end
end
def rm_tag(tag)
ref = Gitlab::Git::TAG_REF_PREFIX + tag.name
oldrev = tag.target
newrev = Gitlab::Git::BLANK_SHA
update_ref_in_hooks(ref, newrev, oldrev) do
repository.rugged.tags.delete(tag_name)
end
end
# Whenever `start_branch_name` is passed, if `branch_name` doesn't exist,
# it would be created from `start_branch_name`.
# If `start_project` is passed, and the branch doesn't exist,
# it would try to find the commits from it instead of current repository.
def with_branch(
branch_name,
start_branch_name: nil,
start_project: repository.project,
&block)
check_with_branch_arguments!(
branch_name, start_branch_name, start_project)
update_branch_with_hooks(branch_name) do
repository.with_repo_branch_commit(
start_project.repository,
start_branch_name || branch_name,
&block)
end
end
private
def update_branch_with_hooks(branch_name)
update_autocrlf_option
was_empty = repository.empty?
# Make commit
newrev = yield
unless newrev
raise Repository::CommitError.new('Failed to create commit')
end
branch = repository.find_branch(branch_name)
oldrev = find_oldrev_from_branch(newrev, branch)
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
update_ref_in_hooks(ref, newrev, oldrev)
# If repo was empty expire cache
repository.after_create if was_empty
repository.after_create_branch if
was_empty || Gitlab::Git.blank_ref?(oldrev)
newrev
end
def find_oldrev_from_branch(newrev, branch)
return Gitlab::Git::BLANK_SHA unless branch
oldrev = branch.target
if oldrev == repository.rugged.merge_base(newrev, branch.target)
oldrev
else
raise Repository::CommitError.new('Branch diverged')
end
end
def update_ref_in_hooks(ref, newrev, oldrev)
with_hooks(ref, newrev, oldrev) do
update_ref(ref, newrev, oldrev)
end
end
def with_hooks(ref, newrev, oldrev)
GitHooksService.new.execute(
user,
repository.path_to_repo,
oldrev,
newrev,
ref) do |service|
yield(service)
end
end
def update_ref(ref, newrev, oldrev)
# We use 'git update-ref' because libgit2/rugged currently does not
# offer 'compare and swap' ref updates. Without compare-and-swap we can
# (and have!) accidentally reset the ref to an earlier state, clobbering
# commits. See also https://github.com/libgit2/libgit2/issues/1534.
command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z]
_, status = Gitlab::Popen.popen(
command,
repository.path_to_repo) do |stdin|
stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00")
end
unless status.zero?
raise Repository::CommitError.new(
"Could not update branch #{Gitlab::Git.branch_name(ref)}." \
" Please refresh and try again.")
end
end
def update_autocrlf_option
if repository.raw_repository.autocrlf != :input
repository.raw_repository.autocrlf = :input
end
end
def check_with_branch_arguments!(
branch_name, start_branch_name, start_project)
return if repository.branch_exists?(branch_name)
if repository.project != start_project
unless start_branch_name
raise ArgumentError,
'Should also pass :start_branch_name if' +
' :start_project is different from current project'
end
unless start_project.repository.branch_exists?(start_branch_name)
raise ArgumentError,
"Cannot find branch #{branch_name} nor" \
" #{start_branch_name} from" \
" #{start_project.path_with_namespace}"
end
elsif start_branch_name
unless repository.branch_exists?(start_branch_name)
raise ArgumentError,
"Cannot find branch #{branch_name} nor" \
" #{start_branch_name} from" \
" #{repository.project.path_with_namespace}"
end
end
end
end

View File

@ -47,9 +47,10 @@ module MergeRequests
end end
def compare_branches def compare_branches
compare = CompareService.new.execute( compare = CompareService.new(
source_project, source_project,
source_branch, source_branch
).execute(
target_project, target_project,
target_branch target_branch
) )

View File

@ -6,13 +6,17 @@ module MergeRequests
# Executed when you do merge via GitLab UI # Executed when you do merge via GitLab UI
# #
class MergeService < MergeRequests::BaseService class MergeService < MergeRequests::BaseService
attr_reader :merge_request attr_reader :merge_request, :source
def execute(merge_request) def execute(merge_request)
@merge_request = merge_request @merge_request = merge_request
return log_merge_error('Merge request is not mergeable', true) unless @merge_request.mergeable? return log_merge_error('Merge request is not mergeable', true) unless @merge_request.mergeable?
@source = find_merge_source
return log_merge_error('No source for merge', true) unless @source
merge_request.in_locked_state do merge_request.in_locked_state do
if commit if commit
after_merge after_merge
@ -34,7 +38,7 @@ module MergeRequests
committer: committer committer: committer
} }
commit_id = repository.merge(current_user, merge_request, options) commit_id = repository.merge(current_user, source, merge_request, options)
if commit_id if commit_id
merge_request.update(merge_commit_sha: commit_id) merge_request.update(merge_commit_sha: commit_id)
@ -73,9 +77,11 @@ module MergeRequests
end end
def merge_request_info def merge_request_info
project = merge_request.project merge_request.to_reference(full: true)
end
"#{project.to_reference}#{merge_request.to_reference}" def find_merge_source
merge_request.diff_head_sha
end end
end end
end end

View File

@ -0,0 +1,22 @@
require_relative 'base_service'
class ValidateNewBranchService < BaseService
def execute(branch_name)
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
unless valid_branch
return error('Branch name is invalid')
end
repository = project.repository
existing_branch = repository.find_branch(branch_name)
if existing_branch
return error('Branch already exists')
end
success
rescue GitHooksService::PreReceiveError => ex
error(ex.message)
end
end

View File

@ -212,7 +212,7 @@
.col-sm-10 .col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control' = f.number_field :max_artifacts_size, class: 'form-control'
.help-block .help-block
Set the maximum file size each build's artifacts can have Set the maximum file size each jobs's artifacts can have
= link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size") = link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size")
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled

View File

@ -12,7 +12,7 @@
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
.row-content-block.second-block .row-content-block.second-block
#{(@scope || 'all').capitalize} builds #{(@scope || 'all').capitalize} jobs
%ul.content-list.builds-content-list.admin-builds-table %ul.content-list.builds-content-list.admin-builds-table
= render "projects/builds/table", builds: @builds, admin: true = render "projects/builds/table", builds: @builds, admin: true

View File

@ -20,9 +20,9 @@
%span %span
Groups Groups
= nav_link path: 'builds#index' do = nav_link path: 'builds#index' do
= link_to admin_builds_path, title: 'Builds' do = link_to admin_builds_path, title: 'Jobs' do
%span %span
Builds Jobs
= nav_link path: ['runners#index', 'runners#show'] do = nav_link path: ['runners#index', 'runners#show'] do
= link_to admin_runners_path, title: 'Runners' do = link_to admin_runners_path, title: 'Runners' do
%span %span

View File

@ -26,7 +26,7 @@
.bs-callout .bs-callout
%p %p
A 'Runner' is a process which runs a build. A 'Runner' is a process which runs a job.
You can setup as many Runners as you need. You can setup as many Runners as you need.
%br %br
Runners can be placed on separate users, servers, even on your local machine. Runners can be placed on separate users, servers, even on your local machine.
@ -37,16 +37,16 @@
%ul %ul
%li %li
%span.label.label-success shared %span.label.label-success shared
\- Runner runs builds from all unassigned projects \- Runner runs jobs from all unassigned projects
%li %li
%span.label.label-info specific %span.label.label-info specific
\- Runner runs builds from assigned projects \- Runner runs jobs from assigned projects
%li %li
%span.label.label-warning locked %span.label.label-warning locked
\- Runner cannot be assigned to other projects \- Runner cannot be assigned to other projects
%li %li
%span.label.label-danger paused %span.label.label-danger paused
\- Runner will not receive any new builds \- Runner will not receive any new jobs
.append-bottom-20.clearfix .append-bottom-20.clearfix
.pull-left .pull-left
@ -68,7 +68,7 @@
%th Runner token %th Runner token
%th Description %th Description
%th Projects %th Projects
%th Builds %th Jobs
%th Tags %th Tags
%th Last contact %th Last contact
%th %th

View File

@ -11,13 +11,13 @@
- if @runner.shared? - if @runner.shared?
.bs-callout.bs-callout-success .bs-callout.bs-callout-success
%h4 This Runner will process builds from ALL UNASSIGNED projects %h4 This Runner will process jobs from ALL UNASSIGNED projects
%p %p
If you want Runners to build only specific projects, enable them in the table below. If you want Runners to build only specific projects, enable them in the table below.
Keep in mind that this is a one way transition. Keep in mind that this is a one way transition.
- else - else
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%h4 This Runner will process builds only from ASSIGNED projects %h4 This Runner will process jobs only from ASSIGNED projects
%p You can't make this a shared Runner. %p You can't make this a shared Runner.
%hr %hr
@ -70,11 +70,11 @@
= paginate @projects, theme: "gitlab" = paginate @projects, theme: "gitlab"
.col-md-6 .col-md-6
%h4 Recent builds served by this Runner %h4 Recent jobs served by this Runner
%table.table.ci-table.runner-builds %table.table.ci-table.runner-builds
%thead %thead
%tr %tr
%th Build %th Job
%th Status %th Status
%th Project %th Project
%th Commit %th Commit

View File

@ -3,7 +3,7 @@
.col-md-4.col-lg-6 .col-md-4.col-lg-6
= users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true) = users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true)
.help-block.append-bottom-10 .help-block.append-bottom-10
Search for users by name, username, or email, or invite new ones using their email address. Search for members by name, username, or email, or invite new ones using their email address.
.col-md-3.col-lg-2 .col-md-3.col-lg-2
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select" = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select"
@ -16,7 +16,7 @@
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input %i.clear-icon.js-clear-input
.help-block.append-bottom-10 .help-block.append-bottom-10
On this date, the user(s) will automatically lose access to this group and all of its projects. On this date, the member(s) will automatically lose access to this group and all of its projects.
.col-md-2 .col-md-2
= f.submit 'Add to group', class: "btn btn-create btn-block" = f.submit 'Add to group', class: "btn btn-create btn-block"

View File

@ -7,7 +7,7 @@
- if can?(current_user, :admin_group_member, @group) - if can?(current_user, :admin_group_member, @group)
.project-members-new.append-bottom-default .project-members-new.append-bottom-default
%p.clearfix %p.clearfix
Add new user to Add new member to
%strong= @group.name %strong= @group.name
= render "new_group_member" = render "new_group_member"
@ -15,7 +15,7 @@
.append-bottom-default.clearfix .append-bottom-default.clearfix
%h5.member.existing-title %h5.member.existing-title
Existing users Existing members
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group .form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
@ -24,7 +24,7 @@
= render 'shared/members/sort_dropdown' = render 'shared/members/sort_dropdown'
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Users with access to Members with access to
%strong= @group.name %strong= @group.name
%span.badge= @members.total_count %span.badge= @members.total_count
%ul.content-list %ul.content-list

View File

@ -143,7 +143,7 @@
.key g .key g
.key b .key b
%td %td
Go to builds Go to jobs
%tr %tr
%td.shortcut %td.shortcut
.key g .key g

View File

@ -96,8 +96,8 @@
-# Shortcut to builds page -# Shortcut to builds page
- if project_nav_tab? :builds - if project_nav_tab? :builds
%li.hidden %li.hidden
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do = link_to project_builds_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
Builds Jobs
-# Shortcut to commits page -# Shortcut to commits page
- if project_nav_tab? :commits - if project_nav_tab? :commits

View File

@ -1,6 +1,6 @@
- content_for :header do - content_for :header do
%h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" } %h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (build failed) GitLab (job failed)
%h3 %h3
Project: Project:
@ -21,4 +21,4 @@
Message: #{@build.pipeline.git_commit_message} Message: #{@build.pipeline.git_commit_message}
%p %p
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}

View File

@ -1,4 +1,4 @@
Build failed for <%= @project.name %> Job failed for <%= @project.name %>
Status: <%= @build.status %> Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %> Commit: <%= @build.pipeline.short_sha %>

View File

@ -1,6 +1,6 @@
- content_for :header do - content_for :header do
%h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" } %h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (build successful) GitLab (job successful)
%h3 %h3
Project: Project:
@ -21,4 +21,4 @@
Message: #{@build.pipeline.git_commit_message} Message: #{@build.pipeline.git_commit_message}
%p %p
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}

View File

@ -1,4 +1,4 @@
Build successful for <%= @project.name %> Job successful for <%= @project.name %>
Status: <%= @build.status %> Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %> Commit: <%= @build.pipeline.short_sha %>

View File

@ -1 +1 @@
Build #<%= build.id %> ( <%= pipeline_build_url(pipeline, build) %> ) Job #<%= build.id %> ( <%= pipeline_build_url(pipeline, build) %> )

View File

@ -1 +1 @@
Build #<%= build.id %> Job #<%= build.id %>

View File

@ -3,6 +3,6 @@
%h4 %h4
Customize your workflow! Customize your workflow!
%p %p
Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and builds, GitLab can help manage your workflow from idea to production! Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and pipelines, GitLab can help manage your workflow from idea to production!
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
= link_to "Get started", edit_project_path(@project), class: "btn btn-success" = link_to "Get started", edit_project_path(@project), class: "btn btn-success"

View File

@ -4,10 +4,10 @@
.checkbox.builds-feature .checkbox.builds-feature
= form.label :only_allow_merge_if_build_succeeds do = form.label :only_allow_merge_if_build_succeeds do
= form.check_box :only_allow_merge_if_build_succeeds = form.check_box :only_allow_merge_if_build_succeeds
%strong Only allow merge requests to be merged if the build succeeds %strong Only allow merge requests to be merged if the pipeline succeeds
%br %br
%span.descr %span.descr
Builds need to be configured to enable this feature. Pipelines need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds') = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')
.checkbox .checkbox
= form.label :only_allow_merge_if_all_discussions_are_resolved do = form.label :only_allow_merge_if_all_discussions_are_resolved do

View File

@ -1,4 +1,4 @@
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds' - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs'
.top-block.row-content-block.clearfix .top-block.row-content-block.clearfix
.pull-right .pull-right

View File

@ -1,7 +1,7 @@
.content-block.build-header .content-block.build-header
.header-content .header-content
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false = render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
Build Job
%strong.js-build-id ##{@build.id} %strong.js-build-id ##{@build.id}
in pipeline in pipeline
= link_to pipeline_path(@build.pipeline) do = link_to pipeline_path(@build.pipeline) do
@ -17,6 +17,6 @@
= render "user" = render "user"
= time_ago_with_tooltip(@build.created_at) = time_ago_with_tooltip(@build.created_at)
- if can?(current_user, :update_build, @build) && @build.retryable? - if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post = link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" } %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
= icon('angle-double-left') = icon('angle-double-left')

View File

@ -2,7 +2,7 @@
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar %aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Build Job
%strong ##{@build.id} %strong ##{@build.id}
%a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" } %a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
= icon('angle-double-right') = icon('angle-double-right')
@ -17,7 +17,7 @@
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?) - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block{ class: ("block-first" if !@build.coverage) } .block{ class: ("block-first" if !@build.coverage) }
.title .title
Build artifacts Job artifacts
- if @build.artifacts_expired? - if @build.artifacts_expired?
%p.build-detail-row %p.build-detail-row
The artifacts were removed The artifacts were removed
@ -42,9 +42,9 @@
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) } .block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
.title .title
Build details Job details
- if can?(current_user, :update_build, @build) && @build.retryable? - if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post = link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
- if @build.merge_request - if @build.merge_request
%p.build-detail-row %p.build-detail-row
%span.build-light-text Merge Request: %span.build-light-text Merge Request:
@ -136,4 +136,4 @@
- else - else
= build.id = build.id
- if build.retried? - if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Build was retried' } %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }

View File

@ -2,14 +2,14 @@
- if builds.blank? - if builds.blank?
%div %div
.nothing-here-block No builds to show .nothing-here-block No jobs to show
- else - else
.table-holder .table-holder
%table.table.ci-table.builds-page %table.table.ci-table.builds-page
%thead %thead
%tr %tr
%th Status %th Status
%th Build %th Job
%th Pipeline %th Pipeline
- if admin - if admin
%th Project %th Project

View File

@ -1,5 +1,5 @@
- @no_container = true - @no_container = true
- page_title "Builds" - page_title "Jobs"
= render "projects/pipelines/head" = render "projects/pipelines/head"
%div{ class: container_class } %div{ class: container_class }
@ -14,7 +14,7 @@
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
- unless @repository.gitlab_ci_yml - unless @repository.gitlab_ci_yml
= link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info' = link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
= link_to ci_lint_path, class: 'btn btn-default' do = link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint %span CI Lint

View File

@ -1,5 +1,5 @@
- @no_container = true - @no_container = true
- page_title "#{@build.name} (##{@build.id})", "Builds" - page_title "#{@build.name} (##{@build.id})", "Jobs"
- trace_with_state = @build.trace_with_state - trace_with_state = @build.trace_with_state
= render "projects/pipelines/head", build_subnav: true = render "projects/pipelines/head", build_subnav: true
@ -12,14 +12,14 @@
.bs-callout.bs-callout-warning .bs-callout.bs-callout-warning
%p %p
- if no_runners_for_project?(@build.project) - if no_runners_for_project?(@build.project)
This build is stuck, because the project doesn't have any runners online assigned to it. This job is stuck, because the project doesn't have any runners online assigned to it.
- elsif @build.tags.any? - elsif @build.tags.any?
This build is stuck, because you don't have any active runners online with any of these tags assigned to them: This job is stuck, because you don't have any active runners online with any of these tags assigned to them:
- @build.tags.each do |tag| - @build.tags.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
- else - else
This build is stuck, because you don't have any active runners that can run this build. This job is stuck, because you don't have any active runners that can run this job.
%br %br
Go to Go to
@ -37,14 +37,14 @@
- environment = environment_for_build(@build.project, @build) - environment = environment_for_build(@build.project, @build)
- if @build.success? && @build.last_deployment.present? - if @build.success? && @build.last_deployment.present?
- if @build.last_deployment.last? - if @build.last_deployment.last?
This build is the most recent deployment to #{environment_link_for_build(@build.project, @build)}. This job is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
- else - else
This build is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}. This job is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
View the most recent deployment #{deployment_link(environment.last_deployment)}. View the most recent deployment #{deployment_link(environment.last_deployment)}.
- elsif @build.complete? && !@build.success? - elsif @build.complete? && !@build.success?
The deployment of this build to #{environment_link_for_build(@build.project, @build)} did not succeed. The deployment of this job to #{environment_link_for_build(@build.project, @build)} did not succeed.
- else - else
This build is creating a deployment to #{environment_link_for_build(@build.project, @build)} This job is creating a deployment to #{environment_link_for_build(@build.project, @build)}
- if environment.try(:last_deployment) - if environment.try(:last_deployment)
and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')} and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')}
@ -52,9 +52,9 @@
- if @build.erased? - if @build.erased?
.erased.alert.alert-warning .erased.alert.alert-warning
- if @build.erased_by_user? - if @build.erased_by_user?
Build has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)} Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)}
- else - else
Build has been erased #{time_ago_with_tooltip(@build.erased_at)} Job has been erased #{time_ago_with_tooltip(@build.erased_at)}
- else - else
#js-build-scroll.scroll-controls #js-build-scroll.scroll-controls
.scroll-step .scroll-step

View File

@ -32,10 +32,10 @@
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace" = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
- if build.stuck? - if build.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.') = icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- if retried - if retried
= icon('refresh', class: 'text-warning has-tooltip', title: 'Build was retried') = icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried')
.label-container .label-container
- if build.tags.any? - if build.tags.any?

View File

@ -15,7 +15,7 @@
- else - else
%span.api.monospace API %span.api.monospace API
- if pipeline.latest? - if pipeline.latest?
%span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest %span.label.label-success.has-tooltip{ title: 'Latest job for this branch' } latest
- if pipeline.triggered? - if pipeline.triggered?
%span.label.label-primary triggered %span.label.label-primary triggered
- if pipeline.yaml_errors.present? - if pipeline.yaml_errors.present?
@ -78,7 +78,7 @@
.btn-group.inline .btn-group.inline
- if actions.any? - if actions.any?
.btn-group .btn-group
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual build', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual build' } %button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual job', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual job' }
= custom_icon('icon_play') = custom_icon('icon_play')
= icon('caret-down', 'aria-hidden' => 'true') = icon('caret-down', 'aria-hidden' => 'true')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right

View File

@ -13,7 +13,7 @@
Pipeline Pipeline
= link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace" = link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace"
with with
= pluralize pipeline.statuses.count(:id), "build" = pluralize pipeline.statuses.count(:id), "job"
- if pipeline.ref - if pipeline.ref
for for
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace" = link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace"
@ -44,7 +44,7 @@
%thead %thead
%tr %tr
%th Status %th Status
%th Build ID %th Job ID
%th Name %th Name
%th %th
- if pipeline.project.build_coverage_enabled? - if pipeline.project.build_coverage_enabled?

View File

@ -5,7 +5,7 @@
- unless diff_file.submodule? - unless diff_file.submodule?
.file-actions.hidden-xs .file-actions.hidden-xs
- if blob_text_viewable?(blob) - if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file", disabled: @diff_notes_disabled do = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
= icon('comment') = icon('comment')
\ \
- if editable_diff?(diff_file) - if editable_diff?(diff_file)

View File

@ -63,7 +63,7 @@
.row .row
.col-md-9.project-feature.nested .col-md-9.project-feature.nested
= feature_fields.label :builds_access_level, "Builds", class: 'label-light' = feature_fields.label :builds_access_level, "Pipelines", class: 'label-light'
%span.help-block Submit, test and deploy your changes before merge %span.help-block Submit, test and deploy your changes before merge
.col-md-3 .col-md-3
= project_feature_access_select(:builds_access_level) = project_feature_access_select(:builds_access_level)
@ -181,13 +181,13 @@
%p %p
The following items will NOT be exported: The following items will NOT be exported:
%ul %ul
%li Build traces and artifacts %li Job traces and artifacts
%li LFS objects %li LFS objects
%li Container registry images %li Container registry images
%li CI variables %li CI variables
%li Any encrypted tokens %li Any encrypted tokens
%hr
- if can? current_user, :archive_project, @project - if can? current_user, :archive_project, @project
%hr
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
%h4.warning-title.prepend-top-0 %h4.warning-title.prepend-top-0

View File

@ -32,8 +32,8 @@
%tr %tr
%th ID %th ID
%th Commit %th Commit
%th Build %th Job
%th %th Created
%th.hidden-xs %th.hidden-xs
= render @deployments = render @deployments

View File

@ -1,4 +1,4 @@
%h4 Build charts %h4 Pipelines charts
%p %p
&nbsp; &nbsp;
%span.cgreen %span.cgreen
@ -11,19 +11,19 @@
.prepend-top-default .prepend-top-default
%p.light %p.light
Builds for last week Jobs for last week
(#{date_from_to(Date.today - 7.days, Date.today)}) (#{date_from_to(Date.today - 7.days, Date.today)})
%canvas#weekChart{ height: 200 } %canvas#weekChart{ height: 200 }
.prepend-top-default .prepend-top-default
%p.light %p.light
Builds for last month Jobs for last month
(#{date_from_to(Date.today - 30.days, Date.today)}) (#{date_from_to(Date.today - 30.days, Date.today)})
%canvas#monthChart{ height: 200 } %canvas#monthChart{ height: 200 }
.prepend-top-default .prepend-top-default
%p.light %p.light
Builds for last year Jobs for last year
%canvas#yearChart.padded{ height: 250 } %canvas#yearChart.padded{ height: 250 }
- [:week, :month, :year].each do |scope| - [:week, :month, :year].each do |scope|

View File

@ -8,5 +8,5 @@
'@click' => "onClickResolveModeButton(file, 'edit')", '@click' => "onClickResolveModeButton(file, 'edit')",
type: 'button' } type: 'button' }
Edit inline Edit inline
%a.btn.view-file.btn-file-option{ ":href" => "file.blobPath" } %a.btn.view-file{ ":href" => "file.blobPath" }
View file @{{conflictsData.shortCommitSha}} View file @{{conflictsData.shortCommitSha}}

View File

@ -10,7 +10,7 @@
= ci_label_for_status(status) = ci_label_for_status(status)
for for
= succeed "." do = succeed "." do
= link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace" = link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace js-commit-link"
%span.ci-coverage %span.ci-coverage
- elsif @merge_request.has_ci? - elsif @merge_request.has_ci?
@ -21,7 +21,7 @@
.ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" } .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" }
= ci_icon_for_status(status) = ci_icon_for_status(status)
%span %span
CI build CI job
= ci_label_for_status(status) = ci_label_for_status(status)
for for
- commit = @merge_request.diff_head_commit - commit = @merge_request.diff_head_commit

View File

@ -16,14 +16,18 @@
gitlab_icon: "#{asset_path 'gitlab_logo.png'}", gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}", ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: { ci_message: {
normal: "Build {{status}} for \"{{title}}\"", normal: "Job {{status}} for \"{{title}}\"",
preparing: "{{status}} build for \"{{title}}\"" preparing: "{{status}} job for \"{{title}}\""
}, },
ci_enable: #{@project.ci_service ? "true" : "false"}, ci_enable: #{@project.ci_service ? "true" : "false"},
ci_title: { ci_title: {
preparing: "{{status}} build", preparing: "{{status}} job",
normal: "Build {{status}}" normal: "Job {{status}}"
}, },
ci_sha: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.short_sha : ''}",
ci_pipeline: #{@merge_request.head_pipeline.try(:id).to_json},
commits_path: "#{project_commits_path(@project)}",
pipeline_path: "#{project_pipelines_path(@project)}",
pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}" pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
}; };

View File

@ -35,10 +35,10 @@
The source branch will be removed. The source branch will be removed.
- elsif @merge_request.can_remove_source_branch?(current_user) - elsif @merge_request.can_remove_source_branch?(current_user)
.accept-control.checkbox .accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do = label_tag :should_remove_source_branch, class: "merge-param-checkbox" do
= check_box_tag :should_remove_source_branch = check_box_tag :should_remove_source_branch
Remove source branch Remove source branch
.accept-control.right .accept-control
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do = link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit') = icon('edit')
Modify commit message Modify commit message

View File

@ -1,6 +1,6 @@
%h4 %h4
= icon('exclamation-triangle') = icon('exclamation-triangle')
The build for this merge request failed The job for this merge request failed
%p %p
Please retry the build or push a new commit to fix the failure. Please retry the job or push a new commit to fix the failure.

Some files were not shown because too many files have changed in this diff Show More