Commit 4a195089 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2017-12-14' into 'master'

CE upstream - Thursday

Closes #4325, gitlab-ce#36054, #4277, #10, gitlab-ce#33926, gitlab-qa#102, gitaly#793, and gitlab-ce#39608

See merge request gitlab-org/gitlab-ee!3787
parents bd4b5181 a8d9352a
......@@ -2,7 +2,7 @@
/* global BoardService */
import Flash from '~/flash';
import PopupDialog from '~/vue_shared/components/popup_dialog.vue';
import modal from '../../vue_shared/components/modal.vue';
import { visitUrl } from '../../lib/utils/url_utility';
import BoardMilestoneSelect from './milestone_select.vue';
import BoardWeightSelect from './weight_select.vue';
......@@ -74,7 +74,7 @@ export default {
BoardLabelsSelect,
BoardMilestoneSelect,
BoardWeightSelect,
PopupDialog,
modal,
},
computed: {
isNewForm() {
......@@ -179,7 +179,7 @@ export default {
</script>
<template>
<popup-dialog
<modal
v-show="currentPage"
modal-dialog-class="board-config-modal"
:hide-footer="readonly"
......@@ -275,5 +275,5 @@ export default {
</div>
</form>
</template>
</popup-dialog>
</modal>
</template>
......@@ -410,7 +410,6 @@ import initGroupAnalytics from './init_group_analytics';
if ($('#tree-slider').length) new TreeView();
if ($('.blob-viewer').length) new BlobViewer();
if ($('.project-show-activity').length) new Activities();
$('#tree-slider').waitForImages(function() {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
......
<script>
import { s__ } from '../../locale';
import tooltip from '../../vue_shared/directives/tooltip';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import modal from '../../vue_shared/components/modal.vue';
import eventHub from '../event_hub';
import { COMMON_STR } from '../constants';
import Icon from '../../vue_shared/components/icon.vue';
......@@ -9,7 +9,7 @@ import Icon from '../../vue_shared/components/icon.vue';
export default {
components: {
Icon,
PopupDialog,
modal,
},
directives: {
tooltip,
......@@ -27,7 +27,7 @@ export default {
},
data() {
return {
dialogStatus: false,
modalStatus: false,
};
},
computed: {
......@@ -43,10 +43,10 @@ export default {
},
methods: {
onLeaveGroup() {
this.dialogStatus = true;
this.modalStatus = true;
},
leaveGroup(leaveConfirmed) {
this.dialogStatus = false;
this.modalStatus = false;
if (leaveConfirmed) {
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
}
......@@ -82,8 +82,8 @@ export default {
class="fa fa-sign-out"
aria-hidden="true"/>
</a>
<popup-dialog
v-show="dialogStatus"
<modal
v-show="modalStatus"
:primary-button-label="__('Leave')"
kind="warning"
:title="__('Are you sure?')"
......
......@@ -48,7 +48,7 @@ export default class Issue {
})
.fail(() => new Flash(issueFailMessage))
.done((data) => {
const isClosedBadge = $('div.status-box-closed');
const isClosedBadge = $('div.status-box-issue-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
......
......@@ -9,7 +9,7 @@ import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
import editedComponent from './edited.vue';
import formComponent from './form.vue';
import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
export default {
props: {
......@@ -152,7 +152,7 @@ export default {
},
mixins: [
RecaptchaDialogImplementor,
recaptchaModalImplementor,
],
methods: {
......@@ -197,7 +197,7 @@ export default {
});
},
closeRecaptchaDialog() {
closeRecaptchaModal() {
this.store.setFormState({
updateLoading: false,
});
......@@ -272,10 +272,10 @@ export default {
:enable-autocomplete="enableAutocomplete"
/>
<recaptcha-dialog
<recaptcha-modal
v-show="showRecaptcha"
:html="recaptchaHTML"
@close="closeRecaptchaDialog"
@close="closeRecaptchaModal"
/>
</div>
<div v-else>
......
<script>
import animateMixin from '../mixins/animate';
import TaskList from '../../task_list';
import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
export default {
mixins: [
animateMixin,
RecaptchaDialogImplementor,
recaptchaModalImplementor,
],
props: {
......@@ -126,7 +126,7 @@
>
</textarea>
<recaptcha-dialog
<recaptcha-modal
v-show="showRecaptcha"
:html="recaptchaHTML"
@close="closeRecaptcha"
......
......@@ -47,7 +47,7 @@
<textarea
id="issue-description"
class="note-textarea js-gfm-input js-autosize markdown-area"
data-supports-quick-actionss="false"
data-supports-quick-actions="false"
aria-label="Description"
v-model="formState.description"
ref="textarea"
......
......@@ -123,9 +123,7 @@
// we need to do this to prevent noteForm inconsistent content warning
// this is something we intentionally do so we need to recover the content
this.note.note = noteText;
if (this.$refs.noteBody) {
this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better
}
this.$refs.noteBody.$refs.noteForm.note = noteText;
},
},
created() {
......
<script>
export default {
props: {
helpPagePath: {
type: String,
required: true,
export default {
props: {
helpPagePath: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
},
emptyStateSvgPath: {
type: String,
required: true,
},
},
};
};
</script>
<template>
<div class="row empty-state js-empty-state">
<div class="col-xs-12">
<div class="svg-content">
<img :src="emptyStateSvgPath"/>
<div class="svg-content svg-250">
<img :src="emptyStateSvgPath" />
</div>
</div>
<div class="col-xs-12 text-center">
<div class="col-xs-12">
<div class="text-content">
<h4>Build with confidence</h4>
<h4 class="text-center">
{{ s__("Pipelines|Build with confidence") }}
</h4>
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
{{ s__("Pipelines|Continous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment.") }}
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
<div class="text-center">
<a
:href="helpPagePath"
class="btn btn-info"
>
{{ s__("Pipelines|Get started with Pipelines") }}
</a>
</div>
</div>
</div>
</div>
......
......@@ -59,8 +59,26 @@
},
computed: {
status() {
return this.job && this.job.status ? this.job.status : {};
},
tooltipText() {
return `${this.job.name} - ${this.job.status.label}`;
const textBuilder = [];
if (this.job.name) {
textBuilder.push(this.job.name);
}
if (this.job.name && this.status.label) {
textBuilder.push('-');
}
if (this.status.label) {
textBuilder.push(`${this.job.status.label}`);
}
return textBuilder.join(' ');
},
/**
......@@ -78,8 +96,8 @@
<div class="ci-job-component">
<a
v-tooltip
v-if="job.status.has_details"
:href="job.status.details_path"
v-if="status.has_details"
:href="status.details_path"
:title="tooltipText"
:class="cssClassJobName"
data-container="body"
......@@ -95,6 +113,7 @@
<div
v-else
v-tooltip
class="js-job-component-tooltip"
:title="tooltipText"
:class="cssClassJobName"
data-container="body"
......@@ -108,18 +127,18 @@
<action-component
v-if="hasAction && !isDropdown"
:tooltip-text="job.status.action.title"
:link="job.status.action.path"
:action-icon="job.status.action.icon"
:action-method="job.status.action.method"
:tooltip-text="status.action.title"
:link="status.action.path"
:action-icon="status.action.icon"
:action-method="status.action.method"
/>
<dropdown-action-component
v-if="hasAction && isDropdown"
:tooltip-text="job.status.action.title"
:link="job.status.action.path"
:action-icon="job.status.action.icon"
:action-method="job.status.action.method"
:tooltip-text="status.action.title"
:link="status.action.path"
:action-icon="status.action.icon"
:action-method="status.action.method"
/>
</div>
</template>
import Vue from 'vue';
import PipelinesStore from './stores/pipelines_store';
import pipelinesComponent from './components/pipelines.vue';
import Translate from '../vue_shared/translate';
Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipelines-list-vue',
......
<script>
import popupDialog from '../../../vue_shared/components/popup_dialog.vue';
import modal from '../../../vue_shared/components/modal.vue';
import { __, s__, sprintf } from '../../../locale';
import csrf from '../../../lib/utils/csrf';
......@@ -26,7 +26,7 @@
};
},
components: {
popupDialog,
modal,
},
computed: {
csrfToken() {
......@@ -89,7 +89,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
<template>
<div>
<popup-dialog
<modal
v-if="isOpen"
:title="s__('Profiles|Delete your account?')"
:text="text"
......@@ -134,7 +134,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</form>
</template>
</popup-dialog>
</modal>
<button
type="button"
......
......@@ -28,8 +28,7 @@ const highlighter = function(element, text, matches) {
};
export default class ProjectFindFile {
constructor (element1, options) {
constructor(element1, options) {
this.element = element1;
this.options = options;
this.goToBlob = this.goToBlob.bind(this);
......
<script>
import { mapActions } from 'vuex';
import { __ } from '../../../locale';
import popupDialog from '../../../vue_shared/components/popup_dialog.vue';
import modal from '../../../vue_shared/components/modal.vue';
export default {
props: {
......@@ -20,7 +20,7 @@
};
},
components: {
popupDialog,
modal,
},
methods: {
...mapActions([
......@@ -68,7 +68,7 @@
</script>
<template>
<popup-dialog
<modal
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
......@@ -94,5 +94,5 @@
</div>
</fieldset>
</form>
</popup-dialog>
</modal>
</template>
......@@ -2,12 +2,12 @@
import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import modal from '../../vue_shared/components/modal.vue';
import commitFilesList from './commit_sidebar/list.vue';
export default {
components: {
PopupDialog,
modal,
icon,
commitFilesList,
},
......@@ -16,7 +16,7 @@ export default {
},
data() {
return {
showNewBranchDialog: false,
showNewBranchModal: false,
submitCommitsLoading: false,
startNewMR: false,
commitMessage: '',
......@@ -58,7 +58,7 @@ export default {
start_branch: createNewBranch ? this.currentBranch : undefined,
};
this.showNewBranchDialog = false;
this.showNewBranchModal = false;
this.submitCommitsLoading = true;
this.commitChanges({ payload, newMr: this.startNewMR })
......@@ -76,7 +76,7 @@ export default {
this.checkCommitStatus()
.then((branchChanged) => {
if (branchChanged) {
this.showNewBranchDialog = true;
this.showNewBranchModal = true;
} else {
this.makeCommit();
}
......@@ -99,13 +99,13 @@ export default {
'is-collapsed': collapsed,
}"
>
<popup-dialog
v-if="showNewBranchDialog"
<modal
v-if="showNewBranchModal"
:primary-button-label="__('Create new branch')"
kind="primary"
:title="__('Branch has changed')"
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
@toggle="showNewBranchDialog = false"
@toggle="showNewBranchModal = false"
@submit="makeCommit(true)"
/>
<button
......
<script>
import { mapGetters, mapActions, mapState } from 'vuex';
import popupDialog from '../../vue_shared/components/popup_dialog.vue';
import modal from '../../vue_shared/components/modal.vue';
export default {
components: {
popupDialog,
modal,
},
computed: {
...mapState([
......@@ -43,7 +43,7 @@ export default {
{{buttonLabel}}
</span>
</button>
<popup-dialog
<modal
v-if="discardPopupOpen"
class="text-left"
:primary-button-label="__('Discard changes')"
......
......@@ -79,7 +79,7 @@ export default class Search {
.on('keyup', this.searchInput, this.searchKeyUp);
$(document)
.off('click', this.searchClear)
.on('click', this.searchClear, this.clearSearchField);
.on('click', this.searchClear, this.clearSearchField.bind(this));
}
static submitSearch() {
......
<script>
export default {
name: 'popup-dialog',
name: 'modal',
props: {
title: {
......@@ -75,7 +75,7 @@ export default {
<template>
<div class="modal-open">
<div
class="modal popup-dialog"
class="modal show"
role="dialog"
tabindex="-1"
>
......
<script>
import PopupDialog from './popup_dialog.vue';
import modal from './modal.vue';
export default {
name: 'recaptcha-dialog',
name: 'recaptcha-modal',
props: {
html: {
......@@ -20,7 +20,7 @@ export default {
},
components: {
PopupDialog,
modal,
},
methods: {
......@@ -65,9 +65,9 @@ export default {
</script>
<template>
<popup-dialog
<modal
kind="warning"
class="recaptcha-dialog js-recaptcha-dialog"
class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
:title="__('Please solve the reCAPTCHA')"
@toggle="close"
......@@ -81,5 +81,5 @@ export default {
v-html="html"
></div>
</div>
</popup-dialog>
</modal>
</template>
import RecaptchaDialog from '../components/recaptcha_dialog.vue';
import recaptchaModal from '../components/recaptcha_modal.vue';
export default {
data() {
......@@ -9,7 +9,7 @@ export default {
},
components: {
RecaptchaDialog,
recaptchaModal,
},
methods: {
......
......@@ -19,6 +19,13 @@
max-width: 425px;
width: 100%;
}
&.svg-250 {
img,
svg {
width: 250px;
}
}
}
@mixin svg-size($size) {
......
......@@ -24,10 +24,14 @@
font-size: $gl-font-size;
line-height: 25px;
&.status-box-closed {
&.status-box-mr-closed {
background-color: $gl-danger;
}
&.status-box-issue-closed {
background-color: $gl-primary;
}
&.status-box-merged {
background-color: $gl-primary;
}
......
......@@ -44,11 +44,18 @@ body.modal-open {
}
}
.modal.popup-dialog {
display: block;
.modal {
background-color: $black-transparent;
z-index: 2100;
@media (min-width: $screen-md-min) {
.modal-dialog {
margin: 30px auto;
}
}
}
.recaptcha-dialog .recaptcha-form {
.recaptcha-modal .recaptcha-form {
display: inline-block;
.recaptcha {
......
......@@ -610,11 +610,19 @@
}
.issuable-status-box {
float: none;
display: inline-block;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
margin-top: 0;
height: auto;
align-self: center;
padding-left: 9px;
padding-right: 9px;
@media (min-width: $screen-sm-min) {
display: inline-block;
height: auto;
align-self: center;
}
}
.issuable-gutter-toggle {
......
.modal.popup-dialog {
display: block;
background-color: $black-transparent;
z-index: 2100;
@media (min-width: $screen-md-min) {
.modal-dialog {
width: 600px;
margin: 30px auto;
}
}
}
.project-refs-form,
.project-refs-target-form {
display: inline-block;
......
class Admin::GroupsController < Admin::ApplicationController
include MembersPresentation
prepend EE::Admin::GroupsController
before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update]
......
......@@ -6,6 +6,7 @@ module WithPerformanceBar
end
def peek_enabled?
return true if Rails.env.development?
return false unless Gitlab::PerformanceBar.enabled?(current_user)
if RequestStore.active?
......
......@@ -118,20 +118,24 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw")
end
def blob_raw_path
def blob_raw_url(only_path: false)
if @build && @entry
raw_project_job_artifacts_path(@project, @build, path: @entry.path)
raw_project_job_artifacts_url(@project, @build, path: @entry.path, only_path: only_path)
elsif @snippet
if @snippet.project_id
raw_project_snippet_path(@project, @snippet)
raw_project_snippet_url(@project, @snippet, only_path: only_path)
else
raw_snippet_path(@snippet)
raw_snippet_url(@snippet, only_path: only_path)
end
elsif @blob
project_raw_path(@project, @id)
project_raw_url(@project, @id, only_path: only_path)
end
end
def blob_raw_path
blob_raw_url(only_path: true)
end
# SVGs can contain malicious JavaScript; only include whitelisted
# elements and attributes. Note that this whitelist is by no means complete
# and may omit some elements.
......
......@@ -104,15 +104,23 @@ module DiffHelper
].join(' ').html_safe
end
def diff_file_blob_raw_path(diff_file)
project_raw_path(@project, tree_join(diff_file.content_sha, diff_file.file_path))
def diff_file_blob_raw_url(diff_file, only_path: false)
project_raw_url(@project, tree_join(diff_file.content_sha, diff_file.file_path), only_path: only_path)
end
def diff_file_old_blob_raw_path(diff_file)
def diff_file_old_blob_raw_url(diff_file, only_path: false)
sha = diff_file.old_content_sha
return unless sha
project_raw_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path))
project_raw_url(@project, tree_join(diff_file.old_content_sha, diff_file.old_path), only_path: only_path)
end
def diff_file_blob_raw_path(diff_file)
diff_file_blob_raw_url(diff_file, only_path: true)
end
def diff_file_old_blob_raw_path(diff_file)
diff_file_old_blob_raw_url(diff_file, only_path: true)
end
def diff_file_html_data(project, diff_file_path, diff_commit_id)
......
......@@ -76,7 +76,7 @@ module IssuesHelper
elsif item.try(:merged?)
'status-box-merged'
elsif item.closed?
'status-box-closed'
'status-box-mr-closed'
elsif item.try(:upcoming?)
'status-box-upcoming'
else
......
module LabelsHelper
include ActionView::Helpers::TagHelper
def show_label_issuables_link?(label, issuables_type, current_user: nil, project: nil)
return true if label.is_a?(GroupLabel)
return true unless project
project.feature_available?(issuables_type, current_user)
end
# Link to a Label
#
# label - Label object to link to
......
......@@ -5,7 +5,7 @@ module Emails
@commit = @note.noteable
@target_url = project_commit_url(*note_target_url_options)
mail_answer_thread(@commit, note_thread_options(recipient_id))
mail_answer_note_thread(@commit, @note, note_thread_options(recipient_id))
end
def note_issue_email(recipient_id, note_id)
......@@ -13,7 +13,7 @@ module Emails
@issue = @note.noteable
@target_url = project_issue_url(*note_target_url_options)
mail_answer_thread(@issue, note_thread_options(recipient_id))
mail_answer_note_thread(@issue, @note, note_thread_options(recipient_id))
end
def note_merge_request_email(recipient_id, note_id)
......@@ -21,7 +21,7 @@ module Emails
@merge_request = @note.noteable
@target_url = project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id))
end
def note_snippet_email(recipient_id, note_id)
......@@ -29,7 +29,7 @@ module Emails
@snippet = @note.noteable
@target_url = project_snippet_url(*note_target_url_options)
mail_answer_thread(@snippet, note_thread_options(recipient_id))
mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id))
end
def note_personal_snippet_email(recipient_id, note_id)
......@@ -37,7 +37,7 @@ module Emails
@snippet = @note.noteable
@target_url = snippet_url(@note.noteable)
mail_answer_thread(@snippet, note_thread_options(recipient_id))
mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id))
end
private
......
......@@ -123,8 +123,8 @@ class Notify < BaseMailer
headers['Reply-To'] = address
fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
headers['References'] ||= ''
headers['References'] << ' ' << fallback_reply_message_id
headers['References'] ||= []
headers['References'] << fallback_reply_message_id
@reply_by_email = true
end
......@@ -160,6 +160,18 @@ class Notify < BaseMailer
mail_thread(model, headers)
end
def mail_answer_note_thread(model, note, headers = {})
headers['Message-ID'] = message_id(note)
headers['In-Reply-To'] = message_id(note.references.last)
headers['References'] = note.references.map { |ref| message_id(ref) }
headers['X-GitLab-Discussion-ID'] = note.discussion.id if note.part_of_discussion?
headers[:subject]&.prepend('Re: ')
mail_thread(model, headers)
end
def reply_key
@reply_key ||= SentNotification.reply_key
end
......
......@@ -501,7 +501,6 @@ module Ci
end
def valid_dependency?
return false unless complete?
return false if artifacts_expired?
return false if erased?
......
......@@ -52,6 +52,20 @@ class Commit
diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) }
end
def order_by(collection:, order_by:, sort:)
return collection unless %w[email name commits].include?(order_by)
return collection unless %w[asc desc].include?(sort)
collection.sort do |a, b|
operands = [a, b].tap { |o| o.reverse! if sort == 'desc' }
attr1, attr2 = operands.first.public_send(order_by), operands.second.public_send(order_by) # rubocop:disable PublicSend
# use case insensitive comparison for string values
order_by.in?(%w[email name]) ? attr1.casecmp(attr2) : attr1 <=> attr2
end
end
# Truncate sha to 8 characters
def truncate_sha(sha)
sha[0..MIN_SHA_LENGTH]
......
......@@ -85,8 +85,7 @@ module CacheMarkdownField
def cached_html_up_to_date?(markdown_field)
html_field = cached_markdown_fields.html_field(markdown_field)
cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? # rubocop:disable GitlabSecurity/PublicSend
return false unless cached
return false if cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? # rubocop:disable GitlabSecurity/PublicSend
markdown_changed = attribute_changed?(markdown_field) || false
html_changed = attribute_changed?(html_field) || false
......
......@@ -16,7 +16,7 @@ class Issue < ActiveRecord::Base
include ThrottledTouch
include IgnorableColumn
ignore_column :assignee_id
ignore_column :assignee_id, :branch_name
WEIGHT_RANGE = 1..9
WEIGHT_ALL = 'Everything'.freeze
......
......@@ -913,11 +913,11 @@ class MergeRequest < ActiveRecord::Base
def state_icon_name
if merged?
"check"
"git-merge"
elsif closed?
"times"
"close"
else
"circle-o"
"issue-open-m"
end
end
......
......@@ -368,6 +368,16 @@ class Note < ActiveRecord::Base
end
end
def references
refs = [noteable]
if part_of_discussion?
refs += discussion.notes.take_while { |n| n.id < id }
end
refs
end
def expire_etag_cache
return unless noteable&.discussions_rendered_on_frontend?
......@@ -409,6 +419,9 @@ class Note < ActiveRecord::Base
end
noteable_object&.touch
# We return the noteable object so we can re-use it in EE for ElasticSearch.
noteable_object
end
def banzai_render_context(field)
......
......@@ -669,7 +669,8 @@ class Project < ActiveRecord::Base
end
def import_started?
import? && import_status == 'started'
# import? does SQL work so only run it if it looks like there's an import running
import_status == 'started' && import?
end
def import_scheduled?
......@@ -1157,7 +1158,7 @@ class Project < ActiveRecord::Base
def change_head(branch)
if repository.branch_exists?(branch)
repository.before_change_head
repository.write_ref('HEAD', "refs/heads/#{branch}")
repository.write_ref('HEAD', "refs/heads/#{branch}", force: true)
repository.copy_gitattributes(branch)
repository.after_change_head
reload_default_branch
......
......@@ -24,6 +24,7 @@ class Repository
attr_accessor :full_path, :disk_path, :project, :is_wiki
delegate :ref_name_for_sha, to: :raw_repository
delegate :write_ref, to: :raw_repository
CreateTreeError = Class.new(StandardError)
......@@ -244,11 +245,10 @@ class Repository
# This will still fail if the file is corrupted (e.g. 0 bytes)
begin
write_ref(keep_around_ref_name(sha), sha)
rescue Rugged::ReferenceError => ex
Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
rescue Rugged::OSError => ex
raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
write_ref(keep_around_ref_name(sha), sha, force: true)
rescue Gitlab::Git::Repository::GitError => ex
# Necessary because https://gitlab.com/gitlab-org/gitlab-ce/issues/20156
return true if ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
end
......@@ -258,10 +258,6 @@ class Repository
ref_exists?(keep_around_ref_name(sha))
end
def write_ref(ref_path, sha)
rugged.references.create(ref_path, sha, force: true)
end
def diverging_commit_counts(branch)
root_ref_hash = raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
......@@ -697,7 +693,9 @@ class Repository
def tags_sorted_by(value)
case value
when 'name'
when 'name_asc'
VersionSorter.sort(tags) { |tag| tag.name }
when 'name_desc'
VersionSorter.rsort(tags) { |tag| tag.name }
when 'updated_desc'
tags_sorted_by_committed_date.reverse
......@@ -708,10 +706,14 @@ class Repository
end
end
def contributors
# Params:
#
# order_by: name|email|commits
# sort: asc|desc default: 'asc'
def contributors(order_by: nil, sort: 'asc')
commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true)
commits.group_by(&:author_email).map do |email, commits|
commits = commits.group_by(&:author_email).map do |email, commits|
contributor = Gitlab::Contributor.new
contributor.email = email
......@@ -725,6 +727,7 @@ class Repository
contributor
end
Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end
def refs_contains_sha(ref_type, sha)
......@@ -1043,7 +1046,7 @@ class Repository
end
def create_ref(ref, ref_path)
raw_repository.write_ref(ref_path, ref)
write_ref(ref_path, ref)
end
def ls_files(ref)
......
......@@ -39,11 +39,15 @@ module Ci
begin
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
build.runner_id = runner.id
build.run!
register_success(build)
return Result.new(build, true)
begin
build.runner_id = runner.id
build.run!
register_success(build)
return Result.new(build, true)
rescue Ci::Build::MissingDependenciesError
build.drop!(:missing_dependency_failure)
end
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
# We are looping to find another build that is not conflicting
# It also indicates that this build can be picked and passed to runner.
......@@ -55,9 +59,6 @@ module Ci
# we still have to return 409 in the end,
# to make sure that this is properly handled by runner.
valid = false
rescue Ci::Build::MissingDependenciesError
build.drop!(:missing_dependency_failure)
valid = false
end
end
......
......@@ -108,12 +108,14 @@ class IssuableBaseService < BaseService
end
def merge_quick_actions_into_params!(issuable)
original_description = params.fetch(:description, issuable.description)
description, command_params =
QuickActions::InterpretService.new(project, current_user)
.execute(params[:description], issuable)
.execute(original_description, issuable)
# Avoid a description already set on an issuable to be overwritten by a nil
params[:description] = description if params.key?(:description)
params[:description] = description if description
params.merge!(command_params)
end
......
......@@ -83,12 +83,12 @@
You're all done!
- elsif current_user.todos.any?
.todos-all-done
.svg-content
.svg-content.svg-250
= image_tag 'illustrations/todos_all_done.svg'
- if todos_filter_empty?
%h4.text-center
= Gitlab.config.gitlab.no_todos_messages.sample
%p.text-center
%p
Are you looking for things to do? Take a look at
= succeed "," do
= link_to "the opened issues", issues_dashboard_path
......@@ -104,7 +104,7 @@
= image_tag 'illustrations/todos_empty.svg'
.todos-empty-content
%h4
Todos let you see what you should do next.
Todos let you see what you should do next
%p
When an issue or merge request is assigned to you, or when you
%strong
......
- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: @project.web_url, avatar_url: @project.avatar_url } if @project&.persisted?
- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted?
.projects-dropdown-container
.project-dropdown-sidebar
%ul
......
.file-content.image_file
= image_tag(blob_raw_path, alt: viewer.blob.name)
-# Uses the full URL rather than the path, to prevent it from getting prefixed with the asset host.
= image_tag(blob_raw_url, alt: viewer.blob.name)
.row.empty-state
.col-xs-12
.svg-content= image_tag 'illustrations/clusters_empty.svg'
.col-xs-12.text-center
.col-xs-12
.text-content
%h4= s_('ClusterIntegration|Integrate cluster automation')
%h4.text-center= s_('ClusterIntegration|Integrate cluster automation')
- link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
%p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
%p
.text-center
= link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success'
......@@ -9,6 +9,7 @@
.nav-text
= s_("ClusterIntegration|Clusters can be used to deploy applications and to provide Review Apps for this project")
= render 'projects/ee/clusters/buttons', project: @project
.ci-table.js-clusters-list
.gl-responsive-table-row.table-row-header{ role: "row" }
.table-section.section-30{ role: "rowheader" }
= s_("ClusterIntegration|Cluster")
......
- blob = diff_file.blob
- old_blob = diff_file.old_blob
- blob_raw_path = diff_file_blob_raw_path(diff_file)
- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file)
- blob_raw_url = diff_file_blob_raw_url(diff_file)
- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file)
- click_to_comment = local_assigns.fetch(:click_to_comment, true)
- diff_view_data = local_assigns.fetch(:diff_view_data, '')
- class_name = ''
......@@ -13,7 +13,7 @@
.two-up.view
.wrap
.frame.deleted
= image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
%p.image-info.hide
%span.meta-filesize= number_to_human_size(old_blob.size)
|
......@@ -23,7 +23,7 @@
%strong H:
%span.meta-height
.wrap
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
%p.image-info.hide
%span.meta-filesize= number_to_human_size(blob.size)
|
......@@ -36,9 +36,9 @@
.swipe.view.hide
.swipe-frame
.frame.deleted
= image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
.swipe-wrap
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
%span.swipe-bar
%span.top-handle
%span.bottom-handle
......@@ -46,8 +46,8 @@
.onion-skin.view.hide
.onion-skin-frame
.frame.deleted
= image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
.controls
.transparent
.drag-track
......
- blob = diff_file.blob
- old_blob = diff_file.old_blob
- blob_raw_path = diff_file_blob_raw_path(diff_file)
- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file)
- blob_raw_url = diff_file_blob_raw_url(diff_file)
- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file)
- click_to_comment = local_assigns.fetch(:click_to_comment, true)
- diff_view_data = local_assigns.fetch(:diff_view_data, '')
- class_name = ''
......@@ -12,5 +12,5 @@
.image.js-single-image{ data: diff_view_data }
.wrap
- single_class_name = diff_file.deleted_file? ? 'deleted' : 'added'
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.file_path }
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.file_path }
%p.image-info= number_to_human_size(blob.size)
......@@ -17,12 +17,12 @@
.detail-page-header
.detail-page-header-body
.issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) }
= icon('check', class: "hidden-sm hidden-md hidden-lg")
.issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) }
= sprite_icon('mobile-issue-close', size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs
Closed
.issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) }
= icon('circle-o', class: "hidden-sm hidden-md hidden-lg")
= sprite_icon('issue-open-m', size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs Open
.issuable-meta
......
......@@ -7,7 +7,7 @@
.detail-page-header
.detail-page-header-body
.issuable-status-box.status-box{ class: status_box_class(@merge_request) }
= icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg")
= sprite_icon(@merge_request.state_icon_name, size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs
= @merge_request.state_human_name
......@@ -27,7 +27,7 @@
.dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul
- if can_update_merge_request
%li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'js-issuable-edit'
%li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- unless current_user == @merge_request.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
- if can_update_merge_request
......
......@@ -14,7 +14,7 @@
.form-group
= label_tag :tag_name, nil, class: 'control-label'
.col-sm-10
= text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
= text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control'
.form-group
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10.create-from
......@@ -28,7 +28,7 @@
.form-group
= label_tag :message, nil, class: 'control-label'
.col-sm-10
= text_area_tag :message, @message, required: false, tabindex: 3, class: 'form-control', rows: 5
= text_area_tag :message, @message, required: false, class: 'form-control', rows: 5
.help-block
= s_('TagsPage|Optionally, add a message to the tag.')
%hr
......@@ -41,6 +41,6 @@
.help-block
= s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.')
.form-actions
= button_tag s_('TagsPage|Create tag'), class: 'btn btn-create', tabindex: 3
= button_tag s_('TagsPage|Create tag'), class: 'btn btn-create'
= link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel'
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
......@@ -2,6 +2,8 @@
- status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject]
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project)
- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project)
%li{ id: label_css_id, data: { id: label.id } }
= render "shared/label_row", label: label
......@@ -12,12 +14,14 @@
= icon('caret-down')
.dropdown-menu.dropdown-menu-align-right
%ul
%li
= link_to_label(label, subject: subject, type: :merge_request) do
View merge requests
%li
= link_to_label(label, subject: subject) do
View open issues
- if show_label_merge_requests_link
%li
= link_to_label(label, subject: subject, type: :merge_request) do
View merge requests
- if show_label_issues_link
%li
= link_to_label(label, subject: subject) do
View open issues
- if current_user
%li.label-subscription
- if can_subscribe_to_label_in_different_levels?(label)
......@@ -43,10 +47,12 @@
class: 'text-danger'
.pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do
view merge requests
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do
view open issues
- if show_label_merge_requests_link
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do
view merge requests
- if show_label_issues_link
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do
view open issues
- if current_user
.label-subscription.inline
......
......@@ -6,18 +6,21 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/issues.svg'
.col-xs-12.text-center
.col-xs-12
.text-content
- if has_button && current_user
%h4
The Issue Tracker is the place to add things that need to be improved or solved in a project
= _("The Issue Tracker is the place to add things that need to be improved or solved in a project")
%p
Issues can be bugs, tasks or ideas to be discussed.
Also, issues are searchable and filterable.
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
- else
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
= _("Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.")
.text-center
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
- else
= link_to 'New issue', button_path, class: 'btn btn-success', title: 'New issue', id: 'new_issue_link'
- else
%h4.text-center= _("There are no issues to show")
%p
= _("The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.")
.text-center
%h4 There are no issues to show.
= link_to _('Register / Sign In'), new_user_session_path, class: 'btn btn-success'
......@@ -2,10 +2,10 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/labels.svg'
.col-xs-12.text-center
.col-xs-12
.text-content
%h4 Labels can be applied to issues and merge requests to categorize them.
%p You can also star a label to make it a priority label.
%h4= _("Labels can be applied to issues and merge requests to categorize them.")
%p= _("You can also star a label to make it a priority label.")
- if can?(current_user, :admin_label, @project)
= link_to 'New label', new_project_label_path(@project), class: 'btn btn-new', title: 'New label', id: 'new_label_link'
= link_to 'Generate a default set of labels', generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link'
= link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link'
= link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
......@@ -6,17 +6,18 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/merge_requests.svg'
.col-xs-12.text-center
.col-xs-12
.text-content
- if has_button
%h4
Merge requests are a place to propose changes you've made to a project and discuss those changes with others.
= _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others")
%p
Interested parties can even contribute by pushing commits if they want to.
- if project_select_button
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request', type: :merge_requests
- else
= link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link'
= _("Interested parties can even contribute by pushing commits if they want to.")
.text-center
- if project_select_button
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests
- else
= link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link'
- else
%h4.text-center
There are no merge requests to show.
= _("There are no merge requests to show")
---
title: Adds ordering to projects contributors in API
merge_request: 15469
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Update issuable status icons
merge_request: 15898
author:
type: changed
---
title: Fix broken illustration images for monitoring page empty states
merge_request: 15889
author:
type: fixed
---
title: add support for sorting in tags api
merge_request: 15772
author: haseebeqx
type: added
---
title: Use relative URL for projects to avoid storing domains
merge_request: 15876
author:
type: fixed
---
title: Add a gitlab:tcp_check rake task
merge_request: 15759
author:
type: added
---
title: Use app host instead of asset host when rendering image blob or diff
merge_request:
author:
type: fixed
---
title: Execute quick actions (if present) when creating MR from issue
merge_request: 15810
author:
type: fixed
---
title: Fixed typo for issue description field declaration
merge_request:
author: Marcus Amargi
type: fixed
---
title: Treat empty markdown and html strings as valid cached text, not missing cache
that needs to be updated
merge_request:
author:
type: performance
---
title: check the import_status field before doing SQL operations to check the import
url
merge_request:
author:
type: performance
---
title: removed tabindexes from tag form
merge_request:
author: Marcus Amargi
type: changed
---
title: Hide link to issues/MRs from labels list if issues/MRs are disabled.
merge_request: 15863
author: Sophie Herold
type: fixed
---
title: Make mail notifications of discussion notes In-Reply-To of each other
merge_request: 14289
author:
type: changed
......@@ -3,9 +3,9 @@
#
# If you come up with a fun one, please feel free to contribute it to GitLab!
# https://about.gitlab.com/contributing/
---
- Good job! Looks like you don't have any todos left.
---
- Good job! Looks like you don't have any todos left
- Isn't an empty todo list beautiful?
- Give yourself a pat on the back!
- Nothing left to do, high five!
- Henceforth you shall be known as "Todo Destroyer".
- Henceforth you shall be known as "Todo Destroyer"
......@@ -4,6 +4,12 @@ class AddNewProjectGuidelinesToAppearances < ActiveRecord::Migration
DOWNTIME = false
def change
# Clears the current Appearance cache otherwise it breaks since
# new_project_guidelines_html would be missing. See
# https://gitlab.com/gitlab-org/gitlab-ce/issues/41041
# We're not using Appearance#flush_redis_cache on purpose here.
Rails.cache.delete('current_appearance')
change_table :appearances do |t|
t.text :new_project_guidelines
t.text :new_project_guidelines_html
......
......@@ -23,6 +23,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- **(EEP)** [GitLab GEO](../gitlab-geo/README.md): Replicate your GitLab instance to other geographical locations as a read-only fully operational version.
- **(EEP)** [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry.
- [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability.
- [High Availability on AWS](../university/high-availability/aws/README.md): Set up GitLab HA on Amazon AWS.
### Configuring GitLab
......@@ -44,6 +45,13 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project.
#### Customizing GitLab's appearance
- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers.
- [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description.
- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project.
### Maintaining GitLab
- [Raketasks](../raketasks/README.md): Perform various tasks for maintenance, backups, automatic webhooks setup, etc.
......
......@@ -221,3 +221,22 @@ sudo gitlab-rake gitlab:shell:create_hooks
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:shell:create_hooks RAILS_ENV=production
```
## Check TCP connectivity to a remote site
Sometimes you need to know if your GitLab installation can connect to a TCP
service on another machine - perhaps a PostgreSQL or HTTPS server. A rake task
is included to help you with this:
**Omnibus Installation**
```
sudo gitlab-rake gitlab:tcp_check[example.com,80]
```
**Source Installation**
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:tcp_check[example.com,80] RAILS_ENV=production
```
......@@ -89,9 +89,11 @@ email address in order to sign up.
If you also host a public-facing GitLab instance at `hooli.com` and set your
incoming email domain to `hooli.com`, an attacker could abuse the "Create new
issue by email" feature by using a project's unique address as the email when
signing up for Slack, which would send a confirmation email, which would create
a new issue on the project owned by the attacker, allowing them to click the
issue by email" or
"[Create new merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email)"
features by using a project's unique address as the email when signing up for
Slack, which would send a confirmation email, which would create a new issue or
merge request on the project owned by the attacker, allowing them to click the
confirmation link and validate their account on your company's private Slack
instance.
......
......@@ -520,9 +520,9 @@ PUT /projects/:id/issues/:issue_iid
| `title` | string | no | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Updates an issue to be confidential |
| `assignee_ids` | Array[integer] | no | The ID of the users to assign the issue to |
| `milestone_id` | integer | no | The ID of a milestone to assign the issue to |
| `labels` | string | no | Comma-separated label names for an issue |
| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
......
......@@ -543,15 +543,15 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The ID of a merge request |
| `target_branch` | string | no | The target branch |
| `title` | string | no | Title of MR |
| `assignee_id` | integer | no | Assignee user ID |
| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for an merge request. Set to an empty string to unassign all labels. |
| `description` | string | no | Description of MR |
| `state_event` | string | no | New state (close/reopen) |
| `labels` | string | no | Labels for MR as a comma-separated list |
| `milestone_id` | integer | no | The ID of a milestone |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
| `squash` | boolean| no | Squash commits into a single commit when merging |
| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
......
......@@ -182,6 +182,8 @@ GET /projects/:id/repository/contributors
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `order_by` (optional) - Return contributors ordered by `name`, `email`, or `commits` fields. If not given contributors are ordered by commit date.
- `sort` (optional) - Return contributors sorted in `asc` or `desc` order. Default is `asc`
Response:
......
......@@ -12,7 +12,11 @@ GET /projects/:id/repository/tags
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string| yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user|
| `order_by` | string | no | Return tags ordered by `name` or `updated` fields. Default is `updated` |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
```json
[
......
......@@ -319,45 +319,62 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd].
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Before showing the available entrypoint override methods, let's describe shortly
how the Runner starts and uses a Docker image for the containers used in the
CI jobs:
1. The Runner starts a Docker container using the defined entrypoint (default
from `Dockerfile` that may be overridden in `.gitlab-ci.yml`)
1. The Runner attaches itself to a running container.
1. The Runner prepares a script (the combination of
[`before_script`](../yaml/README.md#before_script),
[`script`](../yaml/README.md#script),
and [`after_script`](../yaml/README.md#after_script)).
1. The Runner sends the script to the container's shell STDIN and receives the
output.
To override the entrypoint of a Docker image, the recommended solution is to
define an empty `entrypoint` in `.gitlab-ci.yml`, so the Runner doesn't start
a useless shell layer. However, that will not work for all Docker versions, and
you should check which one your Runner is using. Specifically:
- If Docker 17.06 or later is used, the `entrypoint` can be set to an empty value.
- If Docker 17.03 or previous versions are used, the `entrypoint` can be set to
`/bin/sh -c`, `/bin/bash -c` or an equivalent shell available in the image.
The syntax of `image:entrypoint` is similar to [Dockerfile's `ENTRYPOINT`][entrypoint].
----
Let's assume you have a `super/sql:experimental` image with some SQL database
inside it and you would like to use it as a base image for your job because you
want to execute some tests with this database binary. Let's also assume that
this image is configured with `/usr/bin/super-sql run` as an entrypoint. That
means, that when starting the container without additional options, it will run
means that when starting the container without additional options, it will run
the database's process, while Runner expects that the image will have no
entrypoint or at least will start with a shell as its entrypoint.
Before the new extended Docker configuration options, you would need to create
your own image based on the `super/sql:experimental` image, set the entrypoint
to a shell and then use it in job's configuration, like:
entrypoint or that the entrypoint is prepared to start a shell command.
```Dockerfile
# my-super-sql:experimental image's Dockerfile
With the extended Docker configuration options, instead of creating your
own image based on `super/sql:experimental`, setting the `ENTRYPOINT`
to a shell, and then using the new image in your CI job, you can now simply
define an `entrypoint` in `.gitlab-ci.yml`.
FROM super/sql:experimental
ENTRYPOINT ["/bin/sh"]
```
**For Docker 17.06+:**
```yaml
# .gitlab-ci.yml
image: my-super-sql:experimental
image:
name: super/sql:experimental
entrypoint: [""]
```
After the new extended Docker configuration options, you can now simply
set an `entrypoint` in `.gitlab-ci.yml`, like:
**For Docker =< 17.03:**
```yaml
# .gitlab-ci.yml
image:
name: super/sql:experimental
entrypoint: ["/bin/sh"]
entrypoint: ["/bin/sh", "-c"]
```
As you can see the syntax of `entrypoint` is similar to
[Dockerfile's `ENTRYPOINT`][entrypoint].
## Define image and services in `config.toml`
Look for the `[runners.docker]` section:
......
This diff is collapsed.
......@@ -232,14 +232,15 @@ An example project service that defines deployment variables is
## Debug tracing
> Introduced in GitLab Runner 1.7.
>
> **WARNING:** Enabling debug tracing can have severe security implications. The
output **will** contain the content of all your secret variables and any other
secrets! The output **will** be uploaded to the GitLab server and made visible
in job traces!
CAUTION: **Warning:**
Enabling debug tracing can have severe security implications. The
output **will** contain the content of all your secret variables and any other
secrets! The output **will** be uploaded to the GitLab server and made visible
in job traces!
By default, GitLab Runner hides most of the details of what it is doing when
processing a job. This behaviour keeps job traces short, and prevents secrets
processing a job. This behavior keeps job traces short, and prevents secrets
from being leaked into the trace unless your script writes them to the screen.
If a job isn't working as expected, this can make the problem difficult to
......
......@@ -215,6 +215,9 @@ There is also and alternative method to [translate messages from validation erro
sprintf(__('Hello %{username}'), { username: 'Joe' }) => 'Hello Joe'
```
The placeholders should match the code style of the respective source file.
For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript.
### Plurals
- In Ruby/HAML:
......
......@@ -10,7 +10,7 @@
* [Tables](#tables)
* [Blocks](#blocks)
* [Panels](#panels)
* [Dialog modals](#dialog-modals)
* [Modals](#modals)
* [Alerts](#alerts)
* [Forms](#forms)
* [Search box](#search-box)
......@@ -255,18 +255,18 @@ Skeleton loading can replace any existing UI elements for the period in which th
---
## Dialog modals
## Modals
Dialog modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal.
Modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal.
### Usage
* When the action is irreversible, dialog modals provide the details and confirm with the user before they take an advanced action.
* When the action will affect privacy or authorization, dialog modals provide advanced information and confirm with the user.
* When the action is irreversible, modals provide the details and confirm with the user before they take an advanced action.
* When the action will affect privacy or authorization, modals provide advanced information and confirm with the user.
### Style
* Dialog modals contain the header, body, and actions.
* Modals contain the header, body, and actions.
* **Header(1):** The header title is a question instead of a descriptive phrase.
* **Body(2):** The content in body should never be ambiguous and unclear. It provides specific information.
* **Actions(3):** Contains a affirmative action, a dismissive action, and an extra action. The order of actions from left to right: Dismissive action → Extra action → Affirmative action
......@@ -277,13 +277,13 @@ Dialog modals are only used for having a conversation and confirmation with the
### Placement
* Dialog modals should always be the center of the screen horizontally and be positioned **72px** from the top.
* Modals should always be the center of the screen horizontally and be positioned **72px** from the top.
| Dialog with 2 actions | Dialog with 3 actions | Special confirmation |
| Modal with 2 actions | Modal with 3 actions | Special confirmation |
| --------------------- | --------------------- | -------------------- |
| ![two-actions](img/modals-general-confimation-dialog.png) | ![three-actions](img/modals-three-buttons.png) | ![spcial-confirmation](img/modals-special-confimation-dialog.png) |
> TODO: Special case for dialog modal.
> TODO: Special case for modal.
---
......
......@@ -46,11 +46,11 @@ Avoid using periods in solitary sentences in these elements:
* Labels
* Hover text
* Bulleted lists
* Dialog body text
* Modal body text
Periods should be used for:
* Lists or dialogs with multiple sentences
* Lists or modals with multiple sentences
* Any sentence followed by a link
| :white_check_mark: **Do** place periods after sentences followed by a link | :no_entry_sign: **Don’t** place periods after a link if it‘s not followed by a sentence |
......@@ -80,7 +80,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable
| Punctuation mark | Copy and paste | HTML entity | Unicode | Mac shortcut | Windows shortcut | Description |
|---|---|---|---|---|---|---|
| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and dialog body text.<br><br>Use in lists or dialogs with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and modal body text.<br><br>Use in lists or modals with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
| Comma | **,** | | | | | Place inside quotation marks.<br><br>Use a [serial comma][serial comma] in lists of three or more terms. |
| Exclamation point | **!** | | | | | Avoid exclamation points as they tend to come across as shouting. Some exceptions include greetings or congratulatory messages. |
| Colon | **:** | `&#58;` | `\u003A` | | | Omit from labels, for example, in the labels for fields in a form. |
......@@ -88,7 +88,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable
| Quotation marks | **“**<br><br>**”**<br><br>**‘**<br><br>**’** | `&ldquo;`<br><br>`&rdquo;`<br><br>`&lsquo;`<br><br>`&rsquo;` | `\u201C`<br><br>`\u201D`<br><br>`\u2018`<br><br>`\u2019` | <kbd>⌥ Option</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>]</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>]</kbd> | <kbd>Alt</kbd>+<kbd>0 1 4 7</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 8</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 5</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 6</kbd> | Use proper quotation marks (also known as smart quotes, curly quotes, or typographer’s quotes) for quotes. Single quotation marks are used for quotes inside of quotes.<br><br>The right single quotation mark symbol is also used for apostrophes.<br><br>Don’t use primes, straight quotes, or free-standing accents for quotation marks. |
| Primes | **′**<br><br>**″** | `&prime;`<br><br>`&Prime;` | `\u2032`<br><br>`\u2033` | | <kbd>Alt</kbd>+<kbd>8 2 4 2</kbd><br><br><kbd>Alt</kbd>+<kbd>8 2 4 3</kbd> | Use prime (′) only in abbreviations for feet, arcminutes, and minutes: 3° 15′<br><br>Use double-prime (″) only in abbreviations for inches, arcseconds, and seconds: 3° 15′ 35″<br><br>Don’t use quotation marks, straight quotes, or free-standing accents for primes. |
| Straight quotes and accents | **"**<br><br>**'**<br><br>**`**<br><br>**´** | `&quot;`<br><br>`&#39;`<br><br>`&#96;`<br><br>`&acute;` | `\u0022`<br><br>`\u0027`<br><br>`\u0060`<br><br>`\u00B4` | | | Don’t use straight quotes or free-standing accents for primes or quotation marks.<br><br>Proper typography never uses straight quotes. They are left over from the age of typewriters and their only modern use is for code. |
| Ellipsis | **…** | `&hellip;` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a dialog or start some other process. |
| Ellipsis | **…** | `&hellip;` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a modal or start some other process. |
| Chevrons | **«**<br><br>**»**<br><br>**‹**<br><br>**›**<br><br>**<**<br><br>**>** | `&#171;`<br><br>`&#187;`<br><br>`&#8249;`<br><br>`&#8250;`<br><br>`&lt;`<br><br>`&gt;` | `\u00AB`<br><br>`\u00BB`<br><br>`\u2039`<br><br>`\u203A`<br><br>`\u003C`<br><br>`\u003E`<br><br> | | | Omit from links or buttons that open another page or move to the next or previous step in a process. Also known as angle brackets, angular quote brackets, or guillemets. |
| Em dash | **—** | `&mdash;` | `\u2014` | <kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 1</kbd> | Avoid using dashes to separate text. If you must use dashes for this purpose — like this — use an em dash surrounded by spaces. |
| En dash | **–** | `&ndash;` | `\u2013` | <kbd>⌥ Option</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 0</kbd> | Use an en dash without spaces instead of a hyphen to indicate a range of values, such as numbers, times, and dates: “3–5 kg”, “8:00 AM–12:30 PM”, “10–17 Jan” |
......@@ -175,7 +175,7 @@ A **comment** is a written piece of text that users of GitLab can create. Commen
#### Discussion
A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved.
## Confirmation dialogs
## Modals
- Destruction buttons should be clear and always say what they are destroying.
E.g., `Delete page` instead of just `Delete`.
......@@ -184,6 +184,8 @@ A **discussion** is a group of 1 or more comments. A discussion can include subd
- Avoid the word `cancel` or `canceled` in the descriptive copy. It can be
confusing when you then see the `Cancel` button.
see also: guidelines for [modal components](components.md#modals)
---
Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons].
......
......@@ -299,9 +299,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-2-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-3-stable gitlab
**Note:** You can change `10-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
**Note:** You can change `10-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
......
......@@ -61,6 +61,10 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/)
- [Towards a production quality open source Git LFS server](https://about.gitlab.com/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/)
## Troubleshooting
- Learn a few [Git troubleshooting](troubleshooting_git.md) techniques to help you out.
## General information
- **Articles:**
......
# Troubleshooting Git
Sometimes things don't work the way they should or as you might expect when
you're using Git. Here are some tips on troubleshooting and resolving issues
with Git.
## Broken pipe errors on git push
'Broken pipe' errors can occur when attempting to push to a remote repository.
When pushing you will usually see:
```
Write failed: Broken pipe
fatal: The remote end hung up unexpectedly
```
To fix this issue, here are some possible solutions.
### Increase the POST buffer size in Git
**If pushing over HTTP**, you can try increasing the POST buffer size in Git's
configuration. Open a terminal and enter:
```sh
git config http.postBuffer 52428800
```
The value is specified in bytes, so in the above case the buffer size has been
set to 50MB. The default is 1MB.
### Check your SSH configuration
**If pushing over SSH**, first check your SSH configuration as 'Broken pipe'
errors can sometimes be caused by underlying issues with SSH (such as
authentication). Make sure that SSH is correctly configured by following the
instructions in the [SSH troubleshooting] docs.
There's another option where you can prevent session timeouts by configuring
SSH 'keep alive' either on the client or on the server (if you are a GitLab
admin and have access to the server).
NOTE: **Note:** configuring *both* the client and the server is unnecessary.
**To configure SSH on the client side**:
- On UNIX, edit `~/.ssh/config` (create the file if it doesn’t exist) and
add or edit:
```
Host your-gitlab-instance-url.com
ServerAliveInterval 60
ServerAliveCountMax 5
```
- On Windows, if you are using PuTTY, go to your session properties, then
navigate to "Connection" and under "Sending of null packets to keep
session active", set "Seconds between keepalives (0 to turn off)" to `60`.
**To configure SSH on the server side**, edit `/etc/ssh/sshd_config` and add:
```
ClientAliveInterval 60
ClientAliveCountMax 5
```
### Running a git repack
**If 'pack-objects' type errors are also being displayed**, you can try to
run a `git repack` before attempting to push to the remote repository again:
```sh
git repack
git push
```
### Upgrade your Git client
In case you're running an older version of Git (< 2.9), consider upgrading
to >= 2.9 (see [Broken pipe when pushing to Git repository][Broken-Pipe]).
[SSH troubleshooting]: ../../ssh/README.md#troubleshooting "SSH Troubleshooting"
[Broken-Pipe]: https://stackoverflow.com/questions/19120120/broken-pipe-when-pushing-to-git-repository/36971469#36971469 "StackOverflow: 'Broken pipe when pushing to Git repository'"
This diff is collapsed.
......@@ -207,11 +207,11 @@ username, you can create a new group and transfer projects to it.
Changing a group's path can have unintended side effects.
* Existing web URLs for the group and anything under it (i.e. projects) will
redirect to the new URLs
* Existing Git remote URLs for projects under the group will no longer work, but
Git responses will show an error with the new remote URL
* The original namespace can be claimed again by any group or user, which will
destroy web redirects and Git remote warnings
redirect to the new URLs.
* Existing Git remote URLs for projects under the group will redirect to the new remote URL, and they
will show a warning with the new remote URL.
* The redirect to the new URL is permanent, that implies the original namespace
can't be claimed again by any group or user.
* If you are vacating the path so it can be claimed by another group or user,
you may need to rename the group name as well since both names and paths must be
unique
......
......@@ -196,12 +196,23 @@ With inline diffs tags you can display {+ additions +} or [- deletions -].
The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
Examples:
```
- {+ additions +}
- [+ additions +]
- {- deletions -}
- [- deletions -]
```
However the wrapping tags cannot be mixed as such:
```
- {+ additions +]
- [+ additions +}
- {- deletions -]
- [- deletions -}
```
### Emoji
......
......@@ -45,11 +45,10 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand
Changing your username can have unintended side effects.
* Existing web URLs for the user and anything under it (i.e. projects) will
redirect to the new URLs
* Existing Git remote URLs for projects under the user will no longer work, but
Git responses will show an error with the new remote URL
* The original namespace can be claimed again by any group or user, which will
destroy any web redirects and Git remote warnings
redirect to the new URLs.
* Existing Git remote URLs for projects under the user will redirect to the new remote URL. Git responses
will show a warning with the new remote URL.
* The redirect to the new URL is permanent, that implies the original namespace can't be claimed again by any group or user.
> It is currently not possible to rename a namespace if it contains a
project with container registry tags, because the project cannot be moved.
......
......@@ -27,7 +27,7 @@ With GitLab merge requests, you can:
- [Resolve merge conflicts from the UI](#resolve-conflicts)
- Enable [fast-forward merge requests](#fast-forward-merge-requests)
- Enable [semi-linear history merge requests](#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch
- [Create new merge requests by email](#create_by_email)
- [Create new merge requests by email](#create-new-merge-requests-by-email)
With **[GitLab Enterprise Edition][ee]**, you can also:
......@@ -145,7 +145,13 @@ You can create a new merge request by sending an email to a user-specific email
address. The address can be obtained on the merge requests page by clicking on
a **Email a new merge request to this project** button. The subject will be
used as the source branch name for the new merge request and the target branch
will be the default branch for the project.
will be the default branch for the project. The message body (if not empty)
will be used as the merge request description. You need
["Reply by email"](../../../administration/reply_by_email.md) enabled to use
this feature. If it's not enabled to your instance, you may ask your GitLab
administrator to do so.
![Create new merge requests by email](img/create_from_email.png)
## Revert changes
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment