Commit eb2bcce2 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-08-18

* upstream/master: (114 commits)
  Fixes the blown up emoji problem in notes
  Total Pages should be at least one
  Add specs for pagination Link header
  don't add next page link if current page is out of range
  Don't escape html entities when rich == raw line
  Incorporate RepositoryService.ApplyGitattributes Gitaly RPC
  Add GitalyClient::RepositoryService specs
  use proper object notation and rename isNewRepo to isNewRepoEnabled
  Move workflow/importing/ to user/project/import/
  Prevent horizontal scrolling in new sidebar when collapsed
  Allow all users to delete their account
  Backports EE mirror stuck handling feature (https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2628) to CE
  Use grpc 1.4.5
  Make admin user actions dropdown consistent
  Fixed height of group share dropdown
  Run remove_pages in a run_after_commit block
  Fix project import to group when there are project milestones
  Disable migrations output in migrations helpers
  Remove a wat in NamespacelessProjectDestroyWorker
  Migrate down before each migration unit test
  ...
parents fa09edcf cc543b6c
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-phantomjs-2.1-node-7.1-postgresql-9.6"
.default-cache: &default-cache .default-cache: &default-cache
key: "ruby-233-with-yarn" key: "ruby-233-with-yarn"
...@@ -461,7 +461,6 @@ db:rollback-mysql: ...@@ -461,7 +461,6 @@ db:rollback-mysql:
variables: variables:
SIZE: "1" SIZE: "1"
SETUP_DB: "false" SETUP_DB: "false"
RAILS_ENV: "development"
script: script:
- git clone https://gitlab.com/gitlab-org/gitlab-test.git - git clone https://gitlab.com/gitlab-org/gitlab-test.git
/home/git/repositories/gitlab-org/gitlab-test.git /home/git/repositories/gitlab-org/gitlab-test.git
...@@ -510,7 +509,7 @@ karma: ...@@ -510,7 +509,7 @@ karma:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache <<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.13-chrome-59.0-node-7.1-postgresql-9.6"
stage: test stage: test
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
......
...@@ -16,7 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql ...@@ -16,7 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.26.0' gem 'rugged', '~> 0.26.0'
gem 'grape-route-helpers', '~> 2.0.0' gem 'grape-route-helpers', '~> 2.1.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
...@@ -79,7 +79,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false ...@@ -79,7 +79,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.19.2' gem 'grape', '~> 1.0'
gem 'grape-entity', '~> 0.6.0' gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
...@@ -240,7 +240,7 @@ gem 'ace-rails-ap', '~> 4.1.0' ...@@ -240,7 +240,7 @@ gem 'ace-rails-ap', '~> 4.1.0'
gem 'mousetrap-rails', '~> 1.4.6' gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding # Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.3' gem 'charlock_holmes', '~> 0.7.5'
# Faster JSON # Faster JSON
gem 'oj', '~> 2.17.4' gem 'oj', '~> 2.17.4'
......
...@@ -123,7 +123,7 @@ GEM ...@@ -123,7 +123,7 @@ GEM
activesupport (>= 4.0.0) activesupport (>= 4.0.0)
mime-types (>= 1.16) mime-types (>= 1.16)
cause (0.1) cause (0.1)
charlock_holmes (0.7.3) charlock_holmes (0.7.5)
chronic (0.10.2) chronic (0.10.2)
chronic_duration (0.10.6) chronic_duration (0.10.6)
numerizer (~> 0.1.1) numerizer (~> 0.1.1)
...@@ -365,12 +365,9 @@ GEM ...@@ -365,12 +365,9 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (0.19.2) grape (1.0.0)
activesupport activesupport
builder builder
hashie (>= 2.1.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
rack (>= 1.3.0) rack (>= 1.3.0)
rack-accept rack-accept
...@@ -378,11 +375,11 @@ GEM ...@@ -378,11 +375,11 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-route-helpers (2.0.0) grape-route-helpers (2.1.0)
activesupport activesupport
grape (~> 0.16, >= 0.16.0) grape (>= 0.16.0)
rake rake
grpc (1.4.0) grpc (1.4.5)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
gssapi (1.2.0) gssapi (1.2.0)
...@@ -400,7 +397,7 @@ GEM ...@@ -400,7 +397,7 @@ GEM
thor thor
tilt tilt
hashdiff (0.3.4) hashdiff (0.3.4)
hashie (3.5.5) hashie (3.5.6)
hashie-forbidden_attributes (0.1.1) hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0) hashie (>= 3.0)
health_check (2.6.0) health_check (2.6.0)
...@@ -1014,7 +1011,7 @@ DEPENDENCIES ...@@ -1014,7 +1011,7 @@ DEPENDENCIES
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 1.1) carrierwave (~> 1.1)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2) chronic (~> 0.10.2)
chronic_duration (~> 0.10.6) chronic_duration (~> 0.10.6)
concurrent-ruby (~> 1.0.5) concurrent-ruby (~> 1.0.5)
...@@ -1068,10 +1065,14 @@ DEPENDENCIES ...@@ -1068,10 +1065,14 @@ DEPENDENCIES
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
gpgme gpgme
grape (~> 0.19.2) grape (~> 1.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
<<<<<<< HEAD
grape-route-helpers (~> 2.0.0) grape-route-helpers (~> 2.0.0)
gssapi gssapi
=======
grape-route-helpers (~> 2.1.0)
>>>>>>> upstream/master
haml_lint (~> 0.26.0) haml_lint (~> 0.26.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
hashie-forbidden_attributes hashie-forbidden_attributes
......
...@@ -141,18 +141,22 @@ the stable branch are: ...@@ -141,18 +141,22 @@ the stable branch are:
* Fixes for security issues * Fixes for security issues
* New or updated translations (as long as they do not touch application code) * New or updated translations (as long as they do not touch application code)
Any merge requests cherry-picked into the stable branch for a previous release
will also be picked into the latest stable branch. These fixes will be shipped
in the next RC for that release if it is before the 22nd. If the fixes are are
completed on or after the 22nd, they will be shipped in a patch for that
release.
During the feature freeze all merge requests that are meant to go into the upcoming During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set, so that release managers can find and pick them. ~"Pick into Stable" set, so that release managers can find and pick them.
Merge requests without a milestone and this label will Merge requests without a milestone and this label will
not be merged into any stable branches. not be merged into any stable branches.
Fixes marked like this will be shipped in the next RC for that release. Once
the final RC has been prepared ready for release on the 22nd, further fixes
marked ~"Pick into Stable" will go into a patch for that release.
If a merge request is to be picked into more than one release it will also need
the ~"Pick into Backports" label set to remind the release manager to change
the milestone after cherry-picking. As before, it should still have the
~"Pick into Stable" label and the milestone of the highest release it will be
picked into.
### Asking for an exception ### Asking for an exception
If you think a merge request should go into an RC or patch even though it does not meet these requirements, If you think a merge request should go into an RC or patch even though it does not meet these requirements,
......
...@@ -17,7 +17,7 @@ window.CommitsList = (function() { ...@@ -17,7 +17,7 @@ window.CommitsList = (function() {
} }
}); });
Pager.init(limit, false, false, this.processCommits); Pager.init(parseInt(limit, 10), false, false, this.processCommits);
this.content = $("#commits-list"); this.content = $("#commits-list");
this.searchField = $("#commits-search"); this.searchField = $("#commits-search");
......
...@@ -451,7 +451,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -451,7 +451,7 @@ import initGroupAnalytics from './init_group_analytics';
case 'projects:tree:show': case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
if (UserFeatureHelper.isNewRepo()) break; if (UserFeatureHelper.isNewRepoEnabled()) break;
new TreeView(); new TreeView();
new BlobViewer(); new BlobViewer();
...@@ -479,7 +479,7 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -479,7 +479,7 @@ import initGroupAnalytics from './init_group_analytics';
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:blob:show': case 'projects:blob:show':
if (UserFeatureHelper.isNewRepo()) break; if (UserFeatureHelper.isNewRepoEnabled()) break;
new BlobViewer(); new BlobViewer();
initBlob(); initBlob();
break; break;
......
...@@ -15,6 +15,10 @@ export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; }; ...@@ -15,6 +15,10 @@ export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; };
export const slope = (a, b) => (b.y - a.y) / (b.x - a.x); export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
let headerHeight = 50;
export const getHeaderHeight = () => headerHeight;
export const canShowActiveSubItems = (el) => { export const canShowActiveSubItems = (el) => {
const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md'; const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md';
...@@ -74,7 +78,7 @@ export const moveSubItemsToPosition = (el, subItems) => { ...@@ -74,7 +78,7 @@ export const moveSubItemsToPosition = (el, subItems) => {
const isAbove = top < boundingRect.top; const isAbove = top < boundingRect.top;
subItems.classList.add('fly-out-list'); subItems.classList.add('fly-out-list');
subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; // eslint-disable-line no-param-reassign subItems.style.transform = `translate3d(0, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign
const subItemsRect = subItems.getBoundingClientRect(); const subItemsRect = subItems.getBoundingClientRect();
...@@ -153,6 +157,8 @@ export default () => { ...@@ -153,6 +157,8 @@ export default () => {
}, getHideSubItemsInterval()); }, getHideSubItemsInterval());
}); });
headerHeight = document.querySelector('.nav-sidebar').offsetTop;
items.forEach((el) => { items.forEach((el) => {
const subItems = el.querySelector('.sidebar-sub-level-items'); const subItems = el.querySelector('.sidebar-sub-level-items');
......
...@@ -111,8 +111,7 @@ window.GroupsSelect = (function() { ...@@ -111,8 +111,7 @@ window.GroupsSelect = (function() {
}; };
GroupsSelect.prototype.forceOverflow = function (e) { GroupsSelect.prototype.forceOverflow = function (e) {
const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight; this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight)}px`;
this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`;
}; };
GroupsSelect.PER_PAGE = 20; GroupsSelect.PER_PAGE = 20;
......
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
function isNewRepo() { export default {
isNewRepoEnabled() {
return Cookies.get('new_repo') === 'true'; return Cookies.get('new_repo') === 'true';
} },
const UserFeatureHelper = {
isNewRepo,
}; };
export default UserFeatureHelper;
export const isSticky = (el, scrollY, stickyTop) => { export const isSticky = (el, scrollY, stickyTop) => {
const top = el.offsetTop - scrollY; const top = el.offsetTop - scrollY;
if (top === stickyTop) { if (top <= stickyTop) {
el.classList.add('is-stuck'); el.classList.add('is-stuck');
} else { } else {
el.classList.remove('is-stuck'); el.classList.remove('is-stuck');
......
...@@ -142,6 +142,7 @@ import './smart_interval'; ...@@ -142,6 +142,7 @@ import './smart_interval';
import './star'; import './star';
import './subscription'; import './subscription';
import './subscription_select'; import './subscription_select';
<<<<<<< HEAD
// EE-only scripts // EE-only scripts
import './admin_email_select'; import './admin_email_select';
...@@ -151,6 +152,8 @@ import './ee_trial_banner'; ...@@ -151,6 +152,8 @@ import './ee_trial_banner';
import './ldap_groups_select'; import './ldap_groups_select';
import './path_locks'; import './path_locks';
import './weight_select'; import './weight_select';
=======
>>>>>>> upstream/master
import './dispatcher'; import './dispatcher';
......
...@@ -43,10 +43,12 @@ export default class NewNavSidebar { ...@@ -43,10 +43,12 @@ export default class NewNavSidebar {
} }
toggleCollapsedSidebar(collapsed) { toggleCollapsedSidebar(collapsed) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed); const breakpoint = bp.getBreakpointSize();
if (this.$sidebar.length) { if (this.$sidebar.length) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
this.$page.toggleClass('page-with-new-sidebar', !collapsed); this.$page.toggleClass('page-with-new-sidebar', !collapsed);
this.$page.toggleClass('page-with-icon-sidebar', collapsed); this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
} }
NewNavSidebar.setCollapsedCookie(collapsed); NewNavSidebar.setCollapsedCookie(collapsed);
} }
......
...@@ -71,7 +71,7 @@ export default { ...@@ -71,7 +71,7 @@ export default {
/> />
<div v-if="!isConfidential" class="no-value confidential-value"> <div v-if="!isConfidential" class="no-value confidential-value">
<i class="fa fa-eye is-not-confidential"></i> <i class="fa fa-eye is-not-confidential"></i>
None Not confidential
</div> </div>
<div v-else class="value confidential-value hide-collapsed"> <div v-else class="value confidential-value hide-collapsed">
<i aria-hidden="true" data-hidden="true" class="fa fa-eye-slash is-confidential"></i> <i aria-hidden="true" data-hidden="true" class="fa fa-eye-slash is-confidential"></i>
......
...@@ -51,9 +51,13 @@ export default { ...@@ -51,9 +51,13 @@ export default {
class="modal popup-dialog" class="modal popup-dialog"
role="dialog" role="dialog"
tabindex="-1"> tabindex="-1">
<<<<<<< HEAD
<div <div
class="modal-dialog" class="modal-dialog"
role="document"> role="document">
=======
<div class="modal-dialog" role="document">
>>>>>>> upstream/master
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" <button type="button"
...@@ -74,8 +78,12 @@ export default { ...@@ -74,8 +78,12 @@ export default {
@click="emitSubmit(false)"> @click="emitSubmit(false)">
{{closeButtonLabel}} {{closeButtonLabel}}
</button> </button>
<<<<<<< HEAD
<button <button
type="button" type="button"
=======
<button type="button"
>>>>>>> upstream/master
class="btn" class="btn"
:class="btnKindClass" :class="btnKindClass"
@click="emitSubmit(true)"> @click="emitSubmit(true)">
......
...@@ -743,6 +743,11 @@ ...@@ -743,6 +743,11 @@
border-radius: 0; border-radius: 0;
padding: 8px 16px; padding: 8px 16px;
// make sure the text color is not overriden
&.text-danger {
@extend .text-danger;
}
&.is-focused, &.is-focused,
&:hover, &:hover,
&:active, &:active,
......
...@@ -132,6 +132,8 @@ ul.content-list { ...@@ -132,6 +132,8 @@ ul.content-list {
} }
.controls { .controls {
@include new-style-dropdown;
float: right; float: right;
> .control-text { > .control-text {
......
...@@ -204,6 +204,16 @@ ...@@ -204,6 +204,16 @@
} }
} }
div.avatar {
display: inline-flex;
justify-content: center;
align-items: center;
.center {
line-height: 14px;
}
}
strong { strong {
color: $gl-text-color; color: $gl-text-color;
} }
......
...@@ -161,6 +161,8 @@ ...@@ -161,6 +161,8 @@
} }
.nav-controls { .nav-controls {
@include new-style-dropdown;
display: inline-block; display: inline-block;
float: right; float: right;
text-align: right; text-align: right;
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
background-color: $gray-lightest; background-color: $gray-lightest;
} }
img.js-lazy-loaded { img.js-lazy-loaded,
img.emoji {
min-width: inherit; min-width: inherit;
min-height: inherit; min-height: inherit;
background-color: inherit; background-color: inherit;
......
...@@ -403,6 +403,7 @@ header.navbar-gitlab-new { ...@@ -403,6 +403,7 @@ header.navbar-gitlab-new {
} }
.breadcrumbs-extra { .breadcrumbs-extra {
display: flex;
flex: 0 0 auto; flex: 0 0 auto;
margin-left: auto; margin-left: auto;
} }
......
...@@ -97,13 +97,16 @@ $new-sidebar-collapsed-width: 50px; ...@@ -97,13 +97,16 @@ $new-sidebar-collapsed-width: 50px;
top: $header-height; top: $header-height;
bottom: 0; bottom: 0;
left: 0; left: 0;
overflow: auto;
background-color: $gray-normal; background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color; box-shadow: inset -2px 0 0 $border-color;
transform: translate3d(0, 0, 0);
&.sidebar-icons-only { &.sidebar-icons-only {
width: $new-sidebar-collapsed-width; width: $new-sidebar-collapsed-width;
.nav-sidebar-inner-scroll {
overflow-x: hidden; overflow-x: hidden;
}
.badge, .badge,
.project-title { .project-title {
...@@ -111,7 +114,11 @@ $new-sidebar-collapsed-width: 50px; ...@@ -111,7 +114,11 @@ $new-sidebar-collapsed-width: 50px;
} }
.nav-item-name { .nav-item-name {
opacity: 0; display: none;
}
.sidebar-top-level-items > li > a {
min-height: 44px;
} }
} }
...@@ -176,6 +183,12 @@ $new-sidebar-collapsed-width: 50px; ...@@ -176,6 +183,12 @@ $new-sidebar-collapsed-width: 50px;
} }
} }
.nav-sidebar-inner-scroll {
height: 100%;
width: 100%;
overflow: auto;
}
.with-performance-bar .nav-sidebar { .with-performance-bar .nav-sidebar {
top: $header-height + $performance-bar-height; top: $header-height + $performance-bar-height;
} }
......
...@@ -560,9 +560,13 @@ ...@@ -560,9 +560,13 @@
} }
.diff-files-changed { .diff-files-changed {
.inline-parallel-buttons {
position: relative;
z-index: 1;
}
.commit-stat-summary { .commit-stat-summary {
@include new-style-dropdown; @include new-style-dropdown;
z-index: -1;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
margin-left: -$gl-padding; margin-left: -$gl-padding;
......
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
border: 1px solid $white-normal; border: 1px solid $white-normal;
padding: 5px; padding: 5px;
max-height: calc(100vh - 100px); max-height: calc(100vh - 100px);
max-width: 100%;
} }
.emoji-block { .emoji-block {
...@@ -259,7 +260,7 @@ ...@@ -259,7 +260,7 @@
padding-top: 10px; padding-top: 10px;
} }
&:not(.issue-boards-sidebar):not([data-signed-in]) { &:not(.issue-boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header { .issuable-sidebar-header {
display: none; display: none;
} }
......
...@@ -235,6 +235,8 @@ module ProjectsHelper ...@@ -235,6 +235,8 @@ module ProjectsHelper
# If no limit is applied we'll just issue a COUNT since the result set could # If no limit is applied we'll just issue a COUNT since the result set could
# be too large to load into memory. # be too large to load into memory.
def any_projects?(projects) def any_projects?(projects)
return projects.any? if projects.is_a?(Array)
if projects.limit_value if projects.limit_value
projects.to_a.any? projects.to_a.any?
else else
......
...@@ -9,7 +9,7 @@ module BlobViewer ...@@ -9,7 +9,7 @@ module BlobViewer
end end
def manager_url def manager_url
'https://getcomposer.com/' 'https://getcomposer.org/'
end end
def package_name def package_name
......
...@@ -198,10 +198,7 @@ module Ci ...@@ -198,10 +198,7 @@ module Ci
# * Maximum length is 63 bytes # * Maximum length is 63 bytes
# * First/Last Character is not a hyphen # * First/Last Character is not a hyphen
def ref_slug def ref_slug
ref.to_s Gitlab::Utils.slugify(ref.to_s)
.downcase
.gsub(/[^a-z0-9]/, '-')[0..62]
.gsub(/(\A-+|-+\z)/, '')
end end
# Variables whose value does not depend on environment # Variables whose value does not depend on environment
......
...@@ -383,6 +383,6 @@ class Commit ...@@ -383,6 +383,6 @@ class Commit
end end
def gpg_commit def gpg_commit
@gpg_commit ||= Gitlab::Gpg::Commit.new(self) @gpg_commit ||= Gitlab::Gpg::Commit.for_commit(self)
end end
end end
...@@ -25,6 +25,11 @@ module Referable ...@@ -25,6 +25,11 @@ module Referable
to_reference(from_project) to_reference(from_project)
end end
included do
alias_method :non_referable_inspect, :inspect
alias_method :inspect, :referable_inspect
end
def referable_inspect def referable_inspect
if respond_to?(:id) if respond_to?(:id)
"#<#{self.class.name} id:#{id} #{to_reference(full: true)}>" "#<#{self.class.name} id:#{id} #{to_reference(full: true)}>"
...@@ -33,10 +38,6 @@ module Referable ...@@ -33,10 +38,6 @@ module Referable
end end
end end
def inspect
referable_inspect
end
module ClassMethods module ClassMethods
# The character that prefixes the actual reference identifier # The character that prefixes the actual reference identifier
# #
......
...@@ -18,4 +18,8 @@ class GpgSignature < ActiveRecord::Base ...@@ -18,4 +18,8 @@ class GpgSignature < ActiveRecord::Base
def commit def commit
project.commit(commit_sha) project.commit(commit_sha)
end end
def gpg_commit
Gitlab::Gpg::Commit.new(project, commit_sha)
end
end end
...@@ -63,7 +63,7 @@ class Project < ActiveRecord::Base ...@@ -63,7 +63,7 @@ class Project < ActiveRecord::Base
end end
before_destroy :remove_private_deploy_keys before_destroy :remove_private_deploy_keys
after_destroy :remove_pages after_destroy -> { run_after_commit { remove_pages } }
# update visibility_level of forks # update visibility_level of forks
after_update :update_forks_visibility_level after_update :update_forks_visibility_level
...@@ -1237,6 +1237,9 @@ class Project < ActiveRecord::Base ...@@ -1237,6 +1237,9 @@ class Project < ActiveRecord::Base
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal? # TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
def remove_pages def remove_pages
# Projects with a missing namespace cannot have their pages removed
return unless namespace
::Projects::UpdatePagesConfigurationService.new(self).execute ::Projects::UpdatePagesConfigurationService.new(self).execute
# 1. We rename pages to temporary directory # 1. We rename pages to temporary directory
...@@ -1295,12 +1298,16 @@ class Project < ActiveRecord::Base ...@@ -1295,12 +1298,16 @@ class Project < ActiveRecord::Base
status.zero? status.zero?
end end
def full_path_slug
Gitlab::Utils.slugify(full_path.to_s)
end
def predefined_variables def predefined_variables
[ [
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true }, { key: 'CI_PROJECT_ID', value: id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: path, public: true }, { key: 'CI_PROJECT_NAME', value: path, public: true },
{ key: 'CI_PROJECT_PATH', value: full_path, public: true }, { key: 'CI_PROJECT_PATH', value: full_path, public: true },
{ key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true }, { key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true } { key: 'CI_PROJECT_URL', value: web_url, public: true }
] ]
......
...@@ -50,11 +50,6 @@ class User < ActiveRecord::Base ...@@ -50,11 +50,6 @@ class User < ActiveRecord::Base
devise :lockable, :recoverable, :rememberable, :trackable, devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable :validatable, :omniauthable, :confirmable, :registerable
# devise overrides #inspect, so we manually use the Referable one
def inspect
referable_inspect
end
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour # to limit database writes to at most once every hour
def update_tracked_fields!(request) def update_tracked_fields!(request)
......
...@@ -95,8 +95,19 @@ class GitPushService < BaseService ...@@ -95,8 +95,19 @@ class GitPushService < BaseService
end end
def update_signatures def update_signatures
@push_commits.each do |commit| commit_shas = @push_commits.last(PROCESS_COMMIT_LIMIT).map(&:sha)
CreateGpgSignatureWorker.perform_async(commit.sha, @project.id)
return if commit_shas.empty?
shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
commit_shas -= shas_with_cached_signatures
return if commit_shas.empty?
commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
commit_shas.each do |sha|
CreateGpgSignatureWorker.perform_async(sha, project.id)
end end
end end
......
...@@ -25,7 +25,6 @@ module MergeRequests ...@@ -25,7 +25,6 @@ module MergeRequests
end end
def after_create(issuable) def after_create(issuable)
event_service.open_mr(issuable, current_user)
todo_service.new_merge_request(issuable, current_user) todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user) issuable.cache_merge_request_closes_issues!(current_user)
update_merge_requests_head_pipeline(issuable) update_merge_requests_head_pipeline(issuable)
......
...@@ -46,9 +46,10 @@ ...@@ -46,9 +46,10 @@
%li %li
= link_to 'Remove user', admin_user_path(user), = link_to 'Remove user', admin_user_path(user),
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" },
class: 'text-danger',
method: :delete method: :delete
%li %li
= link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true), = link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true),
data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" }, data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" },
class: 'btn btn-remove btn-block', class: 'text-danger',
method: :delete method: :delete
...@@ -189,7 +189,7 @@ ...@@ -189,7 +189,7 @@
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu %ul.dropdown-menu
%li %li
%a Sort by date = link_to 'Sort by date', '#'
= link_to 'New issue', '#', class: 'btn btn-new btn-inverted' = link_to 'New issue', '#', class: 'btn btn-new btn-inverted'
......
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header .context-header
= link_to admin_root_path, title: 'Admin Overview' do = link_to admin_root_path, title: 'Admin Overview' do
.avatar-container.s40.settings-avatar .avatar-container.s40.settings-avatar
...@@ -12,6 +13,44 @@ ...@@ -12,6 +13,44 @@
%span.nav-item-name %span.nav-item-name
Overview Overview
<<<<<<< HEAD
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles audit_logs)) do
= link_to admin_conversational_development_index_path, title: 'Monitoring' do
.nav-icon-container
= custom_icon('monitoring')
%span.nav-item-name
Monitoring
%ul.sidebar-sub-level-items
= nav_link(controller: :conversational_development_index) do
= link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
%span
ConvDev Index
= nav_link(controller: :system_info) do
= link_to admin_system_info_path, title: 'System Info' do
%span
System Info
= nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path, title: 'Background Jobs' do
%span
Background Jobs
= nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do
%span
Logs
= nav_link(controller: :health_check) do
= link_to admin_health_check_path, title: 'Health Check' do
%span
Health Check
= nav_link(controller: :requests_profiles) do
= link_to admin_requests_profiles_path, title: 'Requests Profiles' do
%span
Requests Profiles
= nav_link path: 'audit_logs#index' do
= link_to admin_audit_logs_path, title: 'Audit Log' do
%span
Audit Log
=======
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do = link_to admin_root_path, title: 'Overview' do
...@@ -42,12 +81,13 @@ ...@@ -42,12 +81,13 @@
%span %span
Cohorts Cohorts
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles audit_logs)) do = nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
= link_to admin_conversational_development_index_path, title: 'Monitoring' do = link_to admin_conversational_development_index_path, title: 'Monitoring' do
.nav-icon-container .nav-icon-container
= custom_icon('monitoring') = custom_icon('monitoring')
%span.nav-item-name %span.nav-item-name
Monitoring Monitoring
>>>>>>> upstream/master
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: :conversational_development_index) do = nav_link(controller: :conversational_development_index) do
...@@ -74,10 +114,6 @@ ...@@ -74,10 +114,6 @@
= link_to admin_requests_profiles_path, title: 'Requests Profiles' do = link_to admin_requests_profiles_path, title: 'Requests Profiles' do
%span %span
Requests Profiles Requests Profiles
= nav_link path: 'audit_logs#index' do
= link_to admin_audit_logs_path, title: 'Audit Log' do
%span
Audit Log
= nav_link(controller: :broadcast_messages) do = nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do = link_to admin_broadcast_messages_path, title: 'Messages' do
...@@ -99,14 +135,7 @@ ...@@ -99,14 +135,7 @@
%span.nav-item-name %span.nav-item-name
Applications Applications
= nav_link(controller: :abuse_reports) do <<<<<<< HEAD
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
.nav-icon-container
= custom_icon('abuse_reports')
%span.nav-item-name
Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :licenses) do = nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License' do = link_to admin_license_path, title: 'License' do
.nav-icon-container .nav-icon-container
...@@ -117,11 +146,17 @@ ...@@ -117,11 +146,17 @@
- if akismet_enabled? - if akismet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
=======
= nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
>>>>>>> upstream/master
.nav-icon-container .nav-icon-container
= custom_icon('spam_logs') = custom_icon('abuse_reports')
%span.nav-item-name %span.nav-item-name
Spam Logs Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
<<<<<<< HEAD
= nav_link(controller: :push_rules) do = nav_link(controller: :push_rules) do
= link_to admin_push_rule_path, title: 'Push Rules' do = link_to admin_push_rule_path, title: 'Push Rules' do
.nav-icon-container .nav-icon-container
...@@ -142,6 +177,22 @@ ...@@ -142,6 +177,22 @@
= custom_icon('key') = custom_icon('key')
%span.nav-item-name %span.nav-item-name
Deploy Keys Deploy Keys
=======
- if akismet_enabled?
= nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do
.nav-icon-container
= custom_icon('spam_logs')
%span.nav-item-name
Spam Logs
>>>>>>> upstream/master
= nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
.nav-icon-container
= custom_icon('key')
%span.nav-item-name
Deploy Keys
= nav_link(controller: :services) do = nav_link(controller: :services) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do = link_to admin_application_settings_services_path, title: 'Service Templates' do
......
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
<<<<<<< HEAD
.context-header .context-header
= link_to group_path(@group), title: @group.name do = link_to group_path(@group), title: @group.name do
.avatar-container.s40.group-avatar .avatar-container.s40.group-avatar
...@@ -44,6 +45,68 @@ ...@@ -44,6 +45,68 @@
= link_to issues_group_path(@group), title: 'List' do = link_to issues_group_path(@group), title: 'List' do
%span %span
List List
=======
.nav-sidebar-inner-scroll
.context-header
= link_to group_path(@group), title: @group.name do
.avatar-container.s40.group-avatar
= image_tag group_icon(@group), class: "avatar s40 avatar-tile"
.group-title
= @group.name
%ul.sidebar-top-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Group overview' do
.nav-icon-container
= custom_icon('project')
%span.nav-item-name
Overview
>>>>>>> upstream/master
%ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: 'Group details' do
%span
Details
= nav_link(path: 'groups#activity') do
= link_to activity_group_path(@group), title: 'Activity' do
%span
Activity
<<<<<<< HEAD
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
.nav-icon-container
= custom_icon('mr_bold')
%span.nav-item-name
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
Merge Requests
%span.badge.count= number_with_delimiter(merge_requests.count)
= nav_link(path: 'group_members#index') do
= link_to group_group_members_path(@group), title: 'Members' do
.nav-icon-container
= custom_icon('members')
%span.nav-item-name
Members
- if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do
= link_to edit_group_path(@group), title: 'Settings' do
=======
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
= link_to issues_group_path(@group), title: 'Issues' do
>>>>>>> upstream/master
.nav-icon-container
= custom_icon('issues')
%span.nav-item-name
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
Issues
%span.badge.count= number_with_delimiter(issues.count)
%ul.sidebar-sub-level-items
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
= link_to issues_group_path(@group), title: 'List' do
%span
List
= nav_link(path: 'labels#index') do = nav_link(path: 'labels#index') do
= link_to group_labels_path(@group), title: 'Labels' do = link_to group_labels_path(@group), title: 'Labels' do
...@@ -70,7 +133,7 @@ ...@@ -70,7 +133,7 @@
%span.nav-item-name %span.nav-item-name
Members Members
- if current_user && can?(current_user, :admin_group, @group) - if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]) do = nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
= link_to edit_group_path(@group), title: 'Settings' do = link_to edit_group_path(@group), title: 'Settings' do
.nav-icon-container .nav-icon-container
= custom_icon('settings') = custom_icon('settings')
...@@ -92,6 +155,10 @@ ...@@ -92,6 +155,10 @@
%span %span
CI / CD CI / CD
<<<<<<< HEAD
= render "groups/ee/settings_nav" = render "groups/ee/settings_nav"
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
=======
= render 'shared/sidebar_toggle_button'
>>>>>>> upstream/master
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header .context-header
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
<<<<<<< HEAD
.nav-icon-container
= custom_icon('profile')
%span.nav-item-name
Profile
= nav_link(controller: [:accounts, :two_factor_auths]) do
= link_to profile_account_path, title: 'Account' do
.nav-icon-container
= custom_icon('account')
%span.nav-item-name
Account
- if current_application_settings.should_check_namespace_plan?
= nav_link(controller: :billings) do
= link_to profile_billings_path, title: 'Billing' do
%span
Billing
- if current_application_settings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do
= link_to applications_profile_path, title: 'Applications' do
=======
.avatar-container.s40.settings-avatar .avatar-container.s40.settings-avatar
= icon('user') = icon('user')
.project-title User Settings .project-title User Settings
%ul.sidebar-top-level-items %ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
>>>>>>> upstream/master
.nav-icon-container .nav-icon-container
= custom_icon('profile') = custom_icon('profile')
%span.nav-item-name %span.nav-item-name
...@@ -17,11 +39,6 @@ ...@@ -17,11 +39,6 @@
= custom_icon('account') = custom_icon('account')
%span.nav-item-name %span.nav-item-name
Account Account
- if current_application_settings.should_check_namespace_plan?
= nav_link(controller: :billings) do
= link_to profile_billings_path, title: 'Billing' do
%span
Billing
- if current_application_settings.user_oauth_applications? - if current_application_settings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do = nav_link(controller: 'oauth/applications') do
= link_to applications_profile_path, title: 'Applications' do = link_to applications_profile_path, title: 'Applications' do
...@@ -61,6 +78,7 @@ ...@@ -61,6 +78,7 @@
%span.nav-item-name %span.nav-item-name
Notifications Notifications
<<<<<<< HEAD
= nav_link(controller: :keys) do = nav_link(controller: :keys) do
= link_to profile_keys_path, title: 'SSH Keys' do = link_to profile_keys_path, title: 'SSH Keys' do
.nav-icon-container .nav-icon-container
...@@ -91,5 +109,31 @@ ...@@ -91,5 +109,31 @@
= custom_icon('pipeline') = custom_icon('pipeline')
%span.nav-item-name %span.nav-item-name
Pipeline quota Pipeline quota
=======
= nav_link(controller: :keys) do
= link_to profile_keys_path, title: 'SSH Keys' do
.nav-icon-container
= custom_icon('key')
%span.nav-item-name
SSH Keys
= nav_link(controller: :gpg_keys) do
= link_to profile_gpg_keys_path, title: 'GPG Keys' do
.nav-icon-container
= custom_icon('key_2')
%span.nav-item-name
GPG Keys
= nav_link(controller: :preferences) do
= link_to profile_preferences_path, title: 'Preferences' do
.nav-icon-container
= custom_icon('preferences')
%span.nav-item-name
Preferences
= nav_link(path: 'profiles#audit_log') do
= link_to audit_log_profile_path, title: 'Authentication log' do
.nav-icon-container
= custom_icon('authentication_log')
%span.nav-item-name
Authentication log
>>>>>>> upstream/master
= render 'shared/sidebar_toggle_button' = render 'shared/sidebar_toggle_button'
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
- can_edit = can?(current_user, :admin_project, @project) - can_edit = can?(current_user, :admin_project, @project)
.context-header .context-header
= link_to project_path(@project), title: @project.name do = link_to project_path(@project), title: @project.name do
...@@ -15,6 +16,106 @@ ...@@ -15,6 +16,106 @@
Overview Overview
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
<<<<<<< HEAD
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_tree_path(@project) do
#{ _('Files') }
= nav_link(controller: [:commit, :commits]) do
= link_to project_commits_path(@project, current_ref) do
#{ _('Commits') }
= nav_link(html_options: {class: branches_tab_class}) do
= link_to project_branches_path(@project) do
#{ _('Branches') }
= nav_link(controller: [:tags, :releases]) do
= link_to project_tags_path(@project) do
#{ _('Tags') }
= nav_link(path: 'graphs#show') do
= link_to project_graph_path(@project, current_ref) do
#{ _('Contributors') }
= nav_link(controller: %w(network)) do
= link_to project_network_path(@project, current_ref) do
#{ s_('ProjectNetworkGraph|Graph') }
= nav_link(controller: :compare) do
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do
#{ _('Compare') }
= nav_link(path: 'graphs#charts') do
= link_to charts_project_graph_path(@project, current_ref) do
#{ _('Charts') }
- if project_nav_tab? :container_registry
= nav_link(controller: %w[projects/registry/repositories]) do
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
.nav-icon-container
= custom_icon('container_registry')
%span.nav-item-name
Registry
- if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
.nav-icon-container
= custom_icon('issues')
%span.nav-item-name
Issues
- if @project.issues_enabled?
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
%ul.sidebar-sub-level-items
= nav_link(controller: :issues) do
= link_to project_issues_path(@project), title: 'Issues' do
%span
List
= nav_link(controller: :boards) do
= link_to project_boards_path(@project), title: 'Boards' do
%span
Boards
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: 'Labels' do
%span
Labels
= nav_link(controller: :milestones) do
= link_to project_milestones_path(@project), title: 'Milestones' do
%span
Milestones
- if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
.nav-icon-container
= custom_icon('mr_bold')
%span.nav-item-name
Merge Requests
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
= link_to project_pipelines_path(@project), title: 'CI / CD', class: 'shortcuts-pipelines' do
.nav-icon-container
= custom_icon('pipeline')
%span.nav-item-name
CI / CD
%ul.sidebar-sub-level-items
- if project_nav_tab? :pipelines
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
%span
Pipelines
- if project_nav_tab? :builds
= nav_link(controller: [:jobs, :artifacts]) do
= link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
=======
= nav_link(path: 'projects#show') do = nav_link(path: 'projects#show') do
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do = link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
%span= _('Details') %span= _('Details')
...@@ -90,13 +191,14 @@ ...@@ -90,13 +191,14 @@
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(controller: :issues) do = nav_link(controller: :issues) do
= link_to project_issues_path(@project), title: 'Issues' do = link_to project_issues_path(@project), title: 'Issues' do
>>>>>>> upstream/master
%span %span
List List
= nav_link(controller: :boards) do = nav_link(controller: :boards) do
= link_to project_boards_path(@project), title: 'Boards' do = link_to project_boards_path(@project), title: 'Board' do
%span %span
Boards Board
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: 'Labels' do = link_to project_labels_path(@project), title: 'Labels' do
......
...@@ -92,8 +92,7 @@ ...@@ -92,8 +92,7 @@
Update username Update username
%hr %hr
- if signup_enabled? .row.prepend-top-default
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar .col-lg-4.profile-settings-sidebar
%h4.prepend-top-0.danger-title %h4.prepend-top-0.danger-title
Remove account Remove account
......
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
Blank Blank
- Gitlab::ProjectTemplate.all.each do |template| - Gitlab::ProjectTemplate.all.each do |template|
.btn .btn
%input{ type: "radio", autocomplete: "off", name: "project_templates", id: template.name } %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
= custom_icon(template.logo) = custom_icon(template.logo)
= template.title = template.title
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed{ class: ("diff-files-changed-merge-request" if merge_request) } .content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed{ class: ("diff-files-changed-merge-request" if merge_request) }
.files-changed-inner .files-changed-inner
.inline-parallel-buttons .inline-parallel-buttons.hidden-xs.hidden-sm
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? } - if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default' = link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
- if show_whitespace_toggle - if show_whitespace_toggle
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
%strong.cgreen #{sum_added_lines} additions %strong.cgreen #{sum_added_lines} additions
and and
%strong.cred #{sum_removed_lines} deletions %strong.cred #{sum_removed_lines} deletions
.diff-stats-additions-deletions-collapsed.pull-right{ "aria-hidden": "true", "aria-describedby": "diff-stats" } .diff-stats-additions-deletions-collapsed.pull-right.hidden-xs.hidden-sm{ "aria-hidden": "true", "aria-describedby": "diff-stats" }
%strong.cgreen< %strong.cgreen<
+#{sum_added_lines} +#{sum_added_lines}
%strong.cred< %strong.cred<
......
- if @can_bulk_update - if @can_bulk_update
= button_tag "Edit merge requests", class: "btn js-bulk-update-toggle" = button_tag "Edit merge requests", class: "btn append-right-10 js-bulk-update-toggle"
- if merge_project - if merge_project
= link_to new_merge_request_path, class: "btn btn-new", title: "New merge request" do = link_to new_merge_request_path, class: "btn btn-new", title: "New merge request" do
New merge request New merge request
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
.form-group .form-group
= f.label :template_project, class: 'label-light' do = f.label :template_project, class: 'label-light' do
Create from template Create from template
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'} = link_to icon('question-circle'), help_page_path("gitlab-basics/create-project"), target: '_blank', aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
%div %div
= render 'project_templates', f: f = render 'project_templates', f: f
.second-column .second-column
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination. The import will time out after 15 minutes. 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')}.
<<<<<<< HEAD
%li %li
The Git LFS objects will <strong>not</strong> be imported. The Git LFS objects will <strong>not</strong> be imported.
%li %li
...@@ -30,3 +31,5 @@ ...@@ -30,3 +31,5 @@
repository every hour. The Git LFS objects will not be synced. repository every hour. The Git LFS objects will not be synced.
= f.hidden_field :mirror_user_id, value: current_user.id = f.hidden_field :mirror_user_id, value: current_user.id
=======
>>>>>>> upstream/master
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
.col-sm-6.milestone-actions .col-sm-6.milestone-actions
- if can?(current_user, :admin_milestones, @group) - if can?(current_user, :admin_milestones, @group)
- if milestone.is_group_milestone? - if milestone.is_group_milestone?
= link_to edit_group_milestone_path(@group, milestone.id), class: "btn btn-xs btn-grouped" do = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do
Edit Edit
\ \
- if milestone.closed? - if milestone.closed?
......
- affix_offset = local_assigns.fetch(:affix_offset, "50") - affix_offset = local_assigns.fetch(:affix_offset, "50")
- project = local_assigns[:project] - project = local_assigns[:project]
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar.milestone-sidebar .issuable-sidebar.milestone-sidebar
.block.milestone-progress.issuable-sidebar-header .block.milestone-progress.issuable-sidebar-header
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" } %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
.pull-right .pull-right
- if can?(current_user, :admin_milestones, group) - if can?(current_user, :admin_milestones, group)
- if milestone.is_group_milestone? - if milestone.is_group_milestone?
= link_to edit_group_milestone_path(group, milestone.iid), class: "btn btn btn-grouped" do = link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
Edit Edit
- if milestone.active? - if milestone.active?
= link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
......
...@@ -4,13 +4,9 @@ class CreateGpgSignatureWorker ...@@ -4,13 +4,9 @@ class CreateGpgSignatureWorker
def perform(commit_sha, project_id) def perform(commit_sha, project_id)
project = Project.find_by(id: project_id) project = Project.find_by(id: project_id)
return unless project return unless project
commit = project.commit(commit_sha) # This calculates and caches the signature in the database
Gitlab::Gpg::Commit.new(project, commit_sha).signature
return unless commit
commit.signature
end end
end end
...@@ -24,10 +24,6 @@ class NamespacelessProjectDestroyWorker ...@@ -24,10 +24,6 @@ class NamespacelessProjectDestroyWorker
unlink_fork(project) if project.forked? unlink_fork(project) if project.forked?
# Override Project#remove_pages for this instance so it doesn't do anything
def project.remove_pages
end
project.destroy! project.destroy!
end end
......
...@@ -9,8 +9,13 @@ class StuckImportJobsWorker ...@@ -9,8 +9,13 @@ class StuckImportJobsWorker
projects_with_jid_count = mark_projects_with_jid_as_failed! projects_with_jid_count = mark_projects_with_jid_as_failed!
Gitlab::Metrics.add_event(:stuck_import_jobs, Gitlab::Metrics.add_event(:stuck_import_jobs,
<<<<<<< HEAD
projects_without_jid_count: projects_without_jid_count, projects_without_jid_count: projects_without_jid_count,
projects_with_jid_count: projects_with_jid_count) projects_with_jid_count: projects_with_jid_count)
=======
projects_without_jid_count: projects_without_jid_count,
projects_with_jid_count: projects_with_jid_count)
>>>>>>> upstream/master
end end
private private
......
---
title: Added tests for commits API unauthenticated user and public/private project
merge_request: 13287
author: Jacopo Beschi @jacopo-beschi
---
title: Fix CI_PROJECT_PATH_SLUG slugify
merge_request: 13350
author: Ivan Chernov
---
title: Fix deleting GitLab Pages files when a project is removed
merge_request: 13631
author:
type: fixed
---
title: Don't escape html entities in InlineDiffMarkdownMarker
merge_request:
author:
---
title: allow all users to delete their account
merge_request: 13636
author: Jacopo Beschi @jacopo-beschi
type: changed
---
title: Fix external link to Composer website
merge_request:
author:
type: fixed
---
title: Fix commit list not loading the correct page when scrolling
merge_request:
author:
type: fixed
---
title: Fix inconsistent spacing for edit buttons on issues and merge request page
merge_request:
author:
type: fixed
---
title: Fix API responses when dealing with txt files
merge_request:
author:
---
title: Fix project milestones import when projects belongs to a group
merge_request:
author:
---
title: Improve API pagination headers when no record found
merge_request: 13629
author: Jordan Patterson
type: fixed
---
title: Upgrade grape to 1.0
merge_request:
author:
type: other
...@@ -744,6 +744,9 @@ test: ...@@ -744,6 +744,9 @@ test:
default: default:
path: tmp/tests/repositories/ path: tmp/tests/repositories/
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
failure_count_threshold: 999999
failure_wait_time: 0
storage_timeout: 30
broken: broken:
path: tmp/tests/non-existent-repositories path: tmp/tests/non-existent-repositories
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
......
...@@ -37,12 +37,12 @@ def validate_storages_config ...@@ -37,12 +37,12 @@ def validate_storages_config
storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example") storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example")
end end
%w(failure_count_threshold failure_wait_time failure_reset_time storage_timeout).each do |setting| %w(failure_count_threshold failure_reset_time storage_timeout).each do |setting|
# Falling back to the defaults is fine! # Falling back to the defaults is fine!
next if repository_storage[setting].nil? next if repository_storage[setting].nil?
unless repository_storage[setting].to_f > 0 unless repository_storage[setting].to_f > 0
storage_validation_error("#{setting}, for storage `#{name}` needs to be greater than 0") storage_validation_error("`#{setting}` for storage `#{name}` needs to be greater than 0")
end end
end end
end end
......
module ActiveRecord
class PredicateBuilder
class ArrayHandler
module TypeCasting
def call(attribute, value)
# This is necessary because by default ActiveRecord does not respect
# custom type definitions (like our `ShaAttribute`) when providing an
# array in `where`, like in `where(commit_sha: [sha1, sha2, sha3])`.
model = attribute.relation&.engine
type = model.user_provided_columns[attribute.name] if model
value = value.map { |value| type.type_cast_for_database(value) } if type
super(attribute, value)
end
end
prepend TypeCasting
end
end
end
...@@ -233,6 +233,9 @@ var config = { ...@@ -233,6 +233,9 @@ var config = {
names: ['main', 'locale', 'common', 'webpack_runtime'], names: ['main', 'locale', 'common', 'webpack_runtime'],
}), }),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// copy pre-compiled vendor libraries verbatim // copy pre-compiled vendor libraries verbatim
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ {
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveDuplicateMrEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
class Event < ActiveRecord::Base
self.table_name = 'events'
end
def up
base_condition = "action = 1 AND target_type = 'MergeRequest' AND created_at > '2017-08-13'"
Event.select('target_id, count(*)')
.where(base_condition)
.group('target_id').having('count(*) > 1').each do |event|
duplicates = Event.where("#{base_condition} AND target_id = #{event.target_id}").pluck(:id)
duplicates.shift
Event.where(id: duplicates).delete_all
end
end
def down
end
end
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170811203342) do ActiveRecord::Schema.define(version: 20170811203342) do
=======
ActiveRecord::Schema.define(version: 20170815060945) do
>>>>>>> upstream/master
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
......
...@@ -32,6 +32,7 @@ Shortcuts to GitLab's most visited docs: ...@@ -32,6 +32,7 @@ Shortcuts to GitLab's most visited docs:
- [User documentation](user/index.md) - [User documentation](user/index.md)
- [Administrator documentation](#administrator-documentation) - [Administrator documentation](#administrator-documentation)
- [Technical Articles](articles/index.md)
## Getting started with GitLab ## Getting started with GitLab
......
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
> **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** admin guide || > **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** admin guide ||
> **Level:** intermediary || > **Level:** intermediary ||
> **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) || > **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) ||
> **Publication date:** 2017/05/03 > **Publication date:** 2017-05-03
## Introduction ## Introduction
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
> **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** user guide || > **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** user guide ||
> **Level:** beginner || > **Level:** beginner ||
> **Author:** [Sean Packham](https://gitlab.com/SeanPackham) || > **Author:** [Sean Packham](https://gitlab.com/SeanPackham) ||
> **Publication date:** 2017/05/15 > **Publication date:** 2017-05-15
To begin contributing to GitLab projects To begin contributing to GitLab projects
you will need to install the Git client on your computer. you will need to install the Git client on your computer.
......
This diff is collapsed.
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
> **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial || > **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
> **Level:** intermediary || > **Level:** intermediary ||
> **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) || > **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) ||
> **Publication date:** 2016/06/28 > **Publication date:** 2016-06-28
## Introduction ## Introduction
......
...@@ -113,7 +113,8 @@ Here is an collection of tutorials and guides on setting up your CI pipeline. ...@@ -113,7 +113,8 @@ Here is an collection of tutorials and guides on setting up your CI pipeline.
- [Phoenix](examples/test-phoenix-application.md) - [Phoenix](examples/test-phoenix-application.md)
- [Run PHP Composer & NPM scripts then deploy them to a staging server](examples/deployment/composer-npm-deploy.md) - [Run PHP Composer & NPM scripts then deploy them to a staging server](examples/deployment/composer-npm-deploy.md)
- [Analyze code quality with the Code Climate CLI](examples/code_climate.md) - [Analyze code quality with the Code Climate CLI](examples/code_climate.md)
- **Blog posts** - **Articles**
- [How to deploy Maven projects to Artifactory with GitLab CI/CD](../articles/artifactory_and_gitlab/index.md)
- [Automated Debian packaging](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) - [Automated Debian packaging](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/)
- [Spring boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) - [Spring boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
- [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
......
...@@ -117,7 +117,7 @@ following these rules: ...@@ -117,7 +117,7 @@ following these rules:
created (requires GitLab Runner v1.1.0 or higher) created (requires GitLab Runner v1.1.0 or higher)
To override the default behavior, you can To override the default behavior, you can
[specify a service alias](#available-settings-for-services-entry). [specify a service alias](#available-settings-for-services).
## Define `image` and `services` from `.gitlab-ci.yml` ## Define `image` and `services` from `.gitlab-ci.yml`
...@@ -183,8 +183,7 @@ test: ...@@ -183,8 +183,7 @@ test:
## Extended Docker configuration options ## Extended Docker configuration options
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
When configuring the `image` or `services` entries, you can use a string or a map as When configuring the `image` or `services` entries, you can use a string or a map as
options: options:
...@@ -221,28 +220,29 @@ For example, the following two definitions are equal: ...@@ -221,28 +220,29 @@ For example, the following two definitions are equal:
### Available settings for `image` ### Available settings for `image`
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
| Setting | Required | Description | | Setting | Required | GitLab version | Description |
|------------|----------|-------------| |------------|----------|----------------| ----------- |
| `name` | yes, when used with any other option | Full name of the image that should be used. It should contain the Registry part if needed. | | `name` | yes, when used with any other option | 9.4 |Full name of the image that should be used. It should contain the Registry part if needed. |
| `entrypoint` | no | Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. | | `entrypoint` | no | 9.4 |Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. |
### Available settings for `services` ### Available settings for `services`
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
| Setting | Required | Description | | Setting | Required | GitLab version | Description |
|------------|----------|-------------| |------------|----------|----------------| ----------- |
| `name` | yes, when used with any other option | Full name of the image that should be used. It should contain the Registry part if needed. | | `name` | yes, when used with any other option | 9.4 | Full name of the image that should be used. It should contain the Registry part if needed. |
| `entrypoint` | no | Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. | | `entrypoint` | no | 9.4 |Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. |
| `command` | no | Command or script that should be used as the container's command. It will be translated to arguments passed to Docker after the image's name. The syntax is similar to [`Dockerfile`'s `CMD`][cmd] directive, where each shell token is a separate string in the array. | | `command` | no | 9.4 |Command or script that should be used as the container's command. It will be translated to arguments passed to Docker after the image's name. The syntax is similar to [`Dockerfile`'s `CMD`][cmd] directive, where each shell token is a separate string in the array. |
| `alias` | no | Additional alias that can be used to access the service from the job's container. Read [Accessing the services](#accessing-the-services) for more information. | | `alias` | no | 9.4 |Additional alias that can be used to access the service from the job's container. Read [Accessing the services](#accessing-the-services) for more information. |
### Starting multiple services from the same image ### Starting multiple services from the same image
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Before the new extended Docker configuration options, the following configuration Before the new extended Docker configuration options, the following configuration
would not work properly: would not work properly:
...@@ -274,6 +274,9 @@ in `.gitlab-ci.yml` file. ...@@ -274,6 +274,9 @@ in `.gitlab-ci.yml` file.
### Setting a command for the service ### Setting a command for the service
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Let's assume you have a `super/sql:latest` image with some SQL database Let's assume you have a `super/sql:latest` image with some SQL database
inside it and you would like to use it as a service for your job. Let's also inside it and you would like to use it as a service for your job. Let's also
assume that this image doesn't start the database process while starting assume that this image doesn't start the database process while starting
...@@ -313,6 +316,9 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd]. ...@@ -313,6 +316,9 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd].
### Overriding the entrypoint of an image ### Overriding the entrypoint of an image
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Let's assume you have a `super/sql:experimental` image with some SQL database 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 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 want to execute some tests with this database binary. Let's also assume that
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
## Databases ## Databases
- [Merge Request Checklist](database_merge_request_checklist.md)
- [What requires downtime?](what_requires_downtime.md) - [What requires downtime?](what_requires_downtime.md)
- [Adding database indexes](adding_database_indexes.md) - [Adding database indexes](adding_database_indexes.md)
- [Post Deployment Migrations](post_deployment_migrations.md) - [Post Deployment Migrations](post_deployment_migrations.md)
...@@ -57,6 +58,9 @@ ...@@ -57,6 +58,9 @@
- [Background Migrations](background_migrations.md) - [Background Migrations](background_migrations.md)
- [Storing SHA1 Hashes As Binary](sha1_as_binary.md) - [Storing SHA1 Hashes As Binary](sha1_as_binary.md)
- [Iterating Tables In Batches](iterating_tables_in_batches.md) - [Iterating Tables In Batches](iterating_tables_in_batches.md)
- [Ordering Table Columns](ordering_table_columns.md)
- [Verifying Database Capabilities](verifying_database_capabilities.md)
- [Hash Indexes](hash_indexes.md)
## i18n ## i18n
......
# Merge Request Checklist
When creating a merge request that performs database related changes (schema
changes, adjusting queries to optimise performance, etc) you should use the
merge request template called "Database Changes". This template contains a
checklist of steps to follow to make sure the changes are up to snuff.
To use the checklist, create a new merge request and click on the "Choose a
template" dropdown, then click "Database Changes".
An example of this checklist can be found at
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463.
The source code of the checklist can be found in at
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20Changes.md
# Hash Indexes
Both PostgreSQL and MySQL support hash indexes besides the regular btree
indexes. Hash indexes however are to be avoided at all costs. While they may
_sometimes_ provide better performance the cost of rehashing can be very high.
More importantly: at least until PostgreSQL 10.0 hash indexes are not
WAL-logged, meaning they are not replicated to any replicas. From the PostgreSQL
documentation:
> Hash index operations are not presently WAL-logged, so hash indexes might need
> to be rebuilt with REINDEX after a database crash if there were unwritten
> changes. Also, changes to hash indexes are not replicated over streaming or
> file-based replication after the initial base backup, so they give wrong
> answers to queries that subsequently use them. For these reasons, hash index
> use is presently discouraged.
RuboCop is configured to register an offence when it detects the use of a hash
index.
Instead of using hash indexes you should use regular btree indexes.
# Ordering Table Columns
Similar to C structures the space of a table is influenced by the order of
columns. This is because the size of columns is aligned depending on the type of
the column. Take the following column order for example:
* id (integer, 4 bytes)
* name (text, variable)
* user_id (integer, 4 bytes)
Integers are aligned to the word size. This means that on a 64 bit platform the
actual size of each column would be: 8 bytes, variable, 8 bytes. This means that
each row will require at least 16 bytes for the two integers, and a variable
amount for the text field. If a table has a few rows this is not an issue, but
once you start storing millions of rows you can save space by using a different
order. For the above example a more ideal column order would be the following:
* id (integer, 4 bytes)
* user_id (integer, 4 bytes)
* name (text, variable)
In this setup the `id` and `user_id` columns can be packed together, which means
we only need 8 bytes to store _both_ of them. This in turn each row will require
8 bytes less of space.
For GitLab we require that columns of new tables are ordered based to use the
least amount of space. An easy way of doing this is to order them based on the
type size in descending order with variable sizes (string and text columns for
example) at the end.
## Type Sizes
While the PostgreSQL docuemntation
(https://www.postgresql.org/docs/current/static/datatype.html) contains plenty
of information we will list the sizes of common types here so it's easier to
look them up. Here "word" refers to the word size, which is 4 bytes for a 32
bits platform and 8 bytes for a 64 bits platform.
| Type | Size | Aligned To |
|:-----------------|:-------------------------------------|:-----------|
| smallint | 2 bytes | 1 word |
| integer | 4 bytes | 1 word |
| bigint | 8 bytes | 8 bytes |
| real | 4 bytes | 1 word |
| double precision | 8 bytes | 8 bytes |
| boolean | 1 byte | not needed |
| text / string | variable, 1 byte plus the data | 1 word |
| bytea | variable, 1 or 4 bytes plus the data | 1 word |
| timestamp | 8 bytes | 8 bytes |
| timestamptz | 8 bytes | 8 bytes |
| date | 4 bytes | 1 word |
A "variable" size means the actual size depends on the value being stored. If
PostgreSQL determines this can be embedded directly into a row it may do so, but
for very large values it will store the data externally and store a pointer (of
1 word in size) in the column. Because of this variable sized columns should
always be at the end of a table.
## Real Example
Let's use the "events" table as an example, which currently has the following
layout:
| Column | Type | Size |
|:------------|:----------------------------|:---------|
| id | integer | 4 bytes |
| target_type | character varying | variable |
| target_id | integer | 4 bytes |
| title | character varying | variable |
| data | text | variable |
| project_id | integer | 4 bytes |
| created_at | timestamp without time zone | 8 bytes |
| updated_at | timestamp without time zone | 8 bytes |
| action | integer | 4 bytes |
| author_id | integer | 4 bytes |
After adding padding to align the columns this would translate to columns being
divided into fixed size chunks as follows:
| Chunk Size | Columns |
|:-----------|:------------------|
| 8 bytes | id |
| variable | target_type |
| 8 bytes | target_id |
| variable | title |
| variable | data |
| 8 bytes | project_id |
| 8 bytes | created_at |
| 8 bytes | updated_at |
| 8 bytes | action, author_id |
This means that excluding the variable sized data we need at least 48 bytes per
row.
We can optimise this by using the following column order instead:
| Column | Type | Size |
|:------------|:----------------------------|:---------|
| created_at | timestamp without time zone | 8 bytes |
| updated_at | timestamp without time zone | 8 bytes |
| id | integer | 4 bytes |
| target_id | integer | 4 bytes |
| project_id | integer | 4 bytes |
| action | integer | 4 bytes |
| author_id | integer | 4 bytes |
| target_type | character varying | variable |
| title | character varying | variable |
| data | text | variable |
This would produce the following chunks:
| Chunk Size | Columns |
|:-----------|:-------------------|
| 8 bytes | created_at |
| 8 bytes | updated_at |
| 8 bytes | id, target_id |
| 8 bytes | project_id, action |
| 8 bytes | author_id |
| variable | target_type |
| variable | title |
| variable | data |
Here we only need 40 bytes per row excluding the variable sized data. 8 bytes
being saved may not sound like much, but for tables as large as the "events"
table it does begin to matter. For example, when storing 80 000 000 rows this
translates to a space saving of at least 610 MB: all by just changing the order
of a few columns.
# Serializing Data # Serializing Data
**Summary:** don't store serialized data in the database, use separate columns **Summary:** don't store serialized data in the database, use separate columns
and/or tables instead. and/or tables instead. This includes storing of comma separated values as a
string.
Rails makes it possible to store serialized data in JSON, YAML or other formats. Rails makes it possible to store serialized data in JSON, YAML or other formats.
Such a field can be defined as follows: Such a field can be defined as follows:
......
...@@ -216,4 +216,30 @@ exact same results. This also means there's no need to add an index on ...@@ -216,4 +216,30 @@ exact same results. This also means there's no need to add an index on
`created_at` to ensure consistent performance as `id` is already indexed by `created_at` to ensure consistent performance as `id` is already indexed by
default. default.
## Use WHERE EXISTS instead of WHERE IN
While `WHERE IN` and `WHERE EXISTS` can be used to produce the same data it is
recommended to use `WHERE EXISTS` whenever possible. While in many cases
PostgreSQL can optimise `WHERE IN` quite well there are also many cases where
`WHERE EXISTS` will perform (much) better.
In Rails you have to use this by creating SQL fragments:
```ruby
Project.where('EXISTS (?)', User.select(1).where('projects.creator_id = users.id AND users.foo = X'))
```
This would then produce a query along the lines of the following:
```sql
SELECT *
FROM projects
WHERE EXISTS (
SELECT 1
FROM users
WHERE projects.creator_id = users.id
AND users.foo = X
)
```
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html [gin-index]: http://www.postgresql.org/docs/current/static/gin.html
# Verifying Database Capabilities
Sometimes certain bits of code may only work on a certain database and/or
version. While we try to avoid such code as much as possible sometimes it is
necessary to add database (version) specific behaviour.
To facilitate this we have the following methods that you can use:
* `Gitlab::Database.postgresql?`: returns `true` if PostgreSQL is being used
* `Gitlab::Database.mysql?`: returns `true` if MySQL is being used
* `Gitlab::Database.version`: returns the PostgreSQL version number as a string
in the format `X.Y.Z`. This method does not work for MySQL
This allows you to write code such as:
```ruby
if Gitlab::Database.postgresql?
if Gitlab::Database.version.to_f >= 9.6
run_really_fast_query
else
run_fast_query
end
else
run_query
end
```
# How to create a project in GitLab # How to create a project in GitLab
>**Notes:**
- For a list of words that are not allowed to be used as project names see the
[reserved names][reserved].
1. In your dashboard, click the green **New project** button or use the plus 1. In your dashboard, click the green **New project** button or use the plus
icon in the upper right corner of the navigation bar. icon in the upper right corner of the navigation bar.
...@@ -25,4 +29,12 @@ ...@@ -25,4 +29,12 @@
1. Click **Create project**. 1. Click **Create project**.
## From a template
To kickstart your development GitLab projects can be started from a template.
For example, one of the templates included is Ruby on Rails. When filling out the
form for new projects, click the 'Ruby on Rails' button. During project creation,
this will import a Ruby on Rails template with GitLab CI preconfigured.
[import it]: ../workflow/importing/README.md [import it]: ../workflow/importing/README.md
[reserved]: ../user/reserved_names.md
...@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed ...@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
# Install Git # Install Git
sudo apt-get install -y git-core sudo apt-get install -y git-core
# Make sure Git is version 2.8.4 or higher # Make sure Git is version 2.13.0 or higher
git --version git --version
Is the system packaged Git too old? Remove it and compile from source. Is the system packaged Git too old? Remove it and compile from source.
......
...@@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab. ...@@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit) - [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
- [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase) - [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase)
- **Articles:** - **Articles:**
- [Numerous _undo_ possibilities in Git](../../articles/numerous_undo_possibilities_in_git/index.md)
- [How to install Git](../../articles/how_to_install_git/index.md) - [How to install Git](../../articles/how_to_install_git/index.md)
- [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) - [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
- [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/) - [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
......
...@@ -57,6 +57,10 @@ By doing so: ...@@ -57,6 +57,10 @@ By doing so:
## Create a new group ## Create a new group
> **Notes:**
- For a list of words that are not allowed to be used as group names see the
[reserved names][reserved].
You can create a group in GitLab from: You can create a group in GitLab from:
1. The Groups page: expand the left menu, click **Groups**, and click the green button **New group**: 1. The Groups page: expand the left menu, click **Groups**, and click the green button **New group**:
...@@ -240,3 +244,7 @@ for the group (GitLab admins only, available in [GitLab Enterprise Edition Start ...@@ -240,3 +244,7 @@ for the group (GitLab admins only, available in [GitLab Enterprise Edition Start
[permissions]: ../permissions.md#permissions [permissions]: ../permissions.md#permissions
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/products/
<<<<<<< HEAD
=======
[reserved]: ../reserved_names.md
>>>>>>> upstream/master
...@@ -83,10 +83,7 @@ structure. ...@@ -83,10 +83,7 @@ structure.
- You need to be an Owner of a group in order to be able to create - You need to be an Owner of a group in order to be able to create
a subgroup. For more information check the [permissions table][permissions]. a subgroup. For more information check the [permissions table][permissions].
- For a list of words that are not allowed to be used as group names see the - For a list of words that are not allowed to be used as group names see the
[`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists: [reserved names][reserved].
- `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
- `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
- `GROUP_ROUTES`: are names that are reserved for all groups or projects.
To create a subgroup: To create a subgroup:
...@@ -175,5 +172,5 @@ Here's a list of what you can't do with subgroups: ...@@ -175,5 +172,5 @@ Here's a list of what you can't do with subgroups:
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772 [ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
[permissions]: ../../permissions.md#group [permissions]: ../../permissions.md#group
[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb [reserved]: ../../reserved_names.md
[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600 [issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
...@@ -5,17 +5,17 @@ particular group or project. If a user is both in a group's project and the ...@@ -5,17 +5,17 @@ particular group or project. If a user is both in a group's project and the
project itself, the highest permission level is used. project itself, the highest permission level is used.
On public and internal projects the Guest role is not enforced. All users will On public and internal projects the Guest role is not enforced. All users will
be able to create issues, leave comments, and pull or download the project code. be able to create issues, leave comments, and clone or download the project code.
When a member leaves the team the all assigned Issues and Merge Requests When a member leaves the team all the assigned [Issues](project/issues/index.md) and [Merge Requests](project/merge_requests/index.md)
will be unassigned automatically. will be unassigned automatically.
GitLab administrators receive all permissions. GitLab [administrators](../README.md#administrator-documentation) receive all permissions.
To add or import a user, you can follow the To add or import a user, you can follow the
[project members documentation](../user/project/members/index.md). [project members documentation](../user/project/members/index.md).
## Project ## Project members permissions
The following table depicts the various user permission levels in a project. The following table depicts the various user permission levels in a project.
...@@ -77,7 +77,58 @@ The following table depicts the various user permission levels in a project. ...@@ -77,7 +77,58 @@ The following table depicts the various user permission levels in a project.
| Remove protected branches [^3] | | | | | | | Remove protected branches [^3] | | | | | |
| Remove pages | | | | | ✓ | | Remove pages | | | | | ✓ |
## Group ## Project features permissions
### Wiki and issues
Project features like wiki and issues can be hidden from users depending on
which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
### Protected branches
To prevent people from messing with history or pushing code without
review, we've created protected branches. Read through the documentation on
[protected branches](project/protected_branches.md)
to learn more.
Additionally, you can allow or forbid users with Master and/or
Developer permissions to push to a protected branch. Read through the documentation on
[Allowed to Merge and Allowed to Push settings](project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings)
to learn more.
### Cycle Analytics permissions
Find the current permissions on the Cycle Analytics dashboard on
the [documentation on Cycle Analytics permissions](project/cycle_analytics.md#permissions).
### Issue Board permissions
Developers and users with higher permission level can use all
the functionality of the Issue Board, that is create/delete lists
and drag issues around. Read though the
[documentation on Issue Boards permissions](project/issue_board.md#permissions)
to learn more.
### File Locking permissions (EEP)
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
Read through the documentation on [permissions for File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html#permissions-on-file-locking) to learn more.
File Locking is available in
[GitLab Enterprise Edition Premium](https://about.gitlab.com/gitlab-ee/) only.
### Confidential Issues permissions
Confidential issues can be accessed by reporters and higher permission levels,
as well as by guest users that create a confidential issue. To learn more,
read through the documentation on [permissions and access to confidential issues](project/issues/confidential_issues.md#permissions-and-access-to-confidential-issues).
## Group members permissions
Any user can remove themselves from a group, unless they are the last Owner of Any user can remove themselves from a group, unless they are the last Owner of
the group. The following table depicts the various user permission levels in a the group. The following table depicts the various user permission levels in a
...@@ -93,7 +144,20 @@ group. ...@@ -93,7 +144,20 @@ group.
| Remove group | | | | | ✓ | | Remove group | | | | | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ | | Manage group labels | | ✓ | ✓ | ✓ | ✓ |
<<<<<<< HEAD
## External users ## External users
=======
### Subgroup permissions
When you add a member to a subgroup, they inherit the membership and
permission level from the parent group. This model allows access to
nested groups if you have membership in one of its parents.
To learn more, read through the documentation on
[subgroups memberships](group/subgroups/index.md#membership).
## External users permissions
>>>>>>> upstream/master
In cases where it is desired that a user has access only to some internal or In cases where it is desired that a user has access only to some internal or
private projects, there is the option of creating **External Users**. This private projects, there is the option of creating **External Users**. This
...@@ -117,6 +181,7 @@ will find the option to flag the user as external. ...@@ -117,6 +181,7 @@ will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**. by an administrator under **Admin > Application Settings**.
<<<<<<< HEAD
## Auditor users ## Auditor users
>[Introduced][ee-998] in [GitLab Enterprise Edition Premium][eep] 8.17. >[Introduced][ee-998] in [GitLab Enterprise Edition Premium][eep] 8.17.
...@@ -136,8 +201,11 @@ which visibility level you select on project settings. ...@@ -136,8 +201,11 @@ which visibility level you select on project settings.
- Everyone with access: everyone can see depending on your project visibility level - Everyone with access: everyone can see depending on your project visibility level
## GitLab CI ## GitLab CI
=======
## GitLab CI/CD permissions
>>>>>>> upstream/master
GitLab CI permissions rely on the role the user has in GitLab. There are four GitLab CI/CD permissions rely on the role the user has in GitLab. There are four
permission levels in total: permission levels in total:
- admin - admin
...@@ -145,7 +213,7 @@ permission levels in total: ...@@ -145,7 +213,7 @@ permission levels in total:
- developer - developer
- guest/reporter - guest/reporter
The admin user can perform any action on GitLab CI in scope of the GitLab The admin user can perform any action on GitLab CI/CD in scope of the GitLab
instance and project. In addition, all admins can use the admin interface under instance and project. In addition, all admins can use the admin interface under
`/admin/runners`. `/admin/runners`.
...@@ -161,7 +229,7 @@ instance and project. In addition, all admins can use the admin interface under ...@@ -161,7 +229,7 @@ instance and project. In addition, all admins can use the admin interface under
| See events in the system | | | | ✓ | | See events in the system | | | | ✓ |
| Admin interface | | | | ✓ | | Admin interface | | | | ✓ |
### Jobs permissions ### Job permissions
>**Note:** >**Note:**
GitLab 8.12 has a completely redesigned job permissions system. GitLab 8.12 has a completely redesigned job permissions system.
...@@ -185,6 +253,26 @@ users: ...@@ -185,6 +253,26 @@ users:
| Push container images to current project | | ✓ | ✓ | ✓ | | Push container images to current project | | ✓ | ✓ | ✓ |
| Push container images to other projects | | | | | | Push container images to other projects | | | | |
### New CI job permissions model
GitLab 8.12 has a completely redesigned job permissions system. To learn more,
read through the documentation on the [new CI/CD permissions model](project/new_ci_build_permissions_model.md#new-ci-job-permissions-model).
## LDAP users permissions
Since GitLab 8.15, LDAP user permissions can now be manually overridden by an admin user.
Read through the documentation on [LDAP users permissions](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/index.html#updating-user-permissions-new-feature) to learn more.
## Auditor users permissions (EEP)
An Auditor user should be able to access all projects and groups of a GitLab instance
with the permissions described on the documentation on [auditor users permissions](https://docs.gitlab.com/ee/administration/auditor_users.html#permissions-and-restrictions-of-an-auditor-user).
Auditor users are available in [GitLab Enterprise Edition Premium](https://about.gitlab.com/gitlab-ee/)
only.
----
[^1]: Guest users can only view the confidential issues they created themselves [^1]: Guest users can only view the confidential issues they created themselves
[^2]: If **Public pipelines** is enabled in **Project Settings > Pipelines** [^2]: If **Public pipelines** is enabled in **Project Settings > Pipelines**
[^3]: Not allowed for Guest, Reporter, Developer, Master, or Owner [^3]: Not allowed for Guest, Reporter, Developer, Master, or Owner
......
# Reserved project and group names
Not all project & group names are allowed because they would conflict with
existing routes used by GitLab.
For a list of words that are not allowed to be used as group or project names, see the
[`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
- `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
- `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
- `GROUP_ROUTES`: are names that are reserved for all groups or projects.
## Reserved project names
It is currently not possible to create a project with the following names:
- -
- badges
- blame
- blob
- builds
- commits
- create
- create_dir
- edit
- environments/folders
- files
- find_file
- gitlab-lfs/objects
- info/lfs/objects
- new
- preview
- raw
- refs
- tree
- update
- wikis
## Reserved group names
Currently the following names are reserved as top level groups:
- 503.html
- -
- .well-known
- 404.html
- 422.html
- 500.html
- 502.html
- abuse_reports
- admin
- api
- apple-touch-icon-precomposed.png
- apple-touch-icon.png
- files
- assets
- autocomplete
- ci
- dashboard
- deploy.html
- explore
- favicon.ico
- groups
- header_logo_dark.png
- header_logo_light.png
- health_check
- help
- import
- invites
- jwt
- koding
- notification_settings
- oauth
- profile
- projects
- public
- robots.txt
- s
- search
- sent_notifications
- slash-command-logo.png
- snippets
- u
- unicorn_test
- unsubscribes
- uploads
- users
These group names are unavailable as subgroup names:
- -
- activity
- analytics
- audit_events
- avatar
- edit
- group_members
- hooks
- issues
- labels
- ldap
- ldap_group_links
- merge_requests
- milestones
- notification_setting
- pipeline_quota
- projects
- subgroups
[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb
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