Commit 5b7abbac authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ce-to-ee-2017-11-22' into 'master'

CE upstream - Wednesday

Closes #4100, gitlab-com/gitlab-docs#142, #3711, gitaly#758, gitlab-ce#40283, gitlab-ce#40295, gitlab-com/infrastructure#3240, gitaly#742, gitlab-ce#39821, gitlab-qa#102, gitaly#740, gitlab-ce#31825 et gitlab-ce#39497

See merge request gitlab-org/gitlab-ee!3522
parents 8a72b78b cf9c8834
...@@ -216,7 +216,7 @@ review-docs-deploy: ...@@ -216,7 +216,7 @@ review-docs-deploy:
name: review-docs/$CI_COMMIT_REF_NAME name: review-docs/$CI_COMMIT_REF_NAME
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables # DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are secret variables
# Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693 # Discussion: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14236/diffs#note_40140693
url: http://preview-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX url: http://$DOCS_GITLAB_REPO_SUFFIX-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup on_stop: review-docs-cleanup
script: script:
- ./trigger-build-docs deploy - ./trigger-build-docs deploy
...@@ -279,7 +279,7 @@ flaky-examples-check: ...@@ -279,7 +279,7 @@ flaky-examples-check:
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
NEW_FLAKY_SPECS_REPORT: rspec_flaky/report-new.json NEW_FLAKY_SPECS_REPORT: rspec_flaky/report-new.json
stage: post-test stage: post-test
allow_failure: yes allow_failure: true
retry: 0 retry: 0
only: only:
- branches - branches
...@@ -441,7 +441,6 @@ ee_compat_check: ...@@ -441,7 +441,6 @@ ee_compat_check:
- /^[\d-]+-stable(-ee)?/ - /^[\d-]+-stable(-ee)?/
- branches@gitlab-org/gitlab-ee - branches@gitlab-org/gitlab-ee
- branches@gitlab/gitlab-ee - branches@gitlab/gitlab-ee
allow_failure: no
retry: 0 retry: 0
artifacts: artifacts:
name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}" name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
......
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "feature proposal" label:
For the Community Edition issue tracker:
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=feature+proposal
For the Enterprise Edition issue tracker:
- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=feature+proposal
and verify the issue you're about to submit isn't a duplicate.
Please remove this notice if you're confident your issue isn't a duplicate.
------
### Description ### Description
(Include problem, use cases, benefits, and/or goals) (Include problem, use cases, benefits, and/or goals)
...@@ -25,26 +6,4 @@ Please remove this notice if you're confident your issue isn't a duplicate. ...@@ -25,26 +6,4 @@ Please remove this notice if you're confident your issue isn't a duplicate.
### Links / references ### Links / references
### Documentation blurb /label ~"feature proposal"
#### Overview
What is it?
Why should someone use this feature?
What is the underlying (business) problem?
How do you use this feature?
#### Use cases
Who is this for? Provide one or more use cases.
### Feature checklist
Make sure these are completed before closing the issue,
with a link to the relevant commit.
- [ ] [Feature assurance](https://about.gitlab.com/handbook/product/#feature-assurance)
- [ ] Documentation
- [ ] Added to [features.yml](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
/label ~"feature proposal"
\ No newline at end of file
Remove this section and replace it with a description of your MR. Also follow the Add a description of your merge request here. Merge requests without an adequate
checklist below and check off any tasks that are done. If a certain task can not description will not be reviewed until one is added.
be done you should explain so in the MR body. You are free to remove any
sections that do not apply to your MR.
When gathering statistics (e.g. the output of `EXPLAIN ANALYZE`) you should make
sure your database has enough data. Having around 10 000 rows in the tables
being queries should provide a reasonable estimate of how a query will behave.
Also make sure that PostgreSQL uses the following settings:
* `random_page_cost`: `1`
* `work_mem`: `16MB`
* `maintenance_work_mem`: at least `64MB`
* `shared_buffers`: at least `256MB`
If you have access to GitLab.com's staging environment you should also run your
measurements there, and include the results in this MR.
## Database Checklist ## Database Checklist
...@@ -23,34 +8,23 @@ When adding migrations: ...@@ -23,34 +8,23 @@ When adding migrations:
- [ ] Updated `db/schema.rb` - [ ] Updated `db/schema.rb`
- [ ] Added a `down` method so the migration can be reverted - [ ] Added a `down` method so the migration can be reverted
- [ ] Added the output of the migration(s) to the MR body - [ ] Added the output of the migration(s) to the MR body
- [ ] Added the execution time of the migration(s) to the MR body - [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when migrating data)
- [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when
migrating data)
- [ ] Made sure the migration won't interfere with a running GitLab cluster,
for example by disabling transactions for long running migrations
When adding or modifying queries to improve performance: When adding or modifying queries to improve performance:
- [ ] Included the raw SQL queries of the relevant queries - [ ] Included data that shows the performance improvement, preferably in the form of a benchmark
- [ ] Included the output of `EXPLAIN ANALYZE` and execution timings of the - [ ] Included the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant queries
relevant queries
- [ ] Added tests for the relevant changes
When adding indexes:
- [ ] Described the need for these indexes in the MR body
- [ ] Made sure existing indexes can not be reused instead
When adding foreign keys to existing tables: When adding foreign keys to existing tables:
- [ ] Included a migration to remove orphaned rows in the source table - [ ] Included a migration to remove orphaned rows in the source table before adding the foreign key
- [ ] Removed any instances of `dependent: ...` that may no longer be necessary - [ ] Removed any instances of `dependent: ...` that may no longer be necessary
When adding tables: When adding tables:
- [ ] Ordered columns based on their type sizes in descending order - [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines
- [ ] Added foreign keys if necessary - [ ] Added foreign keys to any columns pointing to data in other tables
- [ ] Added indexes if necessary - [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs
When removing columns, tables, indexes or other structures: When removing columns, tables, indexes or other structures:
...@@ -64,8 +38,6 @@ When removing columns, tables, indexes or other structures: ...@@ -64,8 +38,6 @@ When removing columns, tables, indexes or other structures:
- [ ] API support added - [ ] API support added
- [ ] Tests added for this feature/bug - [ ] Tests added for this feature/bug
- Review - Review
- [ ] Has been reviewed by UX
- [ ] Has been reviewed by Frontend
- [ ] Has been reviewed by Backend - [ ] Has been reviewed by Backend
- [ ] Has been reviewed by Database - [ ] Has been reviewed by Database
- [ ] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html) - [ ] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
......
...@@ -37,8 +37,9 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -37,8 +37,9 @@ Please view this file on the master branch, on stable branches it's out of date.
- Reduce the quiet times between scheduler runs on Geo secondaries. !3185 - Reduce the quiet times between scheduler runs on Geo secondaries. !3185
### Added (19 changes, 1 of them is from the community) ### Added (20 changes, 1 of them is from the community)
- Add new push rule to enforce that only the author of a commit can push to the repository. !3086
- Make the maximum capacity of Geo backfill operations configurable. !3107 - Make the maximum capacity of Geo backfill operations configurable. !3107
- Mirrors can now hard fail, keeping them from being retried until a project admin takes action. !3117 - Mirrors can now hard fail, keeping them from being retried until a project admin takes action. !3117
- View/edit epic at group level. !3126 - View/edit epic at group level. !3126
......
...@@ -69,6 +69,7 @@ entry. ...@@ -69,6 +69,7 @@ entry.
- Update container repository path reference and allow using double underscore. !15417 - Update container repository path reference and allow using double underscore. !15417
- Fix crash when navigating to second page of the group dashbaord when there are projects and groups on the first page. !15456 - Fix crash when navigating to second page of the group dashbaord when there are projects and groups on the first page. !15456
- Fix flash errors showing up on a non configured prometheus integration. !35652 - Fix flash errors showing up on a non configured prometheus integration. !35652
- Fix timezone bug in Pikaday and upgrade Pikaday version.
- Fix arguments Import/Export error importing project merge requests. - Fix arguments Import/Export error importing project merge requests.
- Moves mini graph of pipeline to the end of sentence in MR widget. Cleans HTML and tests. - Moves mini graph of pipeline to the end of sentence in MR widget. Cleans HTML and tests.
- Fix user autocomplete in subgroups. - Fix user autocomplete in subgroups.
...@@ -102,7 +103,6 @@ entry. ...@@ -102,7 +103,6 @@ entry.
- Only set Auto-Submitted header once for emails on push. - Only set Auto-Submitted header once for emails on push.
- Fix overlap of right-sidebar and main content when creating a Wiki page. - Fix overlap of right-sidebar and main content when creating a Wiki page.
- Enables scroll to bottom once user has scrolled back to bottom in job log. - Enables scroll to bottom once user has scrolled back to bottom in job log.
- Fix timezone bug in Pikaday and upgrade Pikaday version.
### Changed (21 changes, 7 of them are from the community) ### Changed (21 changes, 7 of them are from the community)
...@@ -145,9 +145,8 @@ entry. ...@@ -145,9 +145,8 @@ entry.
- Optimise getting the pipeline status of commits. - Optimise getting the pipeline status of commits.
- Improve performance of commits list by fully using DB index when getting commit note counts. - Improve performance of commits list by fully using DB index when getting commit note counts.
### Added (27 changes, 10 of them are from the community) ### Added (26 changes, 10 of them are from the community)
- Add new push rule to enforce that only the author of a commit can push to the repository. !3086
- Expose duration in Job entity. !13644 (Mehdi Lahmam (@mehlah)) - Expose duration in Job entity. !13644 (Mehdi Lahmam (@mehlah))
- Prevent git push when LFS objects are missing. !13837 - Prevent git push when LFS objects are missing. !13837
- Automatic configuration settings page. !13850 (Francisco Lopez) - Automatic configuration settings page. !13850 (Francisco Lopez)
......
...@@ -544,6 +544,7 @@ When having your code reviewed and when reviewing merge requests please take the ...@@ -544,6 +544,7 @@ When having your code reviewed and when reviewing merge requests please take the
etc.), they should conform to our [Licensing guidelines][license-finder-doc]. etc.), they should conform to our [Licensing guidelines][license-finder-doc].
See the instructions in that document for help if your MR fails the See the instructions in that document for help if your MR fails the
"license-finder" test with a "Dependencies that need approval" error. "license-finder" test with a "Dependencies that need approval" error.
1. The merge request meets the [definition of done](#definition-of-done).
## Definition of done ## Definition of done
......
...@@ -275,6 +275,8 @@ gem 'gettext_i18n_rails', '~> 1.8.0' ...@@ -275,6 +275,8 @@ gem 'gettext_i18n_rails', '~> 1.8.0'
gem 'gettext_i18n_rails_js', '~> 1.2.0' gem 'gettext_i18n_rails_js', '~> 1.2.0'
gem 'gettext', '~> 3.2.2', require: false, group: :development gem 'gettext', '~> 3.2.2', require: false, group: :development
gem 'batch-loader'
# Perf bar # Perf bar
gem 'peek', '~> 1.0.1' gem 'peek', '~> 1.0.1'
gem 'peek-gc', '~> 0.0.2' gem 'peek-gc', '~> 0.0.2'
...@@ -414,7 +416,7 @@ group :ed25519 do ...@@ -414,7 +416,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.52.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -81,6 +81,7 @@ GEM ...@@ -81,6 +81,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
babosa (1.0.2) babosa (1.0.2)
base32 (0.3.2) base32 (0.3.2)
batch-loader (1.1.1)
bcrypt (3.1.11) bcrypt (3.1.11)
bcrypt_pbkdf (1.0.0) bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
...@@ -299,7 +300,7 @@ GEM ...@@ -299,7 +300,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.52.0) gitaly-proto (0.54.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1013,6 +1014,7 @@ DEPENDENCIES ...@@ -1013,6 +1014,7 @@ DEPENDENCIES
aws-sdk aws-sdk
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
batch-loader
bcrypt_pbkdf (~> 1.0) bcrypt_pbkdf (~> 1.0)
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0) better_errors (~> 2.1.0)
...@@ -1069,7 +1071,7 @@ DEPENDENCIES ...@@ -1069,7 +1071,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.52.0) gitaly-proto (~> 0.54.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
......
10.2.0-pre 10.3.0-pre
...@@ -345,7 +345,6 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -345,7 +345,6 @@ import initGroupAnalytics from './init_group_analytics';
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new Diff(); new Diff();
shortcut_handler = new ShortcutsIssuable(true);
new ZenMode(); new ZenMode();
initIssuableSidebar(); initIssuableSidebar();
...@@ -355,6 +354,8 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -355,6 +354,8 @@ import initGroupAnalytics from './init_group_analytics';
window.mergeRequest = new MergeRequest({ window.mergeRequest = new MergeRequest({
action: mrShowNode.dataset.mrAction, action: mrShowNode.dataset.mrAction,
}); });
shortcut_handler = new ShortcutsIssuable(true);
break; break;
case 'dashboard:activity': case 'dashboard:activity':
new gl.Activities(); new gl.Activities();
......
...@@ -21,6 +21,7 @@ import tabs from '../../vue_shared/components/navigation_tabs.vue'; ...@@ -21,6 +21,7 @@ import tabs from '../../vue_shared/components/navigation_tabs.vue';
import container from '../components/container.vue'; import container from '../components/container.vue';
export default { export default {
components: { components: {
environmentTable, environmentTable,
container, container,
...@@ -57,6 +58,7 @@ export default { ...@@ -57,6 +58,7 @@ export default {
} }
}); });
}, },
/** /**
* Handles URL and query parameter changes. * Handles URL and query parameter changes.
* When the user uses the pagination or the tabs, * When the user uses the pagination or the tabs,
......
...@@ -4,9 +4,11 @@ import tooltip from '../../vue_shared/directives/tooltip'; ...@@ -4,9 +4,11 @@ import tooltip from '../../vue_shared/directives/tooltip';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { COMMON_STR } from '../constants'; import { COMMON_STR } from '../constants';
import Icon from '../../vue_shared/components/icon.vue';
export default { export default {
components: { components: {
Icon,
PopupDialog, PopupDialog,
}, },
directives: { directives: {
...@@ -63,9 +65,9 @@ export default { ...@@ -63,9 +65,9 @@ export default {
:aria-label="editBtnTitle" :aria-label="editBtnTitle"
data-container="body" data-container="body"
class="edit-group btn no-expand"> class="edit-group btn no-expand">
<i <icon
class="fa fa-cogs" name="settings">
aria-hidden="true"/> </icon>
</a> </a>
<a <a
v-tooltip v-tooltip
......
...@@ -34,11 +34,6 @@ export default { ...@@ -34,11 +34,6 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
issuableRef: { issuableRef: {
type: String, type: String,
required: true, required: true,
...@@ -107,6 +102,11 @@ export default { ...@@ -107,6 +102,11 @@ export default {
required: false, required: false,
default: 'issue', default: 'issue',
}, },
canAttachFile: {
type: Boolean,
required: false,
default: true,
},
}, },
data() { data() {
const store = new Store({ const store = new Store({
......
...@@ -29,7 +29,6 @@ import './commit/image_file'; ...@@ -29,7 +29,6 @@ import './commit/image_file';
// lib/utils // lib/utils
import { handleLocationHash } from './lib/utils/common_utils'; import { handleLocationHash } from './lib/utils/common_utils';
import './lib/utils/datetime_utility'; import './lib/utils/datetime_utility';
import './lib/utils/pretty_time';
import './lib/utils/url_utility'; import './lib/utils/url_utility';
// behaviors // behaviors
......
import Vue from 'vue'; import axios from '../../lib/utils/axios_utils';
import VueResource from 'vue-resource';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils'; import { backOff } from '../../lib/utils/common_utils';
Vue.use(VueResource);
const MAX_REQUESTS = 3; const MAX_REQUESTS = 3;
function backOffRequest(makeRequestCallback) { function backOffRequest(makeRequestCallback) {
...@@ -32,8 +29,8 @@ export default class MonitoringService { ...@@ -32,8 +29,8 @@ export default class MonitoringService {
} }
getGraphsData() { getGraphsData() {
return backOffRequest(() => Vue.http.get(this.metricsEndpoint)) return backOffRequest(() => axios.get(this.metricsEndpoint))
.then(resp => resp.json()) .then(resp => resp.data)
.then((response) => { .then((response) => {
if (!response || !response.data) { if (!response || !response.data) {
throw new Error('Unexpected metrics data response from prometheus endpoint'); throw new Error('Unexpected metrics data response from prometheus endpoint');
...@@ -43,8 +40,8 @@ export default class MonitoringService { ...@@ -43,8 +40,8 @@ export default class MonitoringService {
} }
getDeploymentData() { getDeploymentData() {
return backOffRequest(() => Vue.http.get(this.deploymentEndpoint)) return backOffRequest(() => axios.get(this.deploymentEndpoint))
.then(resp => resp.json()) .then(resp => resp.data)
.then((response) => { .then((response) => {
if (!response || !response.deployments) { if (!response || !response.deployments) {
throw new Error('Unexpected deployment data response from prometheus endpoint'); throw new Error('Unexpected deployment data response from prometheus endpoint');
......
...@@ -171,6 +171,7 @@ ...@@ -171,6 +171,7 @@
*/ */
updateContent(parameters) { updateContent(parameters) {
this.updateInternalState(parameters); this.updateInternalState(parameters);
// fetch new data // fetch new data
return this.service.getPipelines(this.requestData) return this.service.getPipelines(this.requestData)
.then((response) => { .then((response) => {
...@@ -245,9 +246,11 @@ ...@@ -245,9 +246,11 @@
/> />
<div <div
class="blank-state blank-state-no-icon" class="blank-state-row"
v-if="shouldRenderNoPipelinesMessage"> v-if="shouldRenderNoPipelinesMessage">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2> <div class="blank-state-center">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
</div> </div>
<div <div
......
import renderMath from './render_math'; import renderMath from './render_math';
import renderMermaid from './render_mermaid';
// Render Gitlab flavoured Markdown // Render Gitlab flavoured Markdown
// //
// Delegates to syntax highlight and render math // Delegates to syntax highlight and render math & mermaid diagrams.
// //
$.fn.renderGFM = function renderGFM() { $.fn.renderGFM = function renderGFM() {
this.find('.js-syntax-highlight').syntaxHighlight(); this.find('.js-syntax-highlight').syntaxHighlight();
renderMath(this.find('.js-render-math')); renderMath(this.find('.js-render-math'));
renderMermaid(this.find('.js-render-mermaid'));
return this; return this;
}; };
......
// Renders diagrams and flowcharts from text using Mermaid in any element with the
// `js-render-mermaid` class.
//
// Example markup:
//
// <pre class="js-render-mermaid">
// graph TD;
// A-- > B;
// A-- > C;
// B-- > D;
// C-- > D;
// </pre>
//
import Flash from './flash';
export default function renderMermaid($els) {
if (!$els.length) return;
import(/* webpackChunkName: 'mermaid' */ 'blackst0ne-mermaid').then((mermaid) => {
mermaid.initialize({
loadOnStart: false,
theme: 'neutral',
});
$els.each((i, el) => {
mermaid.init(undefined, el);
});
}).catch((err) => {
Flash(`Can't load mermaid module: ${err}`);
});
}
import Flash from './flash'; import Flash from './flash';
import { __, s__ } from './locale'; import { __, s__ } from './locale';
import { spriteIcon } from './lib/utils/common_utils';
export default class Star { export default class Star {
constructor() { constructor() {
...@@ -7,16 +8,18 @@ export default class Star { ...@@ -7,16 +8,18 @@ export default class Star {
.on('ajax:success', function handleSuccess(e, data) { .on('ajax:success', function handleSuccess(e, data) {
const $this = $(this); const $this = $(this);
const $starSpan = $this.find('span'); const $starSpan = $this.find('span');
const $starIcon = $this.find('i'); const $startIcon = $this.find('svg');
function toggleStar(isStarred) { function toggleStar(isStarred) {
$this.parent().find('.star-count').text(data.star_count); $this.parent().find('.star-count').text(data.star_count);
if (isStarred) { if (isStarred) {
$starSpan.removeClass('starred').text(s__('StarProject|Star')); $starSpan.removeClass('starred').text(s__('StarProject|Star'));
$starIcon.removeClass('fa-star').addClass('fa-star-o'); $startIcon.remove();
$this.prepend(spriteIcon('star-o'));
} else { } else {
$starSpan.addClass('starred').text(__('Unstar')); $starSpan.addClass('starred').text(__('Unstar'));
$starIcon.removeClass('fa-star-o').addClass('fa-star'); $startIcon.remove();
$this.prepend(spriteIcon('star'));
} }
} }
......
...@@ -14,7 +14,7 @@ export default { ...@@ -14,7 +14,7 @@ export default {
statusObj() { statusObj() {
return { return {
group: this.status, group: this.status,
icon: `icon_status_${this.status}`, icon: `status_${this.status}`,
}; };
}, },
}, },
......
...@@ -50,7 +50,9 @@ ...@@ -50,7 +50,9 @@
<template> <template>
<div class="md-header"> <div class="md-header">
<ul class="nav-links clearfix"> <ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }"> <li
class="md-header-tab"
:class="{ active: !previewMarkdown }">
<a <a
class="js-write-link" class="js-write-link"
href="#md-write-holder" href="#md-write-holder"
...@@ -59,7 +61,9 @@ ...@@ -59,7 +61,9 @@
Write Write
</a> </a>
</li> </li>
<li :class="{ active: previewMarkdown }"> <li
class="md-header-tab"
:class="{ active: previewMarkdown }">
<a <a
class="js-preview-link" class="js-preview-link"
href="#md-preview-holder" href="#md-preview-holder"
...@@ -68,56 +72,52 @@ ...@@ -68,56 +72,52 @@
Preview Preview
</a> </a>
</li> </li>
<li class="pull-right"> <li class="md-header-toolbar">
<div class="toolbar-group"> <toolbar-button
<toolbar-button tag="**"
tag="**" button-title="Add bold text"
button-title="Add bold text" icon="bold" />
icon="bold" /> <toolbar-button
<toolbar-button tag="*"
tag="*" button-title="Add italic text"
button-title="Add italic text" icon="italic" />
icon="italic" /> <toolbar-button
<toolbar-button tag="> "
tag="> " :prepend="true"
:prepend="true" button-title="Insert a quote"
button-title="Insert a quote" icon="quote" />
icon="quote" /> <toolbar-button
<toolbar-button tag="`"
tag="`" tag-block="```"
tag-block="```" button-title="Insert code"
button-title="Insert code" icon="code" />
icon="code" /> <toolbar-button
<toolbar-button tag="* "
tag="* " :prepend="true"
:prepend="true" button-title="Add a bullet list"
button-title="Add a bullet list" icon="list-bulleted" />
icon="list-bulleted" /> <toolbar-button
<toolbar-button tag="1. "
tag="1. " :prepend="true"
:prepend="true" button-title="Add a numbered list"
button-title="Add a numbered list" icon="list-numbered" />
icon="list-numbered" /> <toolbar-button
<toolbar-button tag="* [ ] "
tag="* [ ] " :prepend="true"
:prepend="true" button-title="Add a task list"
button-title="Add a task list" icon="task-done" />
icon="task-done" /> <button
</div> v-tooltip
<div class="toolbar-group"> aria-label="Go full screen"
<button class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
v-tooltip data-container="body"
aria-label="Go full screen" tabindex="-1"
class="toolbar-btn js-zen-enter" title="Go full screen"
data-container="body" type="button">
tabindex="-1" <icon
title="Go full screen" name="screen-full">
type="button"> </icon>
<icon </button>
name="screen-full">
</icon>
</button>
</div>
</li> </li>
</ul> </ul>
</div> </div>
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
<button <button
v-tooltip v-tooltip
type="button" type="button"
class="toolbar-btn js-md hidden-xs" class="toolbar-btn js-md"
tabindex="-1" tabindex="-1"
data-container="body" data-container="body"
:data-md-tag="tag" :data-md-tag="tag"
......
...@@ -56,6 +56,12 @@ ...@@ -56,6 +56,12 @@
} }
} }
.blank-state-center {
padding-top: 20px;
padding-bottom: 20px;
text-align: center;
}
.blank-state { .blank-state {
padding: 20px; padding: 20px;
border: 1px solid $border-color; border: 1px solid $border-color;
...@@ -67,7 +73,10 @@ ...@@ -67,7 +73,10 @@
align-items: center; align-items: center;
padding: 50px 30px; padding: 50px 30px;
} }
}
.blank-state,
.blank-state-center {
.blank-state-icon { .blank-state-icon {
svg { svg {
display: block; display: block;
......
...@@ -438,6 +438,7 @@ img.emoji { ...@@ -438,6 +438,7 @@ img.emoji {
/** COMMON CLASSES **/ /** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; } .prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; } .prepend-top-5 { margin-top: 5px; }
.prepend-top-8 { margin-top: $grid-size; }
.prepend-top-10 { margin-top: 10px; } .prepend-top-10 { margin-top: 10px; }
.prepend-top-15 { margin-top: 15px; } .prepend-top-15 { margin-top: 15px; }
.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-default { margin-top: $gl-padding !important; }
......
...@@ -40,12 +40,6 @@ ...@@ -40,12 +40,6 @@
a:hover { a:hover {
background-color: $link-hover-background; background-color: $link-hover-background;
color: $gl-text-color; color: $gl-text-color;
.settings-avatar {
svg {
fill: $gl-text-color;
}
}
} }
.avatar-container { .avatar-container {
...@@ -138,10 +132,6 @@ ...@@ -138,10 +132,6 @@
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
} }
svg {
fill: $gl-text-color-secondary;
}
.nav-item-name { .nav-item-name {
flex: 1; flex: 1;
} }
...@@ -224,10 +214,6 @@ ...@@ -224,10 +214,6 @@
&:hover { &:hover {
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
} }
...@@ -338,7 +324,6 @@ ...@@ -338,7 +324,6 @@
align-items: center; align-items: center;
svg { svg {
fill: $gl-text-color-secondary;
margin-right: 8px; margin-right: 8px;
} }
...@@ -349,10 +334,6 @@ ...@@ -349,10 +334,6 @@
&:hover { &:hover {
background-color: $border-color; background-color: $border-color;
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
} }
......
...@@ -364,6 +364,18 @@ span.idiff { ...@@ -364,6 +364,18 @@ span.idiff {
float: none; float: none;
} }
} }
@media (max-width: $screen-xs-max) {
display: block;
.file-actions {
white-space: normal;
.btn-group {
padding-top: 5px;
}
}
}
} }
.is-stl-loading { .is-stl-loading {
......
...@@ -305,16 +305,11 @@ ...@@ -305,16 +305,11 @@
color: $gl-text-color; color: $gl-text-color;
border-color: $dropdown-input-focus-border; border-color: $dropdown-input-focus-border;
outline: none; outline: none;
svg {
fill: $gl-text-color;
}
} }
svg { svg {
height: 14px; height: 14px;
width: 14px; width: 14px;
fill: $gl-text-color-secondary;
vertical-align: middle; vertical-align: middle;
} }
......
...@@ -30,10 +30,6 @@ ...@@ -30,10 +30,6 @@
&.dropdown.open > a { &.dropdown.open > a {
color: $color-900; color: $color-900;
background-color: $color-alternate; background-color: $color-alternate;
svg {
fill: currentColor;
}
} }
&.line-separator { &.line-separator {
...@@ -51,10 +47,6 @@ ...@@ -51,10 +47,6 @@
color: $color-200; color: $color-200;
> a { > a {
svg {
fill: $color-200;
}
&.header-user-dropdown-toggle { &.header-user-dropdown-toggle {
.header-user-avatar { .header-user-avatar {
border-color: $color-200; border-color: $color-200;
......
...@@ -235,10 +235,6 @@ ...@@ -235,10 +235,6 @@
opacity: 1; opacity: 1;
color: $white-light; color: $white-light;
svg {
fill: currentColor;
}
&.header-user-dropdown-toggle .header-user-avatar { &.header-user-dropdown-toggle .header-user-avatar {
border-color: $white-light; border-color: $white-light;
} }
...@@ -269,14 +265,6 @@ ...@@ -269,14 +265,6 @@
font-size: 20px; font-size: 20px;
} }
} }
&.active > a,
&.dropdown.open > a {
svg {
fill: currentColor;
}
}
} }
} }
} }
...@@ -289,10 +277,6 @@ ...@@ -289,10 +277,6 @@
text-decoration: none; text-decoration: none;
outline: 0; outline: 0;
color: $white-light; color: $white-light;
svg {
fill: currentColor;
}
} }
> a { > a {
...@@ -307,10 +291,6 @@ ...@@ -307,10 +291,6 @@
border-radius: $border-radius-default; border-radius: $border-radius-default;
height: 32px; height: 32px;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
svg {
fill: currentColor;
}
} }
&.line-separator { &.line-separator {
......
.ci-status-icon-success, .ci-status-icon-success,
.ci-status-icon-passed { .ci-status-icon-passed {
color: $green-500; color: $green-500;
svg {
fill: $green-500;
}
} }
.ci-status-icon-failed { .ci-status-icon-failed {
color: $gl-danger; color: $gl-danger;
svg {
fill: $gl-danger;
}
} }
.ci-status-icon-pending, .ci-status-icon-pending,
.ci-status-icon-failed_with_warnings, .ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings { .ci-status-icon-success_with_warnings {
color: $orange-500; color: $orange-500;
svg {
fill: $orange-500;
}
} }
.ci-status-icon-running { .ci-status-icon-running {
color: $blue-400; color: $blue-400;
svg {
fill: $blue-400;
}
} }
.ci-status-icon-canceled, .ci-status-icon-canceled,
.ci-status-icon-disabled, .ci-status-icon-disabled,
.ci-status-icon-not-found { .ci-status-icon-not-found {
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
.ci-status-icon-created, .ci-status-icon-created,
.ci-status-icon-skipped { .ci-status-icon-skipped {
color: $gray-darkest; color: $gray-darkest;
svg {
fill: $gray-darkest;
}
} }
.ci-status-icon-manual { .ci-status-icon-manual {
color: $gl-text-color; color: $gl-text-color;
svg {
fill: $gl-text-color;
}
} }
.icon-link { .icon-link {
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
} }
svg { svg {
fill: currentColor;
&.s8 { @include svg-size(8px); } &.s8 { @include svg-size(8px); }
&.s12 { @include svg-size(12px); } &.s12 { @include svg-size(12px); }
&.s16 { @include svg-size(16px); } &.s16 { @include svg-size(16px); }
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
.md-header { .md-header {
.nav-links { .nav-links {
a { a {
width: 100%;
padding-top: 0; padding-top: 0;
line-height: 19px; line-height: 19px;
...@@ -72,6 +73,28 @@ ...@@ -72,6 +73,28 @@
} }
} }
.md-header-tab {
@media(max-width: $screen-xs-max) {
flex: 1;
width: 100%;
border-bottom: 1px solid $border-color;
text-align: center;
}
}
.md-header-toolbar {
margin-left: auto;
@media(max-width: $screen-xs-max) {
flex: none;
display: flex;
justify-content: center;
width: 100%;
padding-top: $gl-padding-top;
padding-bottom: $gl-padding-top;
}
}
.referenced-users { .referenced-users {
color: $gl-text-color; color: $gl-text-color;
padding-top: 10px; padding-top: 10px;
...@@ -126,16 +149,6 @@ ...@@ -126,16 +149,6 @@
} }
} }
.toolbar-group {
float: left;
margin-right: -5px;
margin-left: $gl-padding;
&:first-child {
margin-left: 0;
}
}
.toolbar-btn { .toolbar-btn {
float: left; float: left;
padding: 0 7px; padding: 0 7px;
...@@ -158,6 +171,16 @@ ...@@ -158,6 +171,16 @@
} }
} }
.toolbar-fullscreen-btn {
margin-left: $gl-padding;
margin-right: -5px;
@media(max-width: $screen-xs-max) {
margin-left: 0;
margin-right: 0;
}
}
.atwho-view { .atwho-view {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
......
...@@ -130,14 +130,6 @@ ...@@ -130,14 +130,6 @@
background-color: $color-light; background-color: $color-light;
color: $color-dark; color: $color-dark;
border-color: $color-dark; border-color: $color-dark;
svg {
fill: $color-dark;
}
}
svg {
fill: $color-main;
} }
} }
......
...@@ -57,15 +57,7 @@ ...@@ -57,15 +57,7 @@
padding: 5px; padding: 5px;
font-size: 36px; font-size: 36px;
svg {
fill: $gl-text-color;
}
&:hover { &:hover {
color: $black; color: $black;
svg {
fill: $black;
}
} }
} }
.documentation-index {
h1 {
margin: 0;
}
h2 {
font-size: 20px;
}
li {
line-height: 24px;
color: $document-index-color;
a {
margin-right: 3px;
}
}
}
.shortcut-mappings { .shortcut-mappings {
font-size: 12px; font-size: 12px;
color: $help-shortcut-mapping-color; color: $help-shortcut-mapping-color;
......
...@@ -547,10 +547,6 @@ ul.notes { ...@@ -547,10 +547,6 @@ ul.notes {
width: 16px; width: 16px;
top: 0; top: 0;
vertical-align: text-top; vertical-align: text-top;
path {
fill: currentColor;
}
} }
.award-control-icon-positive, .award-control-icon-positive,
...@@ -570,10 +566,6 @@ ul.notes { ...@@ -570,10 +566,6 @@ ul.notes {
.link-highlight { .link-highlight {
color: $gl-link-color; color: $gl-link-color;
fill: $gl-link-color; fill: $gl-link-color;
svg {
fill: $gl-link-color;
}
} }
.award-control-icon-neutral { .award-control-icon-neutral {
......
...@@ -55,10 +55,6 @@ ...@@ -55,10 +55,6 @@
&:not(span):hover { &:not(span):hover {
background-color: rgba($gl-text-color-secondary, .07); background-color: rgba($gl-text-color-secondary, .07);
} }
svg {
fill: $gl-text-color-secondary;
}
} }
} }
......
...@@ -204,8 +204,7 @@ class ApplicationController < ActionController::Base ...@@ -204,8 +204,7 @@ class ApplicationController < ActionController::Base
end end
def check_password_expiration def check_password_expiration
return if session[:impersonator_id] return if session[:impersonator_id] || !current_user&.allow_password_authentication?
return unless current_user&.allow_password_authentication?
password_expires_at = current_user&.password_expires_at password_expires_at = current_user&.password_expires_at
......
...@@ -152,7 +152,7 @@ module IssuableCollections ...@@ -152,7 +152,7 @@ module IssuableCollections
when 'MergeRequest' when 'MergeRequest'
[ [
:source_project, :target_project, :author, :assignee, :labels, :milestone, :source_project, :target_project, :author, :assignee, :labels, :milestone,
head_pipeline: :project, target_project: :namespace, merge_request_diff: :merge_request_diff_commits head_pipeline: :project, target_project: :namespace, latest_merge_request_diff: :merge_request_diff_commits
] ]
end end
end end
......
...@@ -8,6 +8,7 @@ module PreviewMarkdown ...@@ -8,6 +8,7 @@ module PreviewMarkdown
case controller_name case controller_name
when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] } when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] }
when 'snippets' then { skip_project_check: true } when 'snippets' then { skip_project_check: true }
when 'groups' then { group: group }
else {} else {}
end end
......
...@@ -86,7 +86,8 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -86,7 +86,8 @@ class Groups::MilestonesController < Groups::ApplicationController
GroupMilestone.build_collection(group, group_projects, params) GroupMilestone.build_collection(group, group_projects, params)
end end
milestones + legacy_milestones @sort = params[:sort] || 'due_date_asc'
MilestoneArray.sort(milestones + legacy_milestones, @sort)
end end
def milestone def milestone
......
...@@ -22,12 +22,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -22,12 +22,7 @@ class Projects::CommitController < Projects::ApplicationController
apply_diff_view_cookie! apply_diff_view_cookie!
respond_to do |format| respond_to do |format|
format.html do format.html { render }
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37599
Gitlab::GitalyClient.allow_n_plus_1_calls do
render
end
end
format.diff { render text: @commit.to_diff } format.diff { render text: @commit.to_diff }
format.patch { render text: @commit.to_patch } format.patch { render text: @commit.to_patch }
end end
...@@ -112,7 +107,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -112,7 +107,7 @@ class Projects::CommitController < Projects::ApplicationController
end end
def commit def commit
@noteable = @commit ||= @project.commit(params[:id]) @noteable = @commit ||= @project.commit_by(oid: params[:id])
end end
def define_commit_vars def define_commit_vars
......
...@@ -10,10 +10,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic ...@@ -10,10 +10,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
def show def show
@environment = @merge_request.environments_for(current_user).last @environment = @merge_request.environments_for(current_user).last
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37431 render json: { html: view_to_html_string("projects/merge_requests/diffs/_diffs") }
Gitlab::GitalyClient.allow_n_plus_1_calls do
render json: { html: view_to_html_string("projects/merge_requests/diffs/_diffs") }
end
end end
def diff_for_path def diff_for_path
......
# :nocov:
if Rails.env.test?
class UnicornTestController < ActionController::Base
def pid
render plain: Process.pid.to_s
end
def kill
Process.kill(params[:signal], Process.pid)
render plain: 'Bye!'
end
end
end
# :nocov:
...@@ -222,7 +222,7 @@ module MarkupHelper ...@@ -222,7 +222,7 @@ module MarkupHelper
data = options[:data].merge({ container: 'body' }) data = options[:data].merge({ container: 'body' })
content_tag :button, content_tag :button,
type: 'button', type: 'button',
class: 'toolbar-btn js-md has-tooltip hidden-xs', class: 'toolbar-btn js-md has-tooltip',
tabindex: -1, tabindex: -1,
data: data, data: data,
title: options[:title], title: options[:title],
......
...@@ -150,7 +150,8 @@ module SearchHelper ...@@ -150,7 +150,8 @@ module SearchHelper
placeholder: 'Search or filter results...', placeholder: 'Search or filter results...',
data: { data: {
'username-params' => @users.to_json(only: [:id, :username]) 'username-params' => @users.to_json(only: [:id, :username])
} },
autocomplete: 'off'
} }
if @project.present? if @project.present?
......
...@@ -76,12 +76,24 @@ class Blob < SimpleDelegator ...@@ -76,12 +76,24 @@ class Blob < SimpleDelegator
new(blob, project) new(blob, project)
end end
def self.lazy(project, commit_id, path)
BatchLoader.for(commit_id: commit_id, path: path).batch do |items, loader|
project.repository.blobs_at(items.map(&:values)).each do |blob|
loader.call({ commit_id: blob.commit_id, path: blob.path }, blob) if blob
end
end
end
def initialize(blob, project = nil) def initialize(blob, project = nil)
@project = project @project = project
super(blob) super(blob)
end end
def inspect
"#<#{self.class.name} oid:#{id[0..8]} commit:#{commit_id[0..8]} path:#{path}>"
end
# Returns the data of the blob. # Returns the data of the blob.
# #
# If the blob is a text based blob the content is converted to UTF-8 and any # If the blob is a text based blob the content is converted to UTF-8 and any
...@@ -95,7 +107,10 @@ class Blob < SimpleDelegator ...@@ -95,7 +107,10 @@ class Blob < SimpleDelegator
end end
def load_all_data! def load_all_data!
super(project.repository) if project # Endpoint needed: gitlab-org/gitaly#756
Gitlab::GitalyClient.allow_n_plus_1_calls do
super(project.repository) if project
end
end end
def no_highlighting? def no_highlighting?
......
...@@ -247,7 +247,7 @@ module Ci ...@@ -247,7 +247,7 @@ module Ci
@merge_request ||= @merge_request ||=
begin begin
merge_requests = MergeRequest.includes(:merge_request_diff) merge_requests = MergeRequest.includes(:latest_merge_request_diff)
.where(source_branch: ref, .where(source_branch: ref,
source_project: pipeline.project) source_project: pipeline.project)
.reorder(iid: :desc) .reorder(iid: :desc)
......
...@@ -520,7 +520,10 @@ module Ci ...@@ -520,7 +520,10 @@ module Ci
end end
def latest_builds_with_artifacts def latest_builds_with_artifacts
@latest_builds_with_artifacts ||= builds.latest.with_artifacts # We purposely cast the builds to an Array here. Because we always use the
# rows if there are more than 0 this prevents us from having to run two
# queries: one to get the count and one to get the rows.
@latest_builds_with_artifacts ||= builds.latest.with_artifacts.to_a
end end
private private
......
...@@ -84,7 +84,7 @@ class Commit ...@@ -84,7 +84,7 @@ class Commit
end end
def id def id
@raw.id raw.id
end end
def ==(other) def ==(other)
...@@ -109,12 +109,12 @@ class Commit ...@@ -109,12 +109,12 @@ class Commit
@link_reference_pattern ||= super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})/) @link_reference_pattern ||= super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})/)
end end
def to_reference(from_project = nil, full: false) def to_reference(from = nil, full: false)
commit_reference(from_project, id, full: full) commit_reference(from, id, full: full)
end end
def reference_link_text(from_project = nil, full: false) def reference_link_text(from = nil, full: false)
commit_reference(from_project, short_id, full: full) commit_reference(from, short_id, full: full)
end end
def diff_line_count def diff_line_count
...@@ -361,7 +361,7 @@ class Commit ...@@ -361,7 +361,7 @@ class Commit
@deltas ||= raw.deltas @deltas ||= raw.deltas
end end
def diffs(diff_options = nil) def diffs(diff_options = {})
Gitlab::Diff::FileCollection::Commit.new(self, diff_options: diff_options) Gitlab::Diff::FileCollection::Commit.new(self, diff_options: diff_options)
end end
...@@ -381,8 +381,8 @@ class Commit ...@@ -381,8 +381,8 @@ class Commit
private private
def commit_reference(from_project, referable_commit_id, full: false) def commit_reference(from, referable_commit_id, full: false)
reference = project.to_reference(from_project, full: full) reference = project.to_reference(from, full: full)
if reference.present? if reference.present?
"#{reference}#{self.class.reference_prefix}#{referable_commit_id}" "#{reference}#{self.class.reference_prefix}#{referable_commit_id}"
......
...@@ -89,8 +89,8 @@ class CommitRange ...@@ -89,8 +89,8 @@ class CommitRange
alias_method :id, :to_s alias_method :id, :to_s
def to_reference(from_project = nil, full: false) def to_reference(from = nil, full: false)
project_reference = project.to_reference(from_project, full: full) project_reference = project.to_reference(from, full: full)
if project_reference.present? if project_reference.present?
project_reference + self.class.reference_prefix + self.id project_reference + self.class.reference_prefix + self.id
...@@ -99,8 +99,8 @@ class CommitRange ...@@ -99,8 +99,8 @@ class CommitRange
end end
end end
def reference_link_text(from_project = nil) def reference_link_text(from = nil)
project_reference = project.to_reference(from_project) project_reference = project.to_reference(from)
reference = ref_from + notation + ref_to reference = ref_from + notation + ref_to
if project_reference.present? if project_reference.present?
......
module ManualInverseAssociation
extend ActiveSupport::Concern
module ClassMethods
def manual_inverse_association(association, inverse)
define_method(association) do |*args|
super(*args).tap do |value|
next unless value
child_association = value.association(inverse)
child_association.set_inverse_instance(self)
child_association.target = self
end
end
end
end
end
...@@ -31,11 +31,11 @@ module Mentionable ...@@ -31,11 +31,11 @@ module Mentionable
# #
# By default this will be the class name and the result of calling # By default this will be the class name and the result of calling
# `to_reference` on the object. # `to_reference` on the object.
def gfm_reference(from_project = nil) def gfm_reference(from = nil)
# "MergeRequest" > "merge_request" > "Merge request" > "merge request" # "MergeRequest" > "merge_request" > "Merge request" > "merge request"
friendly_name = self.class.to_s.underscore.humanize.downcase friendly_name = self.class.to_s.underscore.humanize.downcase
"#{friendly_name} #{to_reference(from_project)}" "#{friendly_name} #{to_reference(from)}"
end end
# The GFM reference to this Mentionable, which shouldn't be included in its #references. # The GFM reference to this Mentionable, which shouldn't be included in its #references.
......
...@@ -7,7 +7,7 @@ module Referable ...@@ -7,7 +7,7 @@ module Referable
# Returns the String necessary to reference this object in Markdown # Returns the String necessary to reference this object in Markdown
# #
# from_project - Refering Project object # from - Referring parent object
# #
# This should be overridden by the including class. # This should be overridden by the including class.
# #
...@@ -17,12 +17,12 @@ module Referable ...@@ -17,12 +17,12 @@ module Referable
# Issue.last.to_reference(other_project) # => "cross-project#1" # Issue.last.to_reference(other_project) # => "cross-project#1"
# #
# Returns a String # Returns a String
def to_reference(_from_project = nil, full:) def to_reference(_from = nil, full:)
'' ''
end end
def reference_link_text(from_project = nil) def reference_link_text(from = nil)
to_reference(from_project) to_reference(from)
end end
included do included do
......
...@@ -38,11 +38,11 @@ class ExternalIssue ...@@ -38,11 +38,11 @@ class ExternalIssue
@project.id @project.id
end end
def to_reference(_from_project = nil, full: nil) def to_reference(_from = nil, full: nil)
id id
end end
def reference_link_text(from_project = nil) def reference_link_text(from = nil)
return "##{id}" if id =~ /^\d+$/ return "##{id}" if id =~ /^\d+$/
id id
......
...@@ -115,7 +115,7 @@ class Group < Namespace ...@@ -115,7 +115,7 @@ class Group < Namespace
end end
end end
def to_reference(_from_project = nil, full: nil) def to_reference(_from = nil, full: nil)
"#{self.class.reference_prefix}#{full_path}" "#{self.class.reference_prefix}#{full_path}"
end end
......
...@@ -168,12 +168,12 @@ class Label < ActiveRecord::Base ...@@ -168,12 +168,12 @@ class Label < ActiveRecord::Base
# #
# Returns a String # Returns a String
# #
def to_reference(from_project = nil, target_project: nil, format: :id, full: false) def to_reference(from = nil, target_project: nil, format: :id, full: false)
format_reference = label_format_reference(format) format_reference = label_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if from_project if from
"#{from_project.to_reference(target_project, full: full)}#{reference}" "#{from.to_reference(target_project, full: full)}#{reference}"
else else
reference reference
end end
......
...@@ -6,6 +6,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -6,6 +6,8 @@ class MergeRequest < ActiveRecord::Base
include Elastic::MergeRequestsSearch include Elastic::MergeRequestsSearch
include IgnorableColumn include IgnorableColumn
include TimeTrackable include TimeTrackable
include ManualInverseAssociation
include EachBatch
ignore_column :locked_at, ignore_column :locked_at,
:ref_fetched :ref_fetched
...@@ -18,9 +20,28 @@ class MergeRequest < ActiveRecord::Base ...@@ -18,9 +20,28 @@ class MergeRequest < ActiveRecord::Base
belongs_to :merge_user, class_name: "User" belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs has_many :merge_request_diffs
has_one :merge_request_diff, has_one :merge_request_diff,
-> { order('merge_request_diffs.id DESC') }, inverse_of: :merge_request -> { order('merge_request_diffs.id DESC') }, inverse_of: :merge_request
belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff'
manual_inverse_association :latest_merge_request_diff, :merge_request
# This is the same as latest_merge_request_diff unless:
# 1. There are arguments - in which case we might be trying to force-reload.
# 2. This association is already loaded.
# 3. The latest diff does not exist.
#
# The second one in particular is important - MergeRequestDiff#merge_request
# is the inverse of MergeRequest#merge_request_diff, which means it may not be
# the latest diff, because we could have loaded any diff from this particular
# MR. If we haven't already loaded a diff, then it's fine to load the latest.
def merge_request_diff(*args)
fallback = latest_merge_request_diff if args.empty? && !association(:merge_request_diff).loaded?
fallback || super
end
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
...@@ -172,6 +193,22 @@ class MergeRequest < ActiveRecord::Base ...@@ -172,6 +193,22 @@ class MergeRequest < ActiveRecord::Base
where("merge_requests.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection where("merge_requests.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end end
# This is used after project import, to reset the IDs to the correct
# values. It is not intended to be called without having already scoped the
# relation.
def self.set_latest_merge_request_diff_ids!
update = '
latest_merge_request_diff_id = (
SELECT MAX(id)
FROM merge_request_diffs
WHERE merge_requests.id = merge_request_diffs.merge_request_id
)'.squish
self.each_batch do |batch|
batch.update_all(update)
end
end
WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
def self.work_in_progress?(title) def self.work_in_progress?(title)
......
...@@ -2,6 +2,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -2,6 +2,7 @@ class MergeRequestDiff < ActiveRecord::Base
include Sortable include Sortable
include Importable include Importable
include Gitlab::EncodingHelper include Gitlab::EncodingHelper
include ManualInverseAssociation
# Prevent store of diff if commits amount more then 500 # Prevent store of diff if commits amount more then 500
COMMITS_SAFE_SIZE = 100 COMMITS_SAFE_SIZE = 100
...@@ -10,6 +11,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -10,6 +11,8 @@ class MergeRequestDiff < ActiveRecord::Base
VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze
belongs_to :merge_request belongs_to :merge_request
manual_inverse_association :merge_request, :merge_request_diff
has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) } has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) }
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) } has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }
...@@ -194,7 +197,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -194,7 +197,7 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def latest? def latest?
self == merge_request.merge_request_diff self.id == merge_request.latest_merge_request_diff_id
end end
def compare_with(sha) def compare_with(sha)
......
...@@ -166,18 +166,18 @@ class Milestone < ActiveRecord::Base ...@@ -166,18 +166,18 @@ class Milestone < ActiveRecord::Base
# Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-ce%1" # Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-ce%1"
# Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1" # Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1"
# #
def to_reference(from_project = nil, format: :name, full: false) def to_reference(from = nil, format: :name, full: false)
format_reference = milestone_format_reference(format) format_reference = milestone_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}" reference = "#{self.class.reference_prefix}#{format_reference}"
if project if project
"#{project.to_reference(from_project, full: full)}#{reference}" "#{project.to_reference(from, full: full)}#{reference}"
else else
reference reference
end end
end end
def reference_link_text(from_project = nil) def reference_link_text(from = nil)
self.title self.title
end end
......
...@@ -769,10 +769,10 @@ class Project < ActiveRecord::Base ...@@ -769,10 +769,10 @@ class Project < ActiveRecord::Base
end end
end end
def to_human_reference(from_project = nil) def to_human_reference(from = nil)
if cross_namespace_reference?(from_project) if cross_namespace_reference?(from)
name_with_namespace name_with_namespace
elsif cross_project_reference?(from_project) elsif cross_project_reference?(from)
name name
end end
end end
......
...@@ -224,11 +224,7 @@ class Repository ...@@ -224,11 +224,7 @@ class Repository
def branch_exists?(branch_name) def branch_exists?(branch_name)
return false unless raw_repository return false unless raw_repository
@branch_exists_memo ||= Hash.new do |hash, key| branch_names.include?(branch_name)
hash[key] = raw_repository.branch_exists?(key)
end
@branch_exists_memo[branch_name]
end end
def ref_exists?(ref) def ref_exists?(ref)
...@@ -485,6 +481,11 @@ class Repository ...@@ -485,6 +481,11 @@ class Repository
nil nil
end end
# items is an Array like: [[oid, path], [oid1, path1]]
def blobs_at(items)
raw_repository.batch_blobs(items).map { |blob| Blob.decorate(blob, project) }
end
def root_ref def root_ref
if raw_repository if raw_repository
raw_repository.root_ref raw_repository.root_ref
......
...@@ -76,11 +76,11 @@ class Snippet < ActiveRecord::Base ...@@ -76,11 +76,11 @@ class Snippet < ActiveRecord::Base
@link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/) @link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/)
end end
def to_reference(from_project = nil, full: false) def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{id}" reference = "#{self.class.reference_prefix}#{id}"
if project.present? if project.present?
"#{project.to_reference(from_project, full: full)}#{reference}" "#{project.to_reference(from, full: full)}#{reference}"
else else
reference reference
end end
......
...@@ -456,7 +456,7 @@ class User < ActiveRecord::Base ...@@ -456,7 +456,7 @@ class User < ActiveRecord::Base
username username
end end
def to_reference(_from_project = nil, target_project: nil, full: nil) def to_reference(_from = nil, target_project: nil, full: nil)
"#{self.class.reference_prefix}#{username}" "#{self.class.reference_prefix}#{username}"
end end
...@@ -464,6 +464,10 @@ class User < ActiveRecord::Base ...@@ -464,6 +464,10 @@ class User < ActiveRecord::Base
skip_confirmation! if bool skip_confirmation! if bool
end end
def skip_reconfirmation=(bool)
skip_reconfirmation! if bool
end
def generate_reset_token def generate_reset_token
@reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token) @reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token)
......
...@@ -12,6 +12,8 @@ module MergeRequests ...@@ -12,6 +12,8 @@ module MergeRequests
attr_reader :merge_request, :source attr_reader :merge_request, :source
delegate :merge_jid, :state, to: :@merge_request
def execute(merge_request) def execute(merge_request)
if project.merge_requests_ff_only_enabled && !self.is_a?(FfMergeService) if project.merge_requests_ff_only_enabled && !self.is_a?(FfMergeService)
FfMergeService.new(project, current_user, params).execute(merge_request) FfMergeService.new(project, current_user, params).execute(merge_request)
...@@ -29,6 +31,7 @@ module MergeRequests ...@@ -29,6 +31,7 @@ module MergeRequests
success success
end end
end end
log_info("Merge process finished on JID #{merge_jid} with state #{state}")
rescue MergeError => e rescue MergeError => e
handle_merge_error(log_message: e.message, save_message_on_model: true) handle_merge_error(log_message: e.message, save_message_on_model: true)
end end
...@@ -73,7 +76,9 @@ module MergeRequests ...@@ -73,7 +76,9 @@ module MergeRequests
def commit def commit
message = params[:commit_message] || merge_request.merge_commit_message message = params[:commit_message] || merge_request.merge_commit_message
log_info("Git merge started on JID #{merge_jid}")
commit_id = repository.merge(current_user, source, merge_request, message) commit_id = repository.merge(current_user, source, merge_request, message)
log_info("Git merge finished on JID #{merge_jid} commit #{commit_id}")
raise MergeError, 'Conflicts detected during merge' unless commit_id raise MergeError, 'Conflicts detected during merge' unless commit_id
...@@ -87,7 +92,9 @@ module MergeRequests ...@@ -87,7 +92,9 @@ module MergeRequests
end end
def after_merge def after_merge
log_info("Post merge started on JID #{merge_jid} with state #{state}")
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request) MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
log_info("Post merge finished on JID #{merge_jid} with state #{state}")
if delete_source_branch? if delete_source_branch?
DeleteBranchService.new(@merge_request.source_project, branch_deletion_user) DeleteBranchService.new(@merge_request.source_project, branch_deletion_user)
...@@ -116,6 +123,11 @@ module MergeRequests ...@@ -116,6 +123,11 @@ module MergeRequests
@merge_request.update(merge_error: log_message) if save_message_on_model @merge_request.update(merge_error: log_message) if save_message_on_model
end end
def log_info(message)
@logger ||= Rails.logger
@logger.info("#{merge_request_info} - #{message}")
end
def merge_request_info def merge_request_info
merge_request.to_reference(full: true) merge_request.to_reference(full: true)
end end
......
...@@ -36,7 +36,7 @@ module MergeRequests ...@@ -36,7 +36,7 @@ module MergeRequests
# target branch manually # target branch manually
def close_merge_requests def close_merge_requests
commit_ids = @commits.map(&:id) commit_ids = @commits.map(&:id)
merge_requests = @project.merge_requests.preload(:merge_request_diff).opened.where(target_branch: @branch_name).to_a merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
merge_requests = merge_requests.select(&:diff_head_commit) merge_requests = merge_requests.select(&:diff_head_commit)
merge_requests = merge_requests.select do |merge_request| merge_requests = merge_requests.select do |merge_request|
......
...@@ -8,14 +8,14 @@ module Milestones ...@@ -8,14 +8,14 @@ module Milestones
check_project_milestone!(milestone) check_project_milestone!(milestone)
Milestone.transaction do Milestone.transaction do
# Destroy all milestones with same title across projects
destroy_old_milestones(milestone)
group_milestone = clone_project_milestone(milestone) group_milestone = clone_project_milestone(milestone)
move_children_to_group_milestone(group_milestone) move_children_to_group_milestone(group_milestone)
# Just to be safe # Destroy all milestones with same title across projects
destroy_old_milestones(milestone)
# Rollback if milestone is not valid
unless group_milestone.valid? unless group_milestone.valid?
raise_error(group_milestone.errors.full_messages.to_sentence) raise_error(group_milestone.errors.full_messages.to_sentence)
end end
...@@ -37,7 +37,7 @@ module Milestones ...@@ -37,7 +37,7 @@ module Milestones
end end
def move_children_to_group_milestone(group_milestone) def move_children_to_group_milestone(group_milestone)
milestone_ids_for_merge(group_milestone).in_groups_of(100) do |milestone_ids| milestone_ids_for_merge(group_milestone).in_groups_of(100, false) do |milestone_ids|
update_children(group_milestone, milestone_ids) update_children(group_milestone, milestone_ids)
end end
end end
...@@ -51,7 +51,12 @@ module Milestones ...@@ -51,7 +51,12 @@ module Milestones
create_service = CreateService.new(group, current_user, params) create_service = CreateService.new(group, current_user, params)
create_service.execute milestone = create_service.execute
# milestone won't be valid here because of duplicated title
milestone.save(validate: false)
milestone
end end
def update_children(group_milestone, milestone_ids) def update_children(group_milestone, milestone_ids)
...@@ -67,12 +72,12 @@ module Milestones ...@@ -67,12 +72,12 @@ module Milestones
@group ||= parent.group || raise_error('Project does not belong to a group.') @group ||= parent.group || raise_error('Project does not belong to a group.')
end end
def destroy_old_milestones(group_milestone) def destroy_old_milestones(milestone)
Milestone.where(id: milestone_ids_for_merge(group_milestone)).destroy_all Milestone.where(id: milestone_ids_for_merge(milestone)).destroy_all
end end
def group_project_ids def group_project_ids
@group_project_ids ||= group.projects.map(&:id) @group_project_ids ||= group.projects.pluck(:id)
end end
def raise_error(message) def raise_error(message)
......
# The protected branches API still uses the `developers_can_push` and `developers_can_merge` # The branches#protect API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the # flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so # internal data model (separate access levels). The translation code is non-trivial, and so
# lives in this service. # lives in this service.
module ProtectedBranches module ProtectedBranches
class ApiCreateService < BaseService class LegacyApiCreateService < BaseService
def execute def execute
push_access_level = push_access_level =
if params.delete(:developers_can_push) if params.delete(:developers_can_push)
......
# The protected branches API still uses the `developers_can_push` and `developers_can_merge` # The branches#protect API still uses the `developers_can_push` and `developers_can_merge`
# flags for backward compatibility, and so performs translation between that format and the # flags for backward compatibility, and so performs translation between that format and the
# internal data model (separate access levels). The translation code is non-trivial, and so # internal data model (separate access levels). The translation code is non-trivial, and so
# lives in this service. # lives in this service.
module ProtectedBranches module ProtectedBranches
class ApiUpdateService < BaseService class LegacyApiUpdateService < BaseService
def execute(protected_branch) def execute(protected_branch)
@developers_can_push = params.delete(:developers_can_push) @developers_can_push = params.delete(:developers_can_push)
@developers_can_merge = params.delete(:developers_can_merge) @developers_can_merge = params.delete(:developers_can_merge)
......
...@@ -26,11 +26,15 @@ class FileUploader < GitlabUploader ...@@ -26,11 +26,15 @@ class FileUploader < GitlabUploader
# This is used to build Upload paths dynamically based on the model's current # This is used to build Upload paths dynamically based on the model's current
# namespace and path, allowing us to ignore renames or transfers. # namespace and path, allowing us to ignore renames or transfers.
# #
# model - Object that responds to `path_with_namespace` # model - Object that responds to `full_path` and `disk_path`
# #
# Returns a String without a trailing slash # Returns a String without a trailing slash
def self.dynamic_path_segment(model) def self.dynamic_path_segment(project)
File.join(CarrierWave.root, base_dir, model.disk_path) if project.hashed_storage?(:attachments)
File.join(CarrierWave.root, base_dir, project.disk_path)
else
File.join(CarrierWave.root, base_dir, project.full_path)
end
end end
attr_accessor :model attr_accessor :model
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
= render 'shared/milestones_filter', counts: @milestone_states = render 'shared/milestones_filter', counts: @milestone_states
.nav-controls .nav-controls
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestones, @group) - if can?(current_user, :admin_milestones, @group)
= link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new" = link_to "New milestone", new_group_milestone_path(@group), class: "btn btn-new"
......
- breadcrumb_title "Milestones" - breadcrumb_title "Milestones"
- page_title "Milestones" - page_title "Milestones"
- header_title group_title(@group, "Milestones", group_milestones_path(@group))
%h3.page-title %h3.page-title
New Milestone New Milestone
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
.row.prepend-top-default .row.prepend-top-default
.col-md-8 .col-md-8
.documentation-index .documentation-index.wiki
= markdown(@help_index) = markdown(@help_index)
.col-md-4 .col-md-4
.panel.panel-default .panel.panel-default
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; } table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; } img { -ms-interpolation-mode: bicubic; }
.hidden {
display: none !important;
visibility: hidden !important;
}
/* iOS BLUE LINKS */ /* iOS BLUE LINKS */
a[x-apple-data-detectors] { a[x-apple-data-detectors] {
......
...@@ -10,25 +10,23 @@ ...@@ -10,25 +10,23 @@
.md-area .md-area
.md-header .md-header
%ul.nav-links.clearfix %ul.nav-links.clearfix
%li.active %li.md-header-tab.active
%a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 } %a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 }
Write Write
%li %li.md-header-tab
%a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 } %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
Preview Preview
%li.pull-right %li.md-header-toolbar
.toolbar-group = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: "Add bold text" })
= markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: "Add bold text" }) = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: "Add italic text" })
= markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: "Add italic text" }) = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
= markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) = markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
= markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" }) = markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" }) = markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" }) = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
= markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) %button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
.toolbar-group = sprite_icon("screen-full")
%button.toolbar-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
= sprite_icon("screen-full")
.md-write-holder .md-write-holder
= yield = yield
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.js-file-title.file-title-flex-parent .js-file-title.file-title-flex-parent
= render 'projects/blob/header_content', blob: blob = render 'projects/blob/header_content', blob: blob
.file-actions.hidden-xs .file-actions
= render 'projects/blob/viewer_switcher', blob: blob unless blame = render 'projects/blob/viewer_switcher', blob: blob unless blame
.btn-group{ role: "group" }< .btn-group{ role: "group" }<
......
- if current_user - if current_user
= link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do
- if current_user.starred?(@project) - if current_user.starred?(@project)
= icon('star') = sprite_icon('star')
%span.starred= _('Unstar') %span.starred= _('Unstar')
- else - else
= icon('star-o') = sprite_icon('star-o')
%span= s_('StarProject|Star') %span= s_('StarProject|Star')
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
- else - else
= link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: _('You must sign in to star a project') do = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: _('You must sign in to star a project') do
= icon('star') = sprite_icon('star')
#{ s_('StarProject|Star') } #{ s_('StarProject|Star') }
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
......
...@@ -19,14 +19,15 @@ ...@@ -19,14 +19,15 @@
.environments-container .environments-container
- if @deployments.blank? - if @deployments.blank?
.blank-state.blank-state-no-icon .blank-state-row
%h2.blank-state-title .blank-state-center
You don't have any deployments right now. %h2.blank-state-title
%p.blank-state-text You don't have any deployments right now.
Define environments in the deploy stage(s) in %p.blank-state-text
%code .gitlab-ci.yml Define environments in the deploy stage(s) in
to track deployments here. %code .gitlab-ci.yml
= link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success" to track deployments here.
= link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
- else - else
.table-holder .table-holder
.ci-table.environments{ role: 'grid' } .ci-table.environments{ role: 'grid' }
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
.dropdown-menu.dropdown-menu-align-right.hidden-lg .dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul %ul
- if can_update_issue - if can_update_issue
%li= link_to 'Edit', edit_project_issue_path(@project, @issue) %li= link_to 'Edit', edit_project_issue_path(@project, @issue), class: 'issuable-edit'
- unless current_user == @issue.author - unless current_user == @issue.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue)) %li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue))
- if can_update_issue - if can_update_issue
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.sidebar-container .sidebar-container
.blocks-container .blocks-container
.block .block
%strong.prepend-top-10 %strong.inline.prepend-top-8
= @build.name = @build.name
- if can?(current_user, :update_build, @build) && @build.retryable? - if can?(current_user, :update_build, @build) && @build.retryable?
= link_to "Retry", retry_namespace_project_job_path(@project.namespace, @project, @build), class: 'js-retry-button pull-right btn btn-inverted-secondary btn-retry visible-md-block visible-lg-block', method: :post = link_to "Retry", retry_namespace_project_job_path(@project.namespace, @project, @build), class: 'js-retry-button pull-right btn btn-inverted-secondary btn-retry visible-md-block visible-lg-block', method: :post
......
- @no_container = true - @no_container = true
- @sort ||= sort_value_recently_updated - @sort ||= sort_value_recently_updated
- page_title _('TagsPage|Tags') - page_title s_('TagsPage|Tags')
- add_to_breadcrumbs("Repository", project_tree_path(@project)) - add_to_breadcrumbs("Repository", project_tree_path(@project))
.flex-list{ class: container_class } .flex-list{ class: container_class }
......
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
%li %li
If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>. If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
%li %li
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination. The import will time out after #{time_interval_in_words(Gitlab.config.gitlab_shell.git_timeout)}.
For repositories that take longer, use a clone/push combination.
%li %li
To migrate an SVN repository, check out #{link_to "this document", help_page_path('user/project/import/svn')}. To migrate an SVN repository, check out #{link_to "this document", help_page_path('user/project/import/svn')}.
%li %li
......
- show_create = local_assigns.fetch(:show_create, false)
- show_new_branch_form = show_new_repo? && show_create && can?(current_user, :push_code, @project) - show_new_branch_form = show_new_repo? && show_create && can?(current_user, :push_code, @project)
- dropdown_toggle_text = @ref || @project.default_branch - dropdown_toggle_text = @ref || @project.default_branch
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.controls.hidden-xs .controls.hidden-xs
- if can?(current_user, :admin_group, group) - if can?(current_user, :admin_group, group)
= link_to edit_group_path(group), class: "btn" do = link_to edit_group_path(group), class: "btn" do
= icon('cogs') = sprite_icon('settings')
= link_to leave_group_group_members_path(group), data: { confirm: leave_confirmation_message(group) }, method: :delete, class: "btn", title: s_("GroupsTree|Leave this group") do = link_to leave_group_group_members_path(group), data: { confirm: leave_confirmation_message(group) }, method: :delete, class: "btn", title: s_("GroupsTree|Leave this group") do
= icon('sign-out') = icon('sign-out')
......
...@@ -9,7 +9,7 @@ class PipelineScheduleWorker ...@@ -9,7 +9,7 @@ class PipelineScheduleWorker
pipeline = Ci::CreatePipelineService.new(schedule.project, pipeline = Ci::CreatePipelineService.new(schedule.project,
schedule.owner, schedule.owner,
ref: schedule.ref) ref: schedule.ref)
.execute(:schedule, save_on_errors: false, schedule: schedule) .execute(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
schedule.deactivate! unless pipeline.persisted? schedule.deactivate! unless pipeline.persisted?
rescue => e rescue => e
......
---
title: Impersonation no longer gets stuck on password change.
merge_request: 15497
author:
type: fixed
---
title: Add edit button to mobile file view
merge_request: 15199
author: Travis Miller
type: added
---
title: Disables autocomplete in filtered searc
merge_request: 15477
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Label addition/removal are not going to be redacted wrongfully in the API.
merge_request: 15080
author:
type: fixed
---
title: Add inline editing to issues on mobile
merge_request: 15438
author:
type: changed
---
title: Fix gitlab:backup rake for hashed storage based repositories
merge_request: 15400
author:
type: fixed
---
title: Add dropdown sort to group milestones
merge_request: 15230
author: George Andrinopoulos
type: added
---
title: Fix commits page throwing 500 when the multi-file editor was enabled
merge_request: 15502
author:
type: fixed
---
title: Set the default gitlab-shell timeout to 3 hours
merge_request: 15292
author:
type: fixed
---
title: Fix issue where clicking a GPG verification badge would scroll to the top of
the page
merge_request: 15407
author:
type: fixed
---
title: Removed unused rake task, 'rake gitlab:sidekiq:drop_post_receive'
merge_request: 15493
author:
type: fixed
---
title: Fix bitbucket wiki import with hashed storage enabled
merge_request: 15490
author:
type: fixed
--- ---
title: Fix hashed storage with project transfers to another namespace title: Fix blank states using old css
merge_request: merge_request:
author: author:
type: fixed type: fixed
--- ---
title: Make sure NotesActions#noteable returns a Noteable in the update action title: Align retry button with job title with new grid size
merge_request: merge_request:
author: author:
type: fixed type: fixed
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.
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